链表
一、单链表
1、定义一个struct型变量Linklist
struct Linklist{
int data;
struct Linklist *next;
} ;
2、创建单链表
(1)尾插法
思路如下:
- malloc生成一个单元head,end节点指向head
- 循环malloc生成新节点node,node数据域赋值
- end->next指向新节点node
- end指向当前最后的节点node,也就是刚加入的新节点node
- end->next赋值为NULL
Linklist *init_wei(int n){
Linklist *head,*node,*end;
head = (Linklist*)malloc(sizeof(Linklist));
end = head;
for(int i=0;i<n;i++){
node = (Linklist*)malloc(sizeof(Linklist));
//printf("输入new节点的data值:\n");
//scanf("%d",&node->data);
node->data = i;
end->next = node;
end = node;
}
end->next = NULL;
//循环链表将尾部的next指向第一个元素形成环
//end->next = head->next;
return head;
}
(2)头插法
通过头插法,可以实现单链表的逆序创建
思路如下:
- malloc生成一个单元head,end赋值为NULL
- head->next指向end,先形成一个head和end的链,然后在其中插入新node。
- malloc生成新node,node数据域赋值
- node->next指向head->next
- head->next指向新节点node
Linklist *init_tou(int n){
Linklist *head,*end,*node;
head = (Linklist*)malloc(sizeof(Linklist));
end = NULL;
head->next = end;
for(int i=0;i<n;i++){
node = (Linklist*)malloc(sizeof(Linklist));
node->data = i;
node->next = head->next;
head->next = node;
}
return head;
}
3、输出链表
因为上面创建的链表,head节点是没有数据域的,故从head->next开始判
void shuchu(Linklist *list){
Linklist *h = list;
while(h->next != NULL){
h = h->next;
printf("%d ",h->data);
}
printf("\n");
}
4、增加新节点node
给一个Linklist的链表(Linklist *list)及新节点所在位置(int n)
思路如下:
- 先到达n位置的前一个节点t,如下
while(i<n-1 && t != NULL){
t = t->next;
i++;
}
- malloc一个新节点node,node的数据域赋值
- 将新节点node和t后面的节点形成连接,node->next = t->next
- t节点再指向新node,t->next = node
此处要注意要先保住t和t后方节点的链,先把node和t后面节点连接后,再断开t和t->next的链,最后再更新t->next = node
void addNode(Linklist *list,int n){
Linklist *t = list,*node;
int i = 0;
while(i<n-1 && t != NULL){
t = t->next;
i++;
}
if(t!=NULL){
Linklist *node = (Linklist*)malloc(sizeof(Linklist));
printf("输入add节点的data值:");
scanf("%d",&node->data);
node->next = t->next;
t->next = node;
}
}
5、删除节点node
思路如下:
- 老规矩,先到达要删除节点的前一节点t,和上述addNode的操作一样
- 新建一个节点q指向t后方节点t->next,也就是要删除的节点
- t的next直接指向q的next,t->next = q->next
- 已经形成链了,free掉q即可
void deleteNode(Linklist *list,int n){
Linklist *t = list ,*q;
int i=0;
//此时t变为要删除的前一个节点,下面的q代表当前节点。
while(i<n-1 && t!=NULL){
t = t->next;
i++;
}
if(t!=NULL){
q = t->next;
t->next = q->next;
free(q);
}else{
printf("节点不存在\n");
}
}
6、修改node的数据域
传入Linklist的list、修改节点的位置。直接先while到达这个node,修改它的的数据域即可。
void *modify(Linklist *list, int n){
Linklist *t = list;
int i = 0;
//先到这个点
while(i<n && t != NULL){
t = t->next;
i++;
}
if(t != NULL){
printf("输入要修改的值!\n");
scanf("%d",&t->data);
}else{
printf("节点不存在!");
}
}
7、查询某一个node的数据域
传入Linklist的list、节点的位置。跟modify操作一样,返回数据域即可。
int queryNode(Linklist *list,int n){
Linklist *t = list;
int i = 0;
while(i<n && t!=NULL){
t = t->next;
i++;
}
if(t!=NULL){
return t->data;
}else{
printf("节点不存在\n");
}
}
二、循环链表
1、循环链表的创建
- 循环链表将尾部的next指向第一个元素形成环
- 即end->next = head->next;
此处以尾插法为例,如下:
Linklist *init_wei(int n){
Linklist *head,*node,*end;
head = (Linklist*)malloc(sizeof(Linklist));
end = head;
for(int i=0;i<n;i++){
node = (Linklist*)malloc(sizeof(Linklist));
//printf("输入new节点的data值:\n");
//scanf("%d",&node->data);
node->data = i;
end->next = node;
end = node;
}
//循环链表将尾部的next指向第一个元素形成环
end->next = head->next;
return head;
}
2、两个循环链表的合并
步骤如下:
- 先根据L1、L2的长度分别解链
- 两个单链表L1、L2合并形成一个大长链表L
- 只留一个头head,将L2的head删除
- 将L的首尾相连成循环链表,合并完毕
三、双向链表
- 双向链表解决了单链表找直接前驱的O(n)的时间复杂度。
- 它有三个域,分别是prior、data、next,结构如下:
typedef struct DListNode {
struct DListNode* prior;
struct DListNode* next;
int data;
}DListNode;
1、addNode操作
输入有头结点,插入的位置和数据,找到要插入的位置p(在p之前插入),创建新的结点s。
图示如下:
- s的next指向p,s->next = p;
- s的prior指向p的prior,s->prior = p->prior;
- p的prior的next指向s,p>prior->next = s;
- p的prior指向s,p->prior = s;
老规矩,先不要断开p->prior和p的链。先把新节点s的next和prior指好,然后在p->prior的next指向s,最后再p的prior指向s。
2、deleteNode操作
输入有头结点和位置,p为找到的要删除的结点。
图示如下:
- p的prior的next指向p的next,p->prior->next = p->next;
- p的next的prior指向p的prior,p->next->prior = p->prior;
- free掉p节点;
先吧prior的next指向p后面的节点,再把后面节点的prior的指向p前面的节点,注意顺序。
四、总结
链表的addNode和deleteNode是操作的重点。
在增加操作的时候,先保留原链,t和t后方的节点先不要断开。 即先操作完新节点后再处理t的next域的指向。
在删除操作的时候,先用一个临时的节点p指向要删除的节点t,保留原链的状态。 然后再把t前方的next域直接指向p后方节点,最后free掉p即可。