当前位置: 首页 > news >正文

链表的概念和结构

文章目录

  • 1. 链表的概念
  • 2. 链表的分类
  • 3. 单向不带头非循环链表
    • 3.1 接口设计(SList.h)
    • 3.2 接口实现(SList.c)
      • 1)打印和创建结点
      • 2)头尾插入删除
      • 3)查找和插入
      • 4)删除和销毁
    • 3.3 完整代码
      • SList.h
      • SList.c
      • test.c
      • 运行效果
  • 4. 带头双向循环链表
    • 4.1 接口设计(List.h)
    • 4.2 接口实现(List.c)
      • 1)创建结点
      • 2)初始化和打印
      • 3)头尾插入删除
      • 4)查找插入删除销毁
    • 4.3 完整代码
      • List.h
      • List.c
      • test.c
      • 运行效果
  • 5. 顺序表和链表的优缺点

1. 链表的概念

链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的 。

2. 链表的分类

类别的分类主要由以下几种情况组合起来的,一共合计8种:

1)单向或者双向链表

单向双向

2)带头或不带头链表

带头或者不带头

3)循环或者非循环链表

循环非循环

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

1)无头单向非循环链表

它的结构简单,一般不会单独用来存数据,实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等等

无头单向

2)带头双向循环链表

它的结构最复杂,一般用在单独存储数据,实际中使用的链表数据结构,都是带头双向循环链表。它的结构复杂但会带来很多优势,实现反而简单了。

带头双向循环

3. 单向不带头非循环链表

下面将其分为3个模块进行实现SList.h,SList.c,test.c

3.1 接口设计(SList.h)

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>typedef int SLNDataType;//单链表
typedef struct SListNode
{SLNDataType val;		//存储值struct SListNode* next;	//下一个结点
}SLNode;//打印创建
void SLTPrint(SLNode* phead);
SLNode* CreateNode(SLNDataType x);//头尾插入删除
void SLTPushBack(SLNode** pphead, SLNDataType x);
void SLTPushFront(SLNode** pphead, SLNDataType x);
void SLTPopBack(SLNode** pphead);
void SLTPopFront(SLNode** pphead);//查找插入
SLNode* SLTFind(SLNode** pphead, SLNDataType x);
//在pos前面、后面插入
void SLTInsert(SLNode** pphead, SLNode* pos, SLNDataType x);
void SLTInsertAfter(SLNode* pos, SLNDataType x);//删除当前或后面一个结点
void SLTErase(SLNode** pphead, SLNode* pos);
void SLTEraseAfter(SLNode* pos);void SLTDestroy(SLNode** pphead);

3.2 接口实现(SList.c)

1)打印和创建结点

打印操作不需要改变链表,因此不需要二级指针。

void SLTPrint(SLNode* phead)
{SLNode* cur = phead;while (cur != NULL){printf("%d -> ", cur->val);cur = cur->next;}printf("NULL\n");
}SLNode* CreateNode(SLNDataType x)
{SLNode* newNode = (SLNode*)malloc(sizeof(SLNode));if (newNode == NULL){perror("malloc fail\n");exit(-1);}newNode->val = x;newNode->next = NULL;return newNode;
}

2)头尾插入删除

这样要对链表进行操作,存在结点的指向问题。需要明确的是:对于函数传参问题,不在当前函数的都是外面的,要改变外面的就要用更高一级指针;所以改变外面结构体指针SLNode*,要用二级指针SLNode**

在free时,是将空间都释放完毕了,因此要注意free之前完成涉及的操作,释放空间后,如果再次使用该结构体,会发生内存泄漏或者野指针等问题。

void SLTPushBack(SLNode** pphead, SLNDataType x)
{assert(pphead);SLNode* newNode = CreateNode(x);if (*pphead == NULL){*pphead = newNode;}else{//next指向newNode改变的是结构体(结构体中的next)//即改变外面结构体Node,要用一级指针Node*SLNode* tail = *pphead;while (tail->next != NULL){tail = tail->next;}tail->next = newNode;}
}void SLTPushFront(SLNode** pphead, SLNDataType x)
{assert(pphead);SLNode* newNode = CreateNode(x);newNode->next = *pphead;*pphead = newNode;
}void SLTPopBack(SLNode** pphead)
{assert(pphead);assert(*pphead);//一个结点if ((*pphead)->next == NULL){free(*pphead);*pphead = NULL;}//多个结点else{//找尾//1/*SLNode* prev = NULL;SLNode* tail = *pphead;while (tail->next != NULL){prev = tail;tail = tail->next;}free(tail);tail = NULL;prev->next = NULL;*///2SLNode* tail = *pphead;while (tail->next->next != NULL){tail = tail->next;}free(tail->next);tail->next = NULL;}
}void SLTPopFront(SLNode** pphead)
{assert(pphead);assert(*pphead);//不管是一个还是多个结点SLNode* next = (*pphead)->next;free(*pphead);*pphead = next;
}

3)查找和插入

严格限制pos一定是链表里的一个有效结点

//查找插入
SLNode* SLTFind(SLNode** pphead, SLNDataType x)
{assert(pphead);SLNode* cur = *pphead;while (cur != NULL){if (cur->val == x){return cur;}cur = cur->next;}return NULL;
}//在pos前面、后面插入
void SLTInsert(SLNode** pphead, SLNode* pos, SLNDataType x)
{//严格限制pos一定是链表里的一个有效结点assert(pphead);assert(*pphead);assert(pos);if ((*pphead) == pos){//头插SLTPushFront(pphead, x);}else{SLNode* prev = *pphead;while (prev->next != pos){prev = prev->next;}SLNode* newNode = CreateNode(x);prev->next = newNode;newNode->next = pos;}
}void SLTInsertAfter(SLNode* pos, SLNDataType x)
{assert(pos);SLNode* newNode = CreateNode(x);newNode->next = pos->next;pos->next = newNode;
}

