数据结构: 线性表(带头双向循环链表实现)

之前一章学习了单链表的相关操作, 但是单链表的限制却很多, 比如不能倒序扫描链表, 解决方法是在数据结构上附加一个域, 使它包含指向前一个单元的指针即可.

那么怎么定义数据结构呢? 首先我们先了解以下链表的分类

1. 链表的分类

链表的结构非常多样, 以下情况组合起来就有 8 中链表结构

  • 单向或者双向
    在这里插入图片描述

  • 带头或者不带头
    在这里插入图片描述

  • 循环或者非循环
    在这里插入图片描述


虽然有这么多的链表的结构, 但是我们实际上最常用的还是两种结构:

在这里插入图片描述

无头单向非循环链表

结构简单,一般不会单独用来存放数据.实际上更多是作为其他数据结构的子结构, 如哈希桶, 图的邻接表等等. 另外这种结构在笔试面试中出现很多

带头双向循环链表

结构最复杂, 一般用于单独存储数据.实际上使用的链表数据结构, 都是带头双向循环链表. 虽然结构复杂, 但是实现相关功能比较简单.

严格来说只用实现 InsertErase 两个功能, 就可以实现 “二十分钟” 写完一个链表的任务.

2. 带头双向循环链表

2.1 带头双向循环链表的定义

typedef int LTDataType;     //LTDataType = int
typedef struct ListNode
{LTDataType data;          //数据域struct ListNode* prev;    //指向前一个结点struct ListNode* next;    //指向后一个结点
}ListNode;  //重命名为 ListNode
  1. 相比较单链表的数据结构, 只需要多一个域用来指向前面一个结点就可以了.
  2. 这里使用ListNode来命名这个数据结构, 方便后续学习 STL 进行知识迁移

2.2 接口函数

// 创建并返回链表的头结点
ListNode* ListCreate();
// 双向链表销毁
void ListDestroy(ListNode* phead);
// 双向链表打印
void ListPrint(ListNode* phead);
// 双向链表尾插
void ListPushBack(ListNode* phead, LTDataType x);
// 双向链表尾删
void ListPopBack(ListNode* phead);
// 双向链表头插
void ListPushFront(ListNode* phead, LTDataType x);
// 双向链表头删
void ListPopFront(ListNode* phead);
// 双向链表查找
ListNode* ListFind(ListNode* phead, LTDataType x);
// 双向链表在 pos 的前面进行插入
void ListInsert(ListNode* pos, LTDataType x);
// 双向链表删除 pos 位置的结点
void ListErase(ListNode* pos);

3. 接口函数的实现

因为有虚拟结点的存在, 所以除了创建头结点的函数, 其余接口函数都不会修改结构体指针, 只是修改结构体.

为了统一接口形式, 统一使用一级指针作为函数的形参类型. 需要修改头结点的函数接口, 直接用返回值的方法达到修改头结点指针的目的.

3.1 创建并返回链表的头结点 (ListCreate)

在这里插入图片描述

创建链表即为创建头结点, 它是一个虚拟结点(dummy node), 实际的值没有意义.它的两个指针都指向自己.

  • ListList.h
#pragma once #include <stdio.h>
#include <stdlib.h>
#include <assert.h>typedef int LTDataType;
typedef struct ListNode
{LTDataType data;        //数据域struct ListNode* prev;  //指向前一个结点struct ListNode* next;  //指向后一个结点
}ListNode;// 创建并返回链表的头结点
ListNode* ListCreate();

修改头结点指针, 使用返回值接受头结点的指针

  • ListList.c
// 创建返回链表的头结点
ListNode* ListCreate()
{// 动态开辟空间创建头结点, 如果开辟失败直接结束程序ListNode* head = BuyListNode(0);// 处理链表数据, 数据域随机值, 两个指针指向自己head->next = head;head->prev = head;return head;
}
  1. 这里的 BuyListNode() 是一个我自己定义的静态函数, 方便后续复用. 函数的功能是用来从堆中申请空间用来创建一个新结点.
