链表分为8种:带头/不带头节点、单链表/双向链表、循环/不循环链表,每一个链表都是由这三个条件拼起来的,2*2*2=8种
逻辑结构:线性结构(元素类型相同、序列、有限个)
存储结构:链式存储(以不带头结点的单链表为例)
cpp
typedef int DataType;
这一句代码很方便,可以清楚的将数据域和指针域分开来算,而且代码会变得清晰明了。
cpp
typedef struct LNode {
DataType data;
struct LNode* next;
}LNode;
声明好我们的链表节点。
cpp
LNode* L = NULL;
这一句代码就是模仿的数组的数组名,数组名是头指针,那我们L也是一个头指针,方便找到后面的所有节点。
基本操作:增删查改
创建单链表的方式有三种(头插法:记得是在头结点后面插入、尾插法:记得始终保留一个尾指针、手撕一个链表)
cpp
void CreateByHead(LNode** pL)
{
assert(pL);
int cnt = 0;
int i = 0;
while (scanf("%d", &i) != EOF) {
LNode* s = (LNode*)malloc(sizeof(LNode));
s->data = i;
if (cnt == 0) {
*pL = s;
s->next = NULL;
cnt++;
}
else {
s->next = *pL;
*pL = s;
}
}
}
void CreateByTail(LNode** pL)
{
assert(pL);
int i = 0;
int cnt = 0;
LNode* r = NULL;
while (scanf("%d", &i) != EOF) {
LNode* s = (LNode*)malloc(sizeof(LNode));
s->data = i;
if (cnt == 0) {
*pL = s;
s->next = NULL;
r = s;
cnt++;
}
else {
s->next = NULL;
r->next = s;
r = s;
}
}
}
增加代码:
cpp
void AddList(LNode** pL, int i, int e)
{
assert(pL);
assert(i >= 1);
if (i == 1) {
LNode* s = (LNode*)malloc(sizeof(LNode));
s->data = e;
s->next = *pL;
*pL = s;
return;
}
int j = 1;
LNode* p = *pL;
while (j < i - 1 && p != NULL) {
p = p->next;
j++;
}
if (p == NULL) {
printf("非法位置\n");
return;
}
LNode* s = (LNode*)malloc(sizeof(LNode));
s->data = e;
s->next = p->next;
p->next = s;
printf("插入成功\n");
}
我们一定要先把该判断的(pL,插入位置合法性)判断好,然后找到要插入的位置的前一个节点,这个要根据j来确定,j的大小就是指向第j个节点,找到后再插入即可,插入还有前插和后插,后插很简单,前插我们可以找到指定的结点的前一个节点再后插即可,或者直接后插然后交换两者的数据域。
删除操作也是一样,找到指定节点的前一个节点后删除即可。
查找和修改的代码也比较简单这里不实现了。
这是最简单的不带头结点的单链表,也是算法题目中使用最多的一个链表的形式,还有最复杂的带头结点的双向循环链表,插入删除同样是找到前一个节点,只是找的方式更多种多样了,并且要注意删除后的处理。
链表经典题目:删除、合并、逆置、排序、两个链表找公共节点、快慢指针问题、链表带环问题
删除的题目我们要用到前后指针,用一个指针来扫描整个链表,若遇到条件符合的对象则用到前面的那个指针直接删除此节点即可,有时要删除最大值或者最小值,刚开始扫描到了符合条件的元素不能删除要将其保留,直到扫描完所有的数据确定下来才能进行删除操作。
合并的题目与顺序表中的合并类似而且更简单。
逆置的题目考到的频率比较高,我们可以借用头插的思想,用一个指针依次扫描整个链表,并不断重新进行头插,重新创建结点或者使用原先的节点都可以,删除的时候一定要用另一个指针来指向要移动或者删除的节点,不容易混乱。还有一种思路是直接拨动结点的指针,这要用到三个指针,两个用来指方向,另外一个用来调换方向。
排序可以将所有的数据放到数组中然后qsort排序后再依次放进去。
两个链表找公共节点,有了公共节点后面的所有节点两个链表都是公用的而且是一起结束的,因此我们可以先各自求出两个链表的长度然后让长的先走,两个再一起走。
快慢指针问题,找倒数第k个节点、找中间节点问题。
链表带环问题:让一个指针一次走一步,一个指针一次走两步,若可以遇上则带环,假设慢指针进环的时候,快指针和慢指针相差n个节点,快指针去追赶慢指针,每走一次都会-1,n-1、n-2、n-3最后到0,因此有环一定能追上(但让一个指针一次走一步,一个指针一次走三步就不一定了,看n的奇偶性,可能减不到0)。假设慢指针进环的时候,快指针若与慢指针相差整个环(整个环有n个节点,要走n-1步才能到慢指针那里),两者相差n-1,每走一步就会-1,但是-n就会为0因此最差的情况是慢指针走了n次,也就是一圈,慢指针一定不会超过一圈。我们如何找入环的第一个节点?假设头结点-》入环节点的距离是L,一圈走R步,环节点到相遇点的距离是X,则快指针走了(L+nR+X),慢指针走了(L+X)步,因为两者走的时间相同,速度是2倍,则路程一定是二倍关系,推出L=nR-X,因此让一个节点从相遇点开始走,一个节点从头结点开始走,相遇的时候就是入环的节点。