4)删除和销毁

务必保证删除的结点是属于链表里的一个有效结点

//删除当前或后面一个结点
void SLTErase(SLNode** pphead, SLNode* pos)
{assert(pphead);assert(*pphead);assert(pos);if ((*pphead) == pos){//头删SLTPopFront(pphead);}else{SLNode* prev = *pphead;while (prev->next != pos){prev = prev->next;}prev->next = pos->next;free(pos);pos = NULL;}
}void SLTEraseAfter(SLNode* pos)
{assert(pos);assert(pos->next);SLNode* tmp = pos->next;pos->next = pos->next->next;free(tmp);tmp = NULL;
}void SLTDestroy(SLNode** pphead)
{assert(pphead);SLNode* cur = *pphead;while (cur){SLNode* next = cur->next;free(cur);cur = NULL;cur = next;}*pphead = NULL;
}

3.3 完整代码

SList.h

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>typedef int SLNDataType;//单链表
typedef struct SListNode
{SLNDataType val;struct SListNode* next;
}SLNode;//打印创建
void SLTPrint(SLNode* phead);
SLNode* CreateNode(SLNDataType x);//头尾插入删除
void SLTPushBack(SLNode** pphead, SLNDataType x);
void SLTPushFront(SLNode** pphead, SLNDataType x);
void SLTPopBack(SLNode** pphead);
void SLTPopFront(SLNode** pphead);//查找插入
SLNode* SLTFind(SLNode** pphead, SLNDataType x);
//在pos前面、后面插入
void SLTInsert(SLNode** pphead, SLNode* pos, SLNDataType x);
void SLTInsertAfter(SLNode* pos, SLNDataType x);//删除当前或后面一个结点
void SLTErase(SLNode** pphead, SLNode* pos);
void SLTEraseAfter(SLNode* pos);void SLTDestroy(SLNode** pphead);

SList.c

#include "SList.h"//打印,不改变phead,所以不需要二级指针
void SLTPrint(SLNode* phead)
{SLNode* cur = phead;while (cur != NULL){printf("%d -> ", cur->val);cur = cur->next;}printf("NULL\n");
}SLNode* CreateNode(SLNDataType x)
{SLNode* newNode = (SLNode*)malloc(sizeof(SLNode));if (newNode == NULL){perror("malloc fail\n");exit(-1);}newNode->val = x;newNode->next = NULL;return newNode;
}//头尾插入删除
//不在当前函数的都是外面的,要改变外面的就要用更高一级指针
//传址问题:改变外面结构体指针Node*,要用二级指针Node**
//尾结点指向问题
void SLTPushBack(SLNode** pphead, SLNDataType x)
{assert(pphead);SLNode* newNode = CreateNode(x);if (*pphead == NULL){*pphead = newNode;}else{//next指向newNode改变的是结构体(结构体中的next)//即改变外面结构体Node,要用一级指针Node*SLNode* tail = *pphead;while (tail->next != NULL){tail = tail->next;}tail->next = newNode;}
}void SLTPushFront(SLNode** pphead, SLNDataType x)
{assert(pphead);SLNode* newNode = CreateNode(x);newNode->next = *pphead;*pphead = newNode;
}void SLTPopBack(SLNode** pphead)
{assert(pphead);assert(*pphead);//一个结点if ((*pphead)->next == NULL){free(*pphead);*pphead = NULL;}//多个结点else{//找尾//1/*SLNode* prev = NULL;SLNode* tail = *pphead;while (tail->next != NULL){prev = tail;tail = tail->next;}free(tail);tail = NULL;prev->next = NULL;*///2SLNode* tail = *pphead;while (tail->next->next != NULL){tail = tail->next;}free(tail->next);tail->next = NULL;}
}void SLTPopFront(SLNode** pphead)
{assert(pphead);assert(*pphead);//不管是一个还是多个结点SLNode* next = (*pphead)->next;free(*pphead);*pphead = next;
}//查找插入
SLNode* SLTFind(SLNode** pphead, SLNDataType x)
{assert(pphead);SLNode* cur = *pphead;while (cur != NULL){if (cur->val == x){return cur;}cur = cur->next;}return NULL;
}//在pos前面、后面插入
void SLTInsert(SLNode** pphead, SLNode* pos, SLNDataType x)
{//严格限制pos一定是一个有效结点assert(pphead);assert(*pphead);assert(pos);if ((*pphead) == pos){//头插SLTPushFront(pphead, x);}else{SLNode* prev = *pphead;while (prev->next != pos){prev = prev->next;}SLNode* newNode = CreateNode(x);prev->next = newNode;newNode->next = pos;}
}void SLTInsertAfter(SLNode* pos, SLNDataType x)
{assert(pos);SLNode* newNode = CreateNode(x);newNode->next = pos->next;pos->next = newNode;
}//删除当前或后面一个结点
void SLTErase(SLNode** pphead, SLNode* pos)
{assert(pphead);assert(*pphead);assert(pos);if ((*pphead) == pos){//头删SLTPopFront(pphead);}else{SLNode* prev = *pphead;while (prev->next != pos){prev = prev->next;}prev->next = pos->next;free(pos);pos = NULL;}
}void SLTEraseAfter(SLNode* pos)
{assert(pos);assert(pos->next);SLNode* tmp = pos->next;pos->next = pos->next->next;free(tmp);tmp = NULL;
}void SLTDestroy(SLNode** pphead)
{assert(pphead);SLNode* cur = *pphead;while (cur){SLNode* next = cur->next;free(cur);cur = NULL;cur = next;}*pphead = NULL;
}

test.c

#include "SList.h"void SLNTest()
{SLNode* plist = NULL;SLTPushBack(&plist, 1);SLTPushBack(&plist, 2);SLTPushBack(&plist, 3);SLTPushBack(&plist, 4);SLTPushFront(&plist, 5);SLTPushFront(&plist, 6);SLTPushFront(&plist, 7);SLTPushFront(&plist, 8);printf("头尾插入各4个:\n");SLTPrint(plist);SLNode* find1 = SLTFind(&plist, 5);SLTInsert(&plist, find1, 9);SLTInsertAfter(find1, 10);printf("找5的结点,并在前后分别插入9和10:\n");SLTPrint(plist);SLNode* find2 = SLTFind(&plist, 2);SLTEraseAfter(find2);SLTErase(&plist, find2);printf("删除2后面的结点,再删除2的结点:\n");SLTPrint(plist);SLTDestroy(&plist);printf("销毁全部结点:\n");SLTPrint(plist);
}int main()
{SLNTest();return 0;
}

运行效果

效果

4. 带头双向循环链表

下面将其分为3个模块进行实现List.h,List.c,test.c

4.1 接口设计(List.h)

因为有哨兵位的存在,对头结点的插入删除时不需要改变外部链表,只需要改变结构体内部,因此这里的设计传参采用一级指针的方式

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>typedef int LTDataType;typedef struct ListNode
{LTDataType val;struct ListNode* next;struct ListNode* prev;
}LTNode;LTNode* LTInit();
void LTPrint(LTNode* phead);
LTNode* CreateLTNode(LTDataType x);void LTPushBack(LTNode* phead, LTDataType x);
void LTPushFront(LTNode* phead, LTDataType x);
void LTPopBack(LTNode* phead);
void LTPopFront(LTNode* phead);LTNode* LTFind(LTNode* phead, LTDataType x);
//在pos前插入
void LTInsert(LTNode* pos, LTDataType x);
//删除pos位置
void LTErase(LTNode* pos);
void LTDestroy(LTNode* phead);

4.2 接口实现(List.c)

1)创建结点

LTNode* CreateLTNode(LTDataType x)
{LTNode* newNode = (LTNode*)malloc(sizeof(LTNode));if (newNode == NULL){perror("malloc fail\n");exit(-1);}newNode->val = x;newNode->next = NULL;newNode->prev = NULL;return newNode;
}

2)初始化和打印

初始化将利用创建结点函数初始化,并返回指针。打印从哨兵位下一个结点开始,cur与哨兵位指针地址相同则结束

LTNode* LTInit()
{LTNode* phead = CreateLTNode(-1);phead->next = phead;phead->prev = phead;return phead;
}void LTPrint(LTNode* phead)
{assert(phead);LTNode* cur = phead->next;printf("head <-> ");while (cur != phead){printf("%d <-> ", cur->val);cur = cur->next;}printf("head\n");
}

3)头尾插入删除

