链表
链表
基本概念
- 链表(数据结构):线性表(逻辑结构)的链式存储结构
- 单链表 VS 顺序表
- 在单链表上效率更高的操作
- 删除所有值为x的数
- 删除某个任意元素
- 在任意元素后插入一个数
- 在顺序表上效率更高的操作
- 在最后一个元素后面插入一个数
- 交换任意两个元素的值
- 取出某个元素的值
- 在单链表上效率更高的操作
- 设置头节点的好处:方便运算的实现
- 带头结点的双循环链表
- 链表为空的标志:
head->prior==head&&head->next=head
- 实现删除尾节点和在尾节点后插入的操作效率最高
- 链表为空的标志:
- 带尾指针的单循环链表
- 实现删除头节点和在尾节点后插入的操作效率最高
- 单链表 VS 顺序表
代码实现
-
定义
typedef struct List { int val List* next; List(int _val):val(_val),next(NULL){ } };
-
链式法
-
尾插法
-
带表头
List* creatlist() { List* L1=new(List); List* L2=new(List); /**/List* head=new(List); /**/head->next=L1; while(1) { if(/* 终止条件 */) break; L1->val=val; L2->next=L1; L2=L1; L1=new(List); } L2->next=NULL;//尾节点指向NULL,即代表单链表 //L2->next=head;尾节点指向头节点,即代表循环链表 return head; }
-
无表头:只需将上面的两行/**/标记行改为
List head=L1;
即可
-
-
头插法
-
带表头
List *creatlist() { List *L1 = NULL; List *L2 = NULL; while (1) { if (/* 终止条件 */) break; L1 = new (List); L1->val = val; L1->next = L2; L2 = L1; } /**/List *head = new (List); /**/head->next = L1; return head; }
-
无表头:只需将上面的两行标记行改为
List head=L1;
即可
-
-
总结
-
整体逻辑:L1和L2两个结点,交替地向后或向前循环建立起整个链表
-
尾插法vs头插法
- 尾插法:函数一开始new出来的L1是最终的头节点,所以head=L1写在while循环前
- 头插法:函数一开始new出来的L1是最终的尾节点,直到while循环结束后,L1才是头节点,所以head=L1写while循环之后
- 尾插法:
L2->next=L1;L2=L1;
- 头插法:
L1->next=L2;L2=L1;
-
带表头vs无表头
- 带表头:给表头head分配空间,然后
head->next=L1;
- 无表头:不给表头head分配空间,直接
head=L1;
- 带表头:给表头head分配空间,然后
-
单链表vs循环链表
- 单链表尾:指针指向NULL
- 循环链表:尾指针指向头结点
-
-
-
数组法
-
尾插法
typedef struct List { int val; List* next; }; int main() { int n; cin>>n; List a[500]; for(int i=0;i<n;i++) { /* 输入数据 */ a[i].next=&a[i+1]; } /* 可用sort(a,n,cmp)对链表里的数据进行排序 */ for(int i=0;i<n;i++) { a[i].next=&a[i+1]; } /* 排序后需要重新建立链式关系 */ a[n-1].next=NULL; }
-
头插法
const int N=10010; int e[N],ne[N],idx,head=-1; void insert_head(int x) { e[idx]=x; ne[idx]=head; head=idx++; } //在链表头插入一个节点 void insert(int x,int k) { e[idx]=x; ne[idx]=ne[k]; ne[k]=idx++; } //在节点k后插入一个节点 void remove(int k) { ne[k]=ne[ne[k]]; } //删除一个节点
-
-
常用操作
-
插入
- step1:new一个插入结点x
- step2:next[x]=next[k],next[k]=x
-
删除:next[k]=next[next[k]]
-
执行删除操作时候的一个小技巧:虚拟头结点
List *deletenode(List* head,int k) { List* newhead=new List(0); newhead->next=head; //在真正的头结点之前设置一个虚拟的头节点 //使得需要删除真正的头节点时的处理逻辑,和删除任意结点时的处理逻辑相同 return newhead->next; //注意不能返回head,因为有可能head结点是被删除的,而你没有delet head; }
-
-
-
解题技巧
- 双指针法:fast指针和slow指针
- 反转链表:slow=NULL,fast=head,每次让fast—>next=slow,然后往下遍历
- 倒数第k个结点:fast先走k步,然后slow从head开始跟fast一起走,当fast走到NULL时,slow刚好在倒数第k个结点
- 循环链表
- 双向链表
- 双指针法:fast指针和slow指针