// 创建一个新结点
static ListNode* BuyListNode(LTDataType x)
{ListNode* newNode = (ListNode*)malloc(sizeof(struct ListNode));if (newNode == NULL){perror("malloc fail");exit(-1);}newNode->data = x;newNode->next = NULL;newNode->prev = NULL;return newNode;
}
  1. 创建头结点后, 使头结点指向自己
  • test.c
void LinkListTest1()
{ListNode* head = ListCreate();free(head);
}

测试调试结果如下:
在这里插入图片描述

头结点创建成功, 并且头结点两个指针都指向了自己

3.2 双向链表打印 (ListPrint)

从第一个结点开始遍历链表每个结点, 并且将结点的数据域打印出来, 直到遇到头结点结束
在这里插入图片描述

  • ListList.h
void ListPrint(ListNode* phead);
  • ListList.c
// 双向链表打印
void ListPrint(ListNode* phead)
{assert(phead);  //确保头结点存在printf("head <=> ");ListNode* cur = phead->next;  //从第一个结点开始遍历, 直到遇到头结点结束while (cur != phead){printf("%d <=> ", cur->data);cur = cur->next;}printf("\n");
}
  1. 确保头结点存在
  1. cur第一次定位到头结点后面一个结点, 即第一个有效结点
  1. 顺序遍历并且打印
  • test.c

后续调试其他函数功能都会使用到 ListPrint 函数, 这里就先不调试了.

3.3 双向链表尾插 (ListPushBack)

先找到链表尾结点的位置, 在尾结点和头结点之间插入一个新结点

在这里插入图片描述

  • ListList.h
void ListPushBack(ListNode* phead, LTDataType x);
  • ListList.c