这里将展示3种办法实现,最重要的是要复用函数,这样可以在短时间内容快速手撕带头双向链表

第一种办法是不引入其他指针,要先设置好newNode;

第二种办法是引入多一个指针

第三种办法是复用 LTInsert() 和 LTErase()

void LTPushBack(LTNode* phead, LTDataType x)
{assert(phead);//1 不引入其他指针,要先设置好newNode//LTNode* newNode = CreateLTNode(x);//newNode->next = phead;//newNode->prev = phead->prev;//phead->prev->next = newNode;//phead->prev = newNode;//2 引入多一个指针//LTNode* newNode = CreateLTNode(x);//LTNode* tail = phead->prev;//phead->prev = newNode;//newNode->prev = phead;//newNode->next = tail;//tail->next = newNode;//3 复用插入LTInsert(phead, x);
}void LTPushFront(LTNode* phead, LTDataType x)
{assert(phead);//1//LTNode* newNode = CreateLTNode(x);引入多一个指针//LTNode* next = phead->next;//phead->next = newNode;//newNode->prev = phead;//newNode->next = next;//next->prev = newNode;//2 复用插入LTInsert(phead->next, x);
}void LTPopBack(LTNode* phead)
{assert(phead);//空的情况assert(phead->next != phead);//1 引入多一个//LTNode* tail = phead->prev;//LTNode* tailPrev = tail->prev;//free(tail);//tail = NULL;//tailPrev->next = phead;//phead->prev = tailPrev;//2 复用删除LTErase(phead->prev);
}void LTPopFront(LTNode* phead)
{assert(phead);//空的情况assert(phead->next != phead);//1//LTNode* tmp = phead->next;//phead->next = phead->next->next;//phead->next->prev = phead;//free(tmp);//tmp = NULL;//2LTErase(phead->next);
}

4)查找插入删除销毁

这里的插入删除是实现头尾插入删除部分函数复用的关键

需要明确的是插入为在pos前面插入

LTNode* LTFind(LTNode* phead, LTDataType x)
{assert(phead);LTNode* cur = phead->next;while (cur != phead){if (cur->val == x){return cur;}cur = cur->next;}return NULL;
}//在pos前插入
void LTInsert(LTNode* pos, LTDataType x)
{assert(pos);LTNode* newNode = CreateLTNode(x);LTNode* posPrev = pos->prev;posPrev->next = newNode;newNode->next = pos;newNode->prev = posPrev;pos->prev = newNode;
}//删除pos位置
void LTErase(LTNode* pos)
{assert(pos);LTNode* posPrev = pos->prev;LTNode* posNext = pos->next;posPrev->next = posNext;posNext->prev = posPrev;free(pos);pos = NULL;		//这个代码可有可无,因为pos改变不了外部
}void LTDestroy(LTNode* phead)
{assert(phead);LTNode* cur = phead->next;while (cur != phead){LTNode* tmp = cur;cur = cur->next;free(tmp);tmp = NULL;}free(phead);phead = NULL;	//和LTErase一致
}

4.3 完整代码

List.h

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>typedef int LTDataType;typedef struct ListNode
{LTDataType val;struct ListNode* next;struct ListNode* prev;
}LTNode;LTNode* LTInit();
void LTPrint(LTNode* phead);
LTNode* CreateLTNode(LTDataType x);void LTPushBack(LTNode* phead, LTDataType x);
void LTPushFront(LTNode* phead, LTDataType x);
void LTPopBack(LTNode* phead);
void LTPopFront(LTNode* phead);LTNode* LTFind(LTNode* phead, LTDataType x);
//在pos前插入
void LTInsert(LTNode* pos, LTDataType x);
//删除pos位置
void LTErase(LTNode* pos);
void LTDestroy(LTNode* phead);

List.c

#include "List.h"//创建
LTNode* CreateLTNode(LTDataType x)
{LTNode* newNode = (LTNode*)malloc(sizeof(LTNode));if (newNode == NULL){perror("malloc fail\n");exit(-1);}newNode->val = x;newNode->next = NULL;newNode->prev = NULL;return newNode;
}//初始化和打印
LTNode* LTInit()
{LTNode* phead = CreateLTNode(-1);phead->next = phead;phead->prev = phead;return phead;
}void LTPrint(LTNode* phead)
{assert(phead);LTNode* cur = phead->next;printf("head <-> ");while (cur != phead){printf("%d <-> ", cur->val);cur = cur->next;}printf("head\n");
}//头尾插入删除
//第一种方法都是正常的实现,包括使用3个指针简化
//第二种方法是复用,从而快速实现
void LTPushBack(LTNode* phead, LTDataType x)
{assert(phead);//1 不引入其他指针,要先设置好newNode//LTNode* newNode = CreateLTNode(x);//newNode->next = phead;//newNode->prev = phead->prev;//phead->prev->next = newNode;//phead->prev = newNode;//2 引入多一个指针//LTNode* newNode = CreateLTNode(x);//LTNode* tail = phead->prev;//phead->prev = newNode;//newNode->prev = phead;//newNode->next = tail;//tail->next = newNode;//3 复用插入LTInsert(phead, x);
}void LTPushFront(LTNode* phead, LTDataType x)
{assert(phead);//1//LTNode* newNode = CreateLTNode(x);引入多一个指针//LTNode* next = phead->next;//phead->next = newNode;//newNode->prev = phead;//newNode->next = next;//next->prev = newNode;//2 复用插入LTInsert(phead->next, x);
}void LTPopBack(LTNode* phead)
{assert(phead);//空的情况assert(phead->next != phead);//1 引入多一个//LTNode* tail = phead->prev;//LTNode* tailPrev = tail->prev;//free(tail);//tail = NULL;//tailPrev->next = phead;//phead->prev = tailPrev;//2 复用删除LTErase(phead->prev);
}void LTPopFront(LTNode* phead)
{assert(phead);//空的情况assert(phead->next != phead);//1//LTNode* tmp = phead->next;//phead->next = phead->next->next;//phead->next->prev = phead;//free(tmp);//tmp = NULL;//2LTErase(phead->next);
}//查找插入删除销毁
//上面提及的第二种办法就是复用插入和删除的函数
LTNode* LTFind(LTNode* phead, LTDataType x)
{assert(phead);LTNode* cur = phead->next;while (cur != phead){if (cur->val == x){return cur;}cur = cur->next;}return NULL;
}//在pos前插入
void LTInsert(LTNode* pos, LTDataType x)
{assert(pos);LTNode* newNode = CreateLTNode(x);LTNode* posPrev = pos->prev;posPrev->next = newNode;newNode->next = pos;newNode->prev = posPrev;pos->prev = newNode;
}//删除pos位置
void LTErase(LTNode* pos)
{assert(pos);LTNode* posPrev = pos->prev;LTNode* posNext = pos->next;posPrev->next = posNext;posNext->prev = posPrev;free(pos);pos = NULL;		//这个代码可有可无,因为pos改变不了外部
}void LTDestroy(LTNode* phead)
{assert(phead);LTNode* cur = phead->next;while (cur != phead){LTNode* tmp = cur;cur = cur->next;free(tmp);tmp = NULL;}free(phead);phead = NULL;	//和LTErase一致
}

test.c