// 双向链表尾插
void ListPushBack(ListNode* phead, LTDataType x)
{assert(phead);  //确保头结点存在ListNode* newNode = BuyListNode(x); //创建新结点 ListNode* tail = phead->prev;       //找到尾结点//更改链接关系tail->next = newNode;newNode->prev = tail;phead->prev = newNode;newNode->next = phead;}
  1. 确保头结点存在
  1. 更改链接关系, 需要修改一共四根指针的指向关系
  • test.c
void LinkListTest1()
{ListNode* head = ListCreate();ListPushBack(head, 1);ListPushBack(head, 2);ListPushBack(head, 3);ListPushBack(head, 4);ListPushBack(head, 5);ListPrint(head);
}

测试结果如下:
在这里插入图片描述

3.4 双向链表尾删 (ListPopBack)

找到新的尾结点位置, 更改链接关系后将原尾结点删除
在这里插入图片描述

  • ListList.h
void ListPopBack(ListNode* phead);
  • ListList.c
// 双向链表尾删
void ListPopBack(ListNode* phead)
{assert(phead);                    //确保头结点存在assert(phead->next != phead);     //确保有结点可删ListNode* tail = phead->prev;     //找到要删除的尾结点ListNode* tailPrev = tail->prev;  //找到新的尾结点//更改链接关系tailPrev->next = phead;phead->prev = tailPrev;free(tail); //释放空间
}
  • test.c
void LinkListTest1()
{ListNode* head = ListCreate();ListPushBack(head, 1);ListPushBack(head, 2);ListPushBack(head, 3);ListPushBack(head, 4);ListPushBack(head, 5);ListPrint(head);ListPopBack(head);ListPrint(head);ListPopBack(head);ListPrint(head);ListPopBack(head);ListPopBack(head);ListPopBack(head);ListPrint(head);ListPopBack(head);ListPrint(head);ListDestroy(head);
}

测试结果如下:
在这里插入图片描述

3.5 双链表头插 (ListPushFront)

找到原第一个有效节点, 在头结点和这个结点之间插入一个新结点
在这里插入图片描述

  • ListList.h
void ListPushFront(ListNode* phead, LTDataType x);
  • ListList.c
// 双向链表头插
void ListPushFront(ListNode* phead, LTDataType x)
{assert(phead);  //确保头结点存在ListNode* newNode = BuyListNode(x); //创建新结点ListNode* first = phead->next;      //找到原来的第一个结点//更新链接关系phead->next = newNode;newNode->prev = phead;newNode->next = first;first->prev = newNode;
}
  1. 确保头结点存在
  1. 在头结点和第一个有效结点之间插入新结点
  • test.c
void LinkListTest2()
{ListNode* head = ListCreate();ListPushFront(head, 1);ListPushFront(head, 2);ListPushFront(head, 3);ListPushFront(head, 4);ListPushFront(head, 5);
}

测试运行结果如下:
在这里插入图片描述

3.6 双链表头删 (ListPopFront)

先找到第一个和第二个有效结点, 更新头结点和第二个有效结点之间的链接关系后, 释放第一个结点的空间.
在这里插入图片描述

  • ListList.h
void ListPopFront(ListNode* phead);
  • ListList.c
// 双向链表头删
void ListPopFront(ListNode* phead)
{assert(phead);  //确保哨兵结点存在assert(phead->next != phead); //确保链表不为空ListNode* first = phead->next;  //找到头结点位置ListNode* second = first->next; //找到头结点后一个结点的位置//更新链接关系phead->next = second;second->prev = phead;free(first); //释放空间
}
  • test.c
void LinkListTest2()
{ListNode* head = ListCreate();ListPushFront(head, 1);ListPushFront(head, 2);ListPushFront(head, 3);ListPushFront(head, 4);ListPushFront(head, 5);ListPrint(head);ListPopFront(head);ListPrint(head);ListPopFront(head);ListPopFront(head);ListPopFront(head);ListPopFront(head);ListPrint(head);ListPopFront(head);ListPrint(head);ListDestroy(head);
}

测试结果如下:
在这里插入图片描述

3.7 双链表查找 (ListFind)

从第一个有效结点开始向后遍历链表, 判断是否有想要查找的数据, 直到遇到头结点. 未查找到返回空指针, 查找到返回该结点的地址
在这里插入图片描述

  • ListList.h
ListNode* ListFind(ListNode* phead, LTDataType x);
  • ListList.c
// 双向链表查找
ListNode* ListFind(ListNode* phead, LTDataType x)
{assert(phead);  //确保哨兵结点存在ListNode* cur = phead->next;while (cur != phead){if (cur->data == x){return cur;}cur = cur->next;}return NULL;
}
  • test.c
void LinkListTest3()
{ListNode* head = ListCreate();ListPushBack(head, 1);ListPushBack(head, 2);ListPushBack(head, 3);ListPushBack(head, 4);ListPushBack(head, 5);ListPrint(head);ListNode* pos;pos = ListFind(head, 2);printf("pos <=> ");while (pos && pos != head){printf("%d <=> ", pos->data);pos = pos->next;}puts("\n");pos = ListFind(head, 6);printf("pos <=> ");while (pos && pos != head){printf("%d <=> ", pos->data);pos = pos->next;}puts("\n");
}

测试运行结果如下:
在这里插入图片描述

3.8 双向链表在 pos 的前面进行插入 (LinkInsert)

先找到 pos 的前面一个结点的位置, 随后在这个结点和 pos 之间插入新结点
在这里插入图片描述

  • LinkList.h
void ListInsert(ListNode* pos, LTDataType x);
  • LinkList.c
// 双向链表在 pos 之前插入
void ListInsert(ListNode* pos, LTDataType x)
{assert(pos);  //确保pos合法ListNode* newNode = BuyListNode(x); //创建新结点ListNode* posPrev = pos->prev;      //找到 pos 前一个结点的位置//更新链接方式posPrev->next = newNode;newNode->prev = posPrev;newNode->next = pos;pos->prev = newNode;
}
  • test.c
void LinkListTest3()
{ListNode* head = ListCreate();ListPushBack(head, 1);ListPushBack(head, 2);ListPushBack(head, 3);ListPushBack(head, 4);ListPushBack(head, 5);ListPrint(head);ListNode* pos;pos = ListFind(head, 1);if (pos){ListInsert(pos, -1);ListPrint(head);}pos = ListFind(head, 4);if (pos){ListInsert(pos, -4);ListPrint(head);}pos = ListFind(head, 6);if (pos){ListInsert(pos, -6);ListPrint(head);}ListDestroy(head);
}

测试运行结果如下:
在这里插入图片描述

3.9 双向链表删除 pos 位置的结点 (ListErase)

先找到 pos 的前后两个结点的位置, 随后更新两个结点之间的链接关系, 最后删除 pos 结点
在这里插入图片描述

  • LinkList.h
void ListErase(ListNode* pos);
  • LinkList.c
// 双向链表删除 pos 位置的结点
void ListErase(ListNode* pos)
{assert(pos);  //确保 pos 合法ListNode* posPrev = pos->prev;    //找到 pos 前一个结点的位置ListNode* posNext = pos->next;    //找到 pos 后一个结点的位置//更新链接方式posPrev->next = posNext;posNext->prev = posPrev;free(pos);  //释放空间
}
  • test.c
void LinkListTest3()
{ListNode* head = ListCreate();ListPushBack(head, 1);ListPushBack(head, 2);ListPushBack(head, 3);ListPushBack(head, 4);ListPushBack(head, 5);ListPrint(head);ListNode* pos;pos = ListFind(head, 1);if (pos){ListErase(pos);ListPrint(head);}pos = ListFind(head, 4);if (pos){ListErase(pos);ListPrint(head);}pos = ListFind(head, 6);if (pos){ListErase(pos);ListPrint(head);}ListDestroy(head);
}

测试运行结果如下:
在这里插入图片描述

3.10 双向链表销毁 (ListDestroy)

  • LinkList.h
void ListDestroy(ListNode* phead);
  • LinkList.c
// 双向链表销毁
void ListDestroy(ListNode* phead)
{assert(phead);  //确保哨兵结点存在ListNode* cur = phead->next;while (cur != phead){ListNode* nextNode = cur->next;free(cur);cur = nextNode;}free(phead);
}

4. 总结

不同点顺序表链表
存储空间上物理上一定连续逻辑上连续,但物理上不一定连续
随机访问支持: O ( 1 ) O(1) O(1)不支持: O ( N ) O(N) O(N)
任意位置插入或者删除元素可能需要搬移元素, 效率低 O ( N ) O(N) O(N)只需要修改指针指向
插入动态顺序表, 空间不够时, 需要扩容没有容量的概念
应用场景元素高效存储 + 频繁访问任意位置插入和删除频繁
缓存利用率

5. 完整代码

  • LinkList.h
#pragma once #include <stdio.h>
#include <stdlib.h>
#include <assert.h>typedef int LTDataType;
typedef struct ListNode
{LTDataType data;        //数据域struct ListNode* prev;  //指向前一个结点struct ListNode* next;  //指向后一个结点
}ListNode;// 创建并返回链表的头结点
ListNode* ListCreate();
// 双向链表销毁
void ListDestroy(ListNode* phead);
// 双向链表打印
void ListPrint(ListNode* phead);
// 双向链表尾插
void ListPushBack(ListNode* phead, LTDataType x);
// 双向链表尾删
void ListPopBack(ListNode* phead);
// 双向链表头插
void ListPushFront(ListNode* phead, LTDataType x);
// 双向链表头删
void ListPopFront(ListNode* phead);
// 双向链表查找
ListNode* ListFind(ListNode* phead, LTDataType x);
// 双向链表在 pos 的前面进行插入
void ListInsert(ListNode* pos, LTDataType x);
// 双向链表删除 pos 位置的结点
void ListErase(ListNode* pos);
  • LinkList.c
#include "LinkList.h"// 创建一个新结点
static ListNode* BuyListNode(LTDataType x)
{ListNode* newNode = (ListNode*)malloc(sizeof(struct ListNode));if (newNode == NULL){perror("malloc fail");exit(-1);}newNode->data = x;newNode->next = NULL;newNode->prev = NULL;return newNode;
}// 创建返回链表的头结点
ListNode* ListCreate()
{// 动态开辟空间创建头结点, 如果开辟失败直接结束程序ListNode* head = BuyListNode(0);// 处理链表数据, 数据域随机值, 两个指针指向自己head->next = head;head->prev = head;return head;
}// 双向链表打印
void ListPrint(ListNode* phead)
{assert(phead);  //确保哨兵结点存在printf("head <=> ");ListNode* cur = phead->next;  //从头结点开始遍历, 直到遇到哨兵结点结束while (cur != phead){printf("%d <=> ", cur->data);cur = cur->next;}printf("\n");
}// 双向链表销毁
void ListDestroy(ListNode* phead)
{assert(phead);  //确保哨兵结点存在ListNode* cur = phead->next;while (cur != phead){ListNode* nextNode = cur->next;free(cur);cur = nextNode;}free(phead);
}// 双向链表尾插
void ListPushBack(ListNode* phead, LTDataType x)
{assert(phead);  //确保哨兵结点存在ListNode* newNode = BuyListNode(x); //创建新结点 ListNode* tail = phead->prev;       //找到尾结点//更改链接关系tail->next = newNode;newNode->prev = tail;phead->prev = newNode;newNode->next = phead;}// 双向链表尾删
void ListPopBack(ListNode* phead)
{assert(phead);                    //确保哨兵结点存在assert(phead->next != phead);     //确保有结点可删ListNode* tail = phead->prev;     //找到要删除的尾结点ListNode* tailPrev = tail->prev;  //找到新的尾结点//更改链接关系tailPrev->next = phead;phead->prev = tailPrev;free(tail); //释放空间
}// 双向链表头插
void ListPushFront(ListNode* phead, LTDataType x)
{assert(phead);  //确保哨兵结点存在ListNode* newNode = BuyListNode(x); //创建新结点ListNode* first = phead->next;      //找到原来的头结点//更新链接关系phead->next = newNode;newNode->prev = phead;newNode->next = first;first->prev = newNode;}// 双向链表头删
void ListPopFront(ListNode* phead)
{assert(phead);  //确保哨兵结点存在assert(phead->next != phead); //确保链表不为空ListNode* first = phead->next;  //找到头结点位置ListNode* second = first->next; //找到头结点后一个结点的位置//更新链接关系phead->next = second;second->prev = phead;free(first); //释放空间
}// 双向链表查找
ListNode* ListFind(ListNode* phead, LTDataType x)
{assert(phead);  //确保哨兵结点存在ListNode* cur = phead->next;while (cur != phead){if (cur->data == x){return cur;}cur = cur->next;}return NULL;
}// 双向链表在 pos 之前插入
void ListInsert(ListNode* pos, LTDataType x)
{assert(pos);  //确保pos合法ListNode* newNode = BuyListNode(x); //创建新结点ListNode* posPrev = pos->prev;      //找到 pos 前一个结点的位置//更新链接方式posPrev->next = newNode;newNode->prev = posPrev;newNode->next = pos;pos->prev = newNode;
}
// 双向链表删除 pos 位置的结点
void ListErase(ListNode* pos)
{assert(pos);  //确保 pos 合法ListNode* posPrev = pos->prev;    //找到 pos 前一个结点的位置ListNode* posNext = pos->next;    //找到 pos 后一个结点的位置//更新链接方式posPrev->next = posNext;posNext->prev = posPrev;free(pos);  //释放空间
}
  • test.c
#include "LinkList.h"void LinkListTest1()
{ListNode* head = ListCreate();ListPushBack(head, 1);ListPushBack(head, 2);ListPushBack(head, 3);ListPushBack(head, 4);ListPushBack(head, 5);ListPrint(head);ListPopBack(head);ListPrint(head);ListPopBack(head);ListPrint(head);ListPopBack(head);ListPopBack(head);ListPopBack(head);ListPrint(head);ListPopBack(head);ListPrint(head);ListDestroy(head);
}void LinkListTest2()
{ListNode* head = ListCreate();ListPushFront(head, 1);ListPushFront(head, 2);ListPushFront(head, 3);ListPushFront(head, 4);ListPushFront(head, 5);ListPrint(head);ListPopFront(head);ListPrint(head);ListPopFront(head);ListPopFront(head);ListPopFront(head);ListPopFront(head);ListPrint(head);ListPopFront(head);ListPrint(head);ListDestroy(head);
}void LinkListTest3()
{ListNode* head = ListCreate();ListPushBack(head, 1);ListPushBack(head, 2);ListPushBack(head, 3);ListPushBack(head, 4);ListPushBack(head, 5);ListPrint(head);ListNode* pos;pos = ListFind(head, 1);ListInsert(pos, 0);ListErase(pos);ListPrint(head);pos = ListFind(head, 4);ListInsert(pos, 10);ListPrint(head);pos = ListFind(head, 11);ListInsert(pos, 12);ListPrint(head);ListDestroy(head);
}void LinkListTest4()
{ListNode* head = ListCreate();ListPushBack(head, 1);ListPushBack(head, 2);ListPushBack(head, 3);ListPushBack(head, 4);ListPushBack(head, 5);ListNode* pos;pos = ListFind(head, 2);printf("pos <=> ");while (pos && pos != head){printf("%d <=> ", pos->data);pos = pos->next;}puts("\n");pos = ListFind(head, 6);printf("pos <=> ");while (pos && pos != head){printf("%d <=> ", pos->data);pos = pos->next;}puts("\n");
}void LinkListTest5()
{ListNode* head = ListCreate();ListPushBack(head, 1);ListPushBack(head, 2);ListPushBack(head, 3);ListPushBack(head, 4);ListPushBack(head, 5);ListPrint(head);ListNode* pos;pos = ListFind(head, 1);if (pos){ListInsert(pos, -1);ListPrint(head);}pos = ListFind(head, 4);if (pos){ListInsert(pos, -4);ListPrint(head);}pos = ListFind(head, 6);if (pos){ListInsert(pos, -6);ListPrint(head);}ListDestroy(head);
}void LinkListTest6()
{ListNode* head = ListCreate();ListPushBack(head, 1);ListPushBack(head, 2);ListPushBack(head, 3);ListPushBack(head, 4);ListPushBack(head, 5);ListPrint(head);ListNode* pos;pos = ListFind(head, 1);if (pos){ListErase(pos);ListPrint(head);}pos = ListFind(head, 4);if (pos){ListErase(pos);ListPrint(head);}pos = ListFind(head, 6);if (pos){ListErase(pos);ListPrint(head);}ListDestroy(head);
}int main(void)
{//LinkListTest1();//LinkListTest2();//LinkListTest3();//LinkListTest4();//LinkListTest5();LinkListTest6();return 0;
}

本章完.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/32258.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LVS集群

目录 1、lvs简介&#xff1a; 2、lvs架构图&#xff1a; 3、 lvs的工作模式&#xff1a; 1&#xff09; VS/NAT&#xff1a; 即&#xff08;Virtual Server via Network Address Translation&#xff09; 2&#xff09;VS/TUN &#xff1a;即&#xff08;Virtual Server v…

7.2 手撕VGG11模型 使用Fashion_mnist数据训练VGG

VGG首先引入块的思想将模型通用模板化 VGG模型的特点 与AlexNet&#xff0c;LeNet一样&#xff0c;VGG网络可以分为两部分&#xff0c;第一部分主要由卷积层和汇聚层组成&#xff0c;第二部分由全连接层组成。 VGG有5个卷积块&#xff0c;前两个块包含一个卷积层&#xff0c…

MySQL_SQL性能分析

SQL执行频次 语法&#xff1a; SHOW GLOBAL STATUS LIKE COM_类型; COM_insert; 插入次数 com_delete; 删除次数 com_update; 更新次数 com_select; 查询次数 com_______; 注意&#xff1a;通过语法&#xff0c;可以查询到数据库的实际状态&#xff0c;就可以知道数据库是以增删…

TDesign中后台管理系统-用户登录

目录 1 创建用户表2 开发后端接口3 测试接口4 修改登录页面调用后端接口最终效果总结 中后台系统第一个要实现的功能就是登录了&#xff0c;我们通常的逻辑是让用户在登录页面输入用户名和密码&#xff0c;调用后端接口去验证用户的合法性&#xff0c;然后根据接口返回的结果进…

【T3】金蝶kis凭证数据转换到畅捷通T3软件中。

【问题需求】 将金蝶软件中的账套转换到畅捷通T3软件中。 由于金蝶老版本使用的是非sql server数据库。 进而需要将其数据导入到sql中,在转换到T3。 【转换环境】 金蝶中数据:凭证;科目无项目核算。 1、金蝶的数据文件后缀为.AIS; 2、安装office2003全版软件; 3、安装sq…

【算法】双指针——leetcode盛最多水的容器、剑指Offer57和为s的两个数字

盛水最多的容器 &#xff08;1&#xff09;暴力解法 算法思路&#xff1a;我们枚举出所有的容器大小&#xff0c;取最大值即可。 容器容积的计算方式&#xff1a; 设两指针 i , j &#xff0c;分别指向水槽板的最左端以及最右端&#xff0c;此时容器的宽度为 j - i 。由于容器…

【CDH集群】无法发出查询:Host Monitor未运行

无法发出查询:Host Monitor未运行 【CDH集群】无法发出查询:Host Monitor未运行同事的解决方案解决方法&#xff1a;删除原uuid重启agent查看新uuid修改scm数据库中HOSTS表中的agent的uuid 【CDH集群】无法发出查询:Host Monitor未运行 起初是impala报错&#xff0c;连接不上&…

使用 React Native CLI 创建项目

React Native 安装的先决条件和设置 需要掌握的知识点 掌握 JavaScript 基础知识掌握 React 相关基础知识掌握 TypeScript 相关基础知识 安装软件前需要首先安装Chocolatey。Chocolatey 是一种流行的 Windows 包管理器。 安装 nodejs 和 JDK choco install -y nodejs-lts …

【工作记录】mysql中实现分组统计的三种方式

前言 实际工作中对范围分组统计的需求还是相对普遍的&#xff0c;本文记录下在mysql中通过函数和sql完成分组统计的实现过程。 数据及期望 比如我们获取到了豆瓣电影top250&#xff0c;现在想知道各个分数段的电影总数. 表数据如下: 期望结果: 实现方案 主要思路是根据s…

解决Vue+Element-UI 进行From表单校验时出现了英文提示问题

说明&#xff1a;该篇博客是博主一字一码编写的&#xff0c;实属不易&#xff0c;请尊重原创&#xff0c;谢谢大家&#xff01; 问题描述 在使用form表单时&#xff0c;往往会对表单字段进行校验&#xff0c;字段为必填项时会添加required属性&#xff0c;此时自定义rules规则…

RabbitMQ 消息队列

文章目录 &#x1f370;有几个原因可以解释为什么要选择 RabbitMQ&#xff1a;&#x1f969;mq之间的对比&#x1f33d;RabbitMQ vs Apache Kafka&#x1f33d;RabbitMQ vs ActiveMQ&#x1f33d;RabbitMQ vs RocketMQ&#x1f33d;RabbitMQ vs Redis &#x1f969;linux docke…

MyBatis Plus-个人笔记

前言 学习视频 尚硅谷-Mybatis-Plus教程学习主要内容 本文章记录尚硅谷-Mybatis-Plus教程内容&#xff0c;只是作为自己学习笔记&#xff0c;如有侵扰请联系删除 一、MyBatis-Plus简介 1、简介 MyBatis-Plus&#xff08;简称 MP&#xff09;是一个 MyBatis的增强工具&#…

用python来爬取某鱼的商品信息(1/2)

目录 前言 第一大难题——找到网站入口 曲线救国 模拟搜索 第二大难题——登录 提一嘴 登录cookie获取 第一种 第二种 第四大难题——无法使用导出的cookie 原因 解决办法 最后 出现小问题 总结 前言 本章讲理论&#xff0c;后面一节讲代码 拿来练练手的&#xff…

使用 Python 中的 Langchain 从零到高级快速进行工程

大型语言模型 (LLM) 的一个重要方面是这些模型用于学习的参数数量。模型拥有的参数越多,它就能更好地理解单词和短语之间的关系。这意味着具有数十亿个参数的模型有能力生成各种创造性的文本格式,并以信息丰富的方式回答开放式和挑战性的问题。 ChatGPT 等法学硕士利用 T

【Spring专题】Spring底层核心原理解析

目录 前言阅读导航前置知识Q1&#xff1a;你能描述一下JVM对象创建过程吗&#xff1f;Q2&#xff1a;Spring的特性是什么&#xff1f;前置知识总结 课程内容一、Spring容器的启动二、一般流程推测2.1 扫描2.2 IOC2.3 AOP 2.4 小结三、【扫描】过程简单推测四、【IOC】过程简单推…

数据库--MySQL

一、什么是范式&#xff1f; 范式是数据库设计时遵循的一种规范&#xff0c;不同的规范要求遵循不同的范式。 最常用的三大范式 第一范式(1NF)&#xff1a;属性不可分割&#xff0c;即每个属性都是不可分割的原子项。(实体的属性即表中的列) 第二范式(2NF)&#xff1a;满足…

企业级帮助中心编写方案怎么写?

在现代商业环境中&#xff0c;为客户提供高效的支持和解决方案至关重要。企业级帮助中心是一个集中管理和呈现常见问题和解答的平台&#xff0c;可以为客户提供快速、便捷的自助帮助。本文将提供一个企业级帮助中心编写方案&#xff0c;旨在帮助企业提供优质的客户支持&#xf…

20.4 HTML 表单

1. form表单 <form>标签: 用于创建一个表单, 通过表单, 用户可以向网站提交数据. 表单可以包含文本输入字段, 复选框, 单选按钮, 下拉列表, 提交按钮等等. 当用户提交表单时, 表单数据会发送到服务器进行处理.action属性: 应指向一个能够处理表单数据的服务器端脚本或UR…

React入门学习笔记3

事件处理 通过onXxx属性指定事件处理函数(注意大小写) React使用的是自定义(合成)事件, 而不是使用的原生DOM事件——为了更好的兼容性 eg&#xff1a;οnclick》onClickReact中的事件是通过事件委托方式处理的(委托给组件最外层的元素)——为了更高效通过event.target得到发生…

AlmaLinux 9 安装 Go 1.20

AlmaLinux 9 安装 Golang 1.20 1. 下载 go 安装包2. 安装 go3. 配置环境变量4. 确认 go 版本 1. 下载 go 安装包 访问 https://go.dev/dl/&#xff0c;下载你想安装的版本&#xff0c;比如 go1.20.7.linux-amd64.tar.gz&#xff0c; 2. 安装 go (可选)删除旧版本&#xff0c;…