#include "List.h"void ListTTest()
{LTNode* plist = LTInit();LTPushBack(plist, 1);LTPushBack(plist, 2);LTPushBack(plist, 3);LTPushBack(plist, 4);LTPushFront(plist, 5);LTPushFront(plist, 6);LTPushFront(plist, 7);LTPushFront(plist, 8);printf("头尾各插入4个元素:\n");LTPrint(plist);LTPopBack(plist);LTPopBack(plist);LTPopFront(plist);LTPopFront(plist);printf("头尾各删除2个元素:\n");LTPrint(plist);printf("查找1的位置并在前面插入10:\n");LTNode* pos = LTFind(plist, 1);if (pos != NULL){LTInsert(pos, 10);}LTPrint(plist);printf("查找5的位置并将其删除:\n");pos = LTFind(plist, 5);if (pos != NULL){LTErase(pos);pos = NULL;}LTPrint(plist);printf("删除全部结点:\n");LTDestroy(plist);plist = NULL;LTPrint(plist);
}int main()
{ListTTest();return 0;
}

运行效果

双向带头循环链表运行效果

5. 顺序表和链表的优缺点

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

总结:

1)链表(双向)

优势:a.任意位置插入和删除都是O(1);b.按需申请释放,合理利用空间,不存在浪费

问题:下标访问不方便O(N)

2)顺序表

优势:支持下标随机访问O(1)

问题: a.头部或者中间插入删除效率低O(N);b.空间不够需要扩容,扩容有一定消耗且存在一定空间浪费;c.只适合尾插尾删除

相关文章:

链表的概念和结构

文章目录 1. 链表的概念2. 链表的分类3. 单向不带头非循环链表3.1 接口设计&#xff08;SList.h&#xff09;3.2 接口实现&#xff08;SList.c&#xff09;1&#xff09;打印和创建结点2&#xff09;头尾插入删除3&#xff09;查找和插入4&#xff09;删除和销毁 3.3 完整代码S…...

使用AI后为什么思考会变得困难?

使用AI后为什么思考会变得困难&#xff1f; 我总结了四篇近期的研究论文&#xff0c;来展示AI是如何以及为什么侵蚀我们的批判性思维能力。 作者使用AI制作的图像 前言&#xff1a;作者在这篇文章中&#xff0c;借AI技术的崛起&#xff0c;揭示了一场悄然发生的思想博弈。表面…...

Github 2025-03-02 php开源项目日报Top10

根据Github Trendings的统计,今日(2025-03-02统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量PHP项目10Blade项目1JavaScript项目1Nextcloud服务器:安全的数据之家 创建周期:2796 天开发语言:PHP, JavaScript协议类型:GNU Affero Gene…...

智能座舱介绍

目录 智能座舱智能座舱的核心技术组成车载信息娱乐系统(IVI)数字仪表盘与HUD(抬头显示)语音交互与AI助手多屏联动与场景化交互生物识别技术智能座舱的发展趋势沉浸式体验情感化与个性化多模态交互融合车联网(V2X)生态扩展应用场景挑战与未来硬件系统软件系统关键技术智能…...

2025年能源工作指导意见

2025年是“十四五”规划收官之年&#xff0c;做好全年能源工作意义重大。为深入贯彻落实党中央、国务院决策部署&#xff0c;以能源高质量发展和高水平安全助力我国经济持续回升向好&#xff0c;满足人民群众日益增长的美好生活用能需求&#xff0c;制定本意见。 一、总体要求…...

​豪越科技:智慧园区后勤单位消防安全管理,实时告警与整改闭环

在当今数字化、智能化飞速发展的时代&#xff0c;智慧园区已成为现代产业发展的重要载体。而园区后勤单位的消防安全管理&#xff0c;作为保障园区安全运营的关键环节&#xff0c;正面临着前所未有的挑战与机遇。豪越科技凭借其先进的技术和丰富的经验&#xff0c;为智慧园区后…...

zookeeper-docker版

Zookeeper-docker版 1 zookeeper概述 1.1 什么是zookeeper Zookeeper是一个分布式的、高性能的、开源的分布式系统的协调&#xff08;Coordination&#xff09;服务&#xff0c;它是一个为分布式应用提供一致性服务的软件。 1.2 zookeeper应用场景 zookeeper是一个经典的分…...

华为手机自助维修的方法

测试环境&#xff1a;华为荣耀 &#xff08;全文完&#xff09;...

基于Springboot博物馆文博资源库系统【附源码】

基于Springboot博物馆文博资源库系统 效果如下&#xff1a; 系统登陆页面 文物信息管理页面 流动申请页面 文物报修页面 个人信息页面 文物保修管理页面 系统主页面 文物类型页面 研究背景 随着信息技术的飞速发展&#xff0c;博物馆文博资源的管理与利用日益受到重视。传统…...

辛格迪客户案例 | 祐儿医药科技GMP培训管理(TMS)项目

01 项目背景&#xff1a;顺应行业趋势&#xff0c;弥补管理短板 随着医药科技行业的快速发展&#xff0c;相关法规和标准不断更新&#xff0c;对企业的质量管理和人员培训提出了更高要求。祐儿医药科技有限公司&#xff08;以下简称“祐儿医药”&#xff09;作为一家专注于创新…...

Git 2.48.1 官方安装与配置全流程指南(Windows平台)

一、软件简介 Git 是 分布式版本控制系统 的标杆工具&#xff0c;由 Linus Torvalds 开发&#xff0c;广泛应用于代码版本管理、团队协作开发等场景。2.48.1 版本优化了文件系统监控性能&#xff0c;并修复了跨平台兼容性问题。 二、下载准备 1. 官方下载地址 访问 Git 官网…...

MATLAB中asManyOfPattern函数用法

目录 语法 说明 示例 匹配尽可能多的模式实例 指定要匹配的最小模式数 指定要匹配的最小和最大模式数 asManyOfPattern函数的功能是模式匹配次数尽可能多。 语法 newpat asManyOfPattern(pat) newpat asManyOfPattern(pat,minPattern) newpat asManyOfPattern(pat,m…...

大模型推理时的尺度扩展定律

大模型推理时的尺度扩展定律 FesianXu at 20250212 at Wechat Search Team 前言 大模型的尺度扩展定律告诉我们&#xff1a;『LLM的性能会随着模型的参数量、模型的训练量、模型的训练数据量的增加而增加』。训练存在尺度扩展定律&#xff0c;测试也存在尺度扩展定律&#xff…...

迷你世界脚本世界UI接口:UI

世界UI接口&#xff1a;UI 彼得兔 更新时间: 2023-10-25 10:40:44 具体函数名及描述如下: 序号 函数名 函数描述 1 setGBattleUI(...) 设置战斗总结UI 2 world2RadarPos(...) 世界坐标转换到小地图 3 world2RadarDist(...) 世界长度转换到小地图 4 …...

局域网自动识别机器名和MAC并生成文件的命令

更新版本&#xff1a;添加了MAC 地址 确定了设备唯一性 V1.1 局域网自动识别机器名和MAC并生成文件的批处理命令 echo off setlocal enabledelayedexpansionREM 设置输出文件 set outputFilenetwork_info.txtREM 清空或创建输出文件 echo Scanning network from 192.168.20.1…...

神经网络之词嵌入模型(基于torch api调用)

一、Word Embedding&#xff08;词嵌入&#xff09;简介 Word Embedding&#xff08;词嵌入&#xff09;&#xff1a; 词嵌入技术是自然语言处理&#xff08;NLP&#xff09;领域的一项重大创新&#xff0c;它极大地推动了计算机理解和处理人类语言的能力。 通过将单词、句子甚…...

微服务即时通信系统---(七)文件管理子服务

目录 功能设计 模块划分 业务接口/功能示意图 服务实现流程 服务代码实现 封装文件操作模块(utils.hpp) 获取唯一标识ID 文件读操作 文件写操作 编写proto文件 文件元信息 文件管理proto 单文件上传 多文件上传 单文件下载 多文件下载 RPC调用 服务端创建子…...

鸿蒙5.0实战案例:基于原生能力获取视频缩略图

往期推文全新看点&#xff08;文中附带全新鸿蒙5.0全栈学习笔录&#xff09; ✏️ 鸿蒙&#xff08;HarmonyOS&#xff09;北向开发知识点记录~ ✏️ 鸿蒙&#xff08;OpenHarmony&#xff09;南向开发保姆级知识点汇总~ ✏️ 鸿蒙应用开发与鸿蒙系统开发哪个更有前景&#…...

《Canvas修仙传·第三重天金丹境(下集)》 ——量子烟花与物理宇宙的混沌法则

各位道友久候&#xff01;上集我们炼就了《灵蛇奇谭》的元神&#xff0c;今日将开启Canvas修仙路上最绚丽的篇章——掌控微观粒子的创世之力&#xff01;(&#xff89;≧∀≦)&#xff89; 章前黑话词典 &#x1f50d; 量子境术语表&#xff1a; 对象池&#xff08;Object Po…...

Hutool - POI:让 Excel 与 Word 操作变得轻而易举

各位开发者们&#xff0c;在日常的 Java 开发工作里&#xff0c;处理 Excel 和 Word 文件是相当常见的需求。无论是从 Excel 里读取数据进行分析&#xff0c;还是将数据写入 Excel 生成报表&#xff0c;亦或是对 Word 文档进行内容编辑&#xff0c;传统的 Apache POI 库虽然功能…...

请谈谈 Node.js 中的流(Stream)模块,如何使用流进行数据处理?

1. Node.js中的流&#xff08;Stream&#xff09;模块 流的基本概念&#xff1a; 流是 Node.js 中用于处理流式数据的抽象接口。 它是一种高效的数据处理机制&#xff0c;适合处理大文件或高数据吞吐量的场景。 流主要有四种类型&#xff1a; Readable&#xff1a;可读流&am…...

DeepSeek 202502 开源周合集

DeepSeek 本周的开源项目体现了其在 AI 技术栈中的深厚积累&#xff0c;从硬件协同优化&#xff08;FlashMLA&#xff09;、通信库&#xff08;DeepEP&#xff09;、核心计算&#xff08;DeepGEMM&#xff09;到推理模型&#xff08;DeepSeek-R1&#xff09;&#xff0c;覆盖了…...

《从0到1:用Python在鸿蒙系统开发安防图像分类AI功能》

在人工智能与移动应用深度融合的当下,类目标签AI功能成为众多行业提升效率和用户体验的关键技术。本文聚焦于HarmonyOS NEXT API 12及以上版本,以图像分类在智能家居安防领域的应用为例,为开发者详细阐述如何利用Python开发类目标签AI功能,助力鸿蒙技术在该领域的创新应用。…...

【机器学习chp10】降维——(核化)PCA + MDS + lsomap + 拉普拉斯特征映射 + t-NSE + UMAP

目录 一、降维的意义与本质 1、意义 2、本质 3、常见降维方法 &#xff08;1&#xff09;线性降维 &#xff08;2&#xff09;非线性降维 二、基于重构的降维 1、PCA 2、核化PCA &#xff08;1&#xff09;实现过程 步骤一&#xff1a;数据映射与核函数定义 步骤二…...

Visual Studio 2022 常用快捷键

1. 格式化代码&#xff1a;ctrl K,ctrl f 2. 向下插入一行&#xff1a;ctrl shift 回车 3. 全部变成大写&#xff1a;ctrl shift U 4. 全部变成小写&#xff1a;ctrl U 5. 查找&#xff1a; ctrl f 6. 删除当前行&#xff1a; ctrl shi…...

JavaWeb——CSS

一、什么是CSS CSS(Cascading Style Sheet)&#xff1a;层叠样式表&#xff0c;是一种用于描述网页内容外观和样式的语言&#xff0c;主要用于控制网页的布局、颜色、字体、间距、动画等视觉效果。 CSS基本语法&#xff1a; CSS基本语法十分简单&#xff0c;基本由以下几个部分…...

Oracle 数据库基础入门(四):分组与联表查询的深度探索(下)

在 Oracle 数据库的操作中&#xff0c;联合查询与子查询是获取复杂数据的关键手段。当单表数据无法满足业务需求时&#xff0c;联合查询允许我们从多张表中提取关联信息&#xff0c;而子查询则能以嵌套的方式实现更灵活的数据筛选。对于 Java 全栈开发者而言&#xff0c;掌握这…...

【欢迎来到Git世界】Github入门

241227 241227 241227 Hello World 参考&#xff1a;Hello World - GitHub 文档. 1.创建存储库 r e p o s i t o r y repository repository&#xff08;含README.md&#xff09; 仓库名需与用户名一致。 选择公共。 选择使用Readme初始化此仓库。 2.何时用分支&#xf…...

从矩阵乘法探秘Transformer

目录 前言1. transformer背景1.1 回顾线性代数的知识1.1.1 矩阵和行向量1.1.2 矩阵相乘和算子作用1.1.3 从分块矩阵的乘法来看 Q K T V QK^TV QKTV 1.2 encoder-decoder1.3 低阶到高阶语义向量的转换1.4 核心的问题 2. transformer网络结构2.1 基于KV查询的相似性计算2.2 在一个…...

【前端基础】Day 3 CSS-2

目录 1. Emmet语法 1.1 快速生成HTML结构语法 1.2 快速生成CSS样式语法 2. CSS的复合选择器 2.1 后代选择器 2.2 子选择器 2.3 并集选择器 2.4 伪类选择器 2.4.1 链接伪类选择器 2.4.2 focus伪类选择器 2.5 复合选择器总结 3. CSS的元素显示模式 3.1 什么是元素显示…...

Difyにboto3を変更したカスタムDockerイメージの構築手順

Difyにboto3を変更したカスタムDockerイメージの構築手順 はじめに1. Dockerfileの作成2. Dockerイメージのビルド3. docker-compose.yamlの更新変更点&#xff1a; 4. コンテナの再起動注意事項まとめ はじめに DifyのDockerイメージに特定バージョンのboto3を変更する手順を…...

C++ 类和对象(上)

Hello&#xff01;&#xff01;&#xff01;大家早上中午晚上好&#xff01;&#xff01;&#xff01;今天我们来复习C中最重要的内容之一&#xff1a;类和对象&#xff01;&#xff01; 一、类的定义 1.1语法 类定义关键字class 类名 {//括号体类定义成员变量或成员函数}&…...

虚拟机快照与linux的目录结构

虚拟机快照是对虚拟机某一时刻状态的完整捕获&#xff0c;包括内存、磁盘、配置及虚拟硬件状态等&#xff0c;保存为独立文件。 其作用主要有数据备份恢复、方便系统测试实验、用于灾难恢复以及数据对比分析。具有快速创建和恢复、占用空间小、可多个快照并存的特点。在管理维…...

代码随想录算法训练营第三十天 | 卡码网46.携带研究材料(二维解法)、卡码网46.携带研究材料(滚动数组)、LeetCode416.分割等和子集

代码随想录算法训练营第三十天 | 卡码网46.携带研究材料&#xff08;二维解法&#xff09;、卡码网46.携带研究材料&#xff08;滚动数组&#xff09;、LeetCode416.分割等和子集 01-1 卡码网46.携带研究材料&#xff08;二维&#xff09; 相关资源 题目链接&#xff1a;46. 携…...

计算机基础:二进制基础03,二进制数的位基和位权

专栏导航 本节文章分别属于《Win32 学习笔记》和《MFC 学习笔记》两个专栏&#xff0c;故划分为两个专栏导航。读者可以自行选择前往哪个专栏。 &#xff08;一&#xff09;WIn32 专栏导航 上一篇&#xff1a;计算机基础&#xff1a;二进制基础02&#xff0c;用二进制来计数…...

知识库技术选型:主流Embedding模型特性对比

知识库技术选型&#xff1a;主流Embedding模型特性对比 1. 知识库与大模型结合的背景 知识库是存储和管理结构化知识的系统&#xff0c;广泛应用于问答系统、推荐系统和搜索引擎等领域。随着大语言模型&#xff08;LLM&#xff09;的发展&#xff0c;知识库与大模型的结合成为…...

鸿蒙开发第4篇__关于在鸿蒙应用中使用Java语言进行设计

本博文对于鸿蒙APP程序员来说&#xff0c;很重要 HarmonyOS从 API8 开始不再支持使用Java作为开发语言&#xff0c;未来的新功能将在ArkTS中实现. API 8对应的是HarmonyOS 3.0.0版本, 2022年7月27日&#xff0c; 华为发布了HarmonyOS 3.0。 请看下图&#xff1a; 因此&#…...

监控视频流web端播放

最近在弄一个视频监测系统&#xff0c;核心功能之一就是视频监控查看。选择的方案是FFMPEGRTSP2web组合。 1、环境搭建&前期准备&#xff1a; 准备好软件&#xff0c;&#xff08;ffmpeg网上很多&#xff0c;大家自己去下载吧&#xff0c;rtsp-server已上传&#xff0c;方…...

Java中的泛型类 --为集合的学习做准备

学习目标 ● 掌握在集合中正确使用泛型 ● 了解泛型类、泛型接口、泛型方法 ● 了解泛型上下限 ● 了解基本的使用场景 1.有关泛型 1.1泛型的概念 泛型&#xff08;Generics&#xff09;是Java中引入的参数化类型机制&#xff0c;允许在定义类、接口或方法时使用类型参数&a…...

【MySQL】表的基本操作

??表的基本操作 文章目录&#xff1a; 表的基本操作 创建查看表 创建表 查看表结构 表的修改 表的重命名 表的添加与修改 删除表结构 总结 前言&#xff1a; 在数据库中&#xff0c;数据表是存储和组织数据的基本单位&#xff0c;对于数据表的操作是每个程序员需要烂熟…...

横向移动靶场-Tr0ll: 3

Tr0ll: 3来自 <Tr0ll: 3 ~ VulnHub> 1&#xff0c;将两台虚拟机网络连接都改为NAT模式 2&#xff0c;攻击机上做namp局域网扫描发现靶机 nmap -sn 192.168.23.0/24 那么攻击机IP为192.168.23.182&#xff0c;靶场IP192.168.23.187 3&#xff0c;对靶机进行端口服务探测 …...

房屋租赁|房屋租赁系统|基于Springboot的房屋租赁系统设计与实现(源码+数据库+文档)

房屋租赁系统目录 目录 基于Springboot的房屋租赁系统设计与实现 一、前言 二、系统功能设计 三、系统实现 管理员功能模块 房主功能模块 用户功能模块 四、数据库设计 1、实体ER图 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取&#xff1a…...

自然语言处理:词频-逆文档频率

介绍 大家好&#xff0c;博主又来给大家分享知识了。本来博主计划完成稠密向量表示的内容分享后&#xff0c;就开启自然语言处理中文本表示的讲解。可在整理分享资料的时候&#xff0c;博主发现还有个知识点&#xff0c;必须得单独拎出来好好说道说道。 这就是TF-IDF&#xf…...

Linux | 程序 / 进程调用库依赖关系查看

注&#xff1a;本文为 “Linux | 程序 / 进程调用库依赖” 相关文章合辑。 英文引文&#xff0c;机翻未校。 未整理去重。 How to Check Library Dependencies in Linux 如何在 Linux 系统中检查库依赖关系 Mohd Shakir Zakaria Programs on Linux often require external…...

3-4 WPS JS宏 工作表的新建、删除与错务内容处理(批量新建工作表)学习笔记

************************************************************************************************************** 点击进入 -我要自学网-国内领先的专业视频教程学习网站 *******************************************************************************************…...

泰勒公式详解与应用

前言 本文隶属于专栏《机器学习数学通关指南》&#xff0c;该专栏为笔者原创&#xff0c;引用请注明来源&#xff0c;不足和错误之处请在评论区帮忙指出&#xff0c;谢谢&#xff01; 本专栏目录结构和参考文献请见《机器学习数学通关指南》 正文 &#x1f4dd; 一句话总结 泰…...

计算机视觉(opencv-python)入门之图像的读取,显示,与保存

在计算机视觉领域&#xff0c;Python的cv2库是一个不可或缺的工具&#xff0c;它提供了丰富的图像处理功能。作为OpenCV的Python接口&#xff0c;cv2使得图像处理的实现变得简单而高效。 示例图片 目录 opencv获取方式 图像基本知识 颜色空间 RGB HSV 图像格式 BMP格式 …...

现代前端框架渲染机制深度解析:虚拟DOM到编译时优化

引言&#xff1a;前端框架的性能进化论 TikTok Web将React 18迁移至Vue 3后&#xff0c;点击响应延迟降低42%&#xff0c;内存占用减少35%。Shopify采用Svelte重构核心交互模块&#xff0c;首帧渲染速度提升580%。Discord在Next.js 14中启用React Server Components后&#xf…...

Selenium自动化测试:如何搭建自动化测试环境,搭建环境过程应该注意的问题

最近也有很多人私下问我&#xff0c;selenium学习难吗&#xff0c;基础入门的学习内容很多是3以前的版本资料&#xff0c;对于有基础的人来说&#xff0c;3到4的差别虽然有&#xff0c;但是不足以影响自己&#xff0c;但是对于没有学过的人来说&#xff0c;通过资料再到自己写的…...

Linux服务升级:Almalinux 升级 DeepSeek-R1

目录 一、实验 1.环境 2.Almalinux 部署 Ollama 3.Almalinux 升级 DeepSeek-R1 4.Almalinux 部署 docker 5. docker 部署 DeepSeek-R1 6.Almalinux 部署 Cpolar (内网穿透) 7.使用cpolar内网穿透 二、问题 1.构建容器失败 一、实验 1.环境 &#xff08;1&#xff09…...