循环链表
定义
一个首尾相接的链表,单链表最后结点的指针由NULL改为指向头结点或者第一节点。
连接两循环链表
两个带有头结点的循环链表LA,LB,将两个循环单链表合并成一个循环链表,头指针为LA
LinkList merge_1(LinkList LA,LinkList LB)
{ Node *p,*q;
p=LA;
q=LB;
while(p->next!=LA){
p=p->next; //找到LA表尾
}
while(q->next!=LB){
q=q->next; //找到LB表尾
}
q->next=LA; //LB表尾指向LA表头
p->next=LB->next; //LB表尾指向LB第一节点
free(LB); //释放LB的头结点
return (LA);
}
将第一个链表的尾鱼第二个表的第一结点连起来,让第二个表的尾指向第一个表的头结点。
主体解读:分别找到表尾后,将LB的表尾指向LA的头结点(组合循环链表还是要尾连首),将LA的表尾指向LB的第一结点(去掉LB的头结点)。
最后释放LB的头结点。
双向链表
定义
每个结点都有指向前和指向后两个指针
声明
typedef struct DNode{
ElemType data;
struct DNode *prior;//指向前驱
struct DNode *next;//指向后继
}*DoubleList,DNode;
和单链表基本一致。
前插操作
在双向链表的第i个位置前插入值为e的结点
void DlinkListIns(DoubleList L,int i,ElemType e){ //在双向链表的第i个位置前插入值为e的结点
DNode *s,*p; //一个申请,一个定位
int k=0;
p=L;
while(p!=NULL&&k<i) //因为双向,所以直接定位在第i个位置
{ p=p->next;
k++;
}
if(p==NULL) //由于找尾巴出问题
return FALSE; //位置不合法
s=(*DNode)malloc(sizeof(DNode));
if(s)
{ s->data=e;
s->prior=p->prior; //新结点s获取插入位的前驱
p->prior->next=s; //插入位p前一个结点的后继指向新结点s
s->next=p; //s的后继为插入位
p->prior=s; //插入位的前驱指向新结点s
return TRUE; //成功
}
else
return FALSE; //失败
}
主体解读:由于是在双向链表中操作,因此不需要像单链表一样定位在i的前一个结点,直接让定位指针定位到i上。 之后就是分别将p的前驱赋给s的前驱,p上一个结点的后继指向s,s后继指向p,p前驱指向s。
删除操作
删除第i个结点并将该结点值保存在e中
int DlinkDel(DoubleList L,int i,ElemType *e){
DNode *p;
int k;
p=L;
while(p!=NULL&&k<i)
{ p=p->next;
k++;
}
if(p==NULL)
{ return FALSE;
}
*e=p->data;
p->prior->next=p->next;
p->next->prior=p->prior;
free(p)
return TRUE;
}
主体解读:p前驱的后继连到p的后继,p后继的前驱连到p的前驱。
静态链表
仅在C语言情况下能使用,其实就是用数组模拟链表
声明
- cursor域为游标指示器,指示后继结点在数组中的数组下标
- 数组首元为表的头结点,其cursor域指示了表中第一个结点的位置
- 以cursor值为0表示静态单链表的结束。
typedef struct{
ElemType data;
int cursor;
}Component,StaticList[MaxSize];
每个结点的结构是由一个数据域和一个游标组成,并且按这个结构声明了一个名为“StaticList”的数组。
创建静态链表
指将静态链表初始化为一个备用的静态单链表
整个表分两部分,最初已用空间是空,备用位置后面都空闲可用
void Creat(StaticList space,int *av) //av是空闲可用位置
{ int k;
space[0]->cursor=-1; //下标为0储存的游标为-1(位置无用)
for(k=1;k<MaxSize-1;k++)
{ space[k]->cursor=k+1;
}
space[MaxSize-1]=-1; //回到最初space[0]
*av=1; //提供可用下标位置
}
主体解读:给下标为0位置的游标设置-1,通过循环,从下标1到倒数第二个,分别赋游标以连接成指向下一个结点的状态,最后一个结点游标指回最初-1,并将空闲可使用的位置保存。
申请新结点
不同于顺序表和链表,申请新结点本质上是在被用表中减少一个结点。
给我一个av所指的结点,返回这个结点的下标
int Getnode(StaticList space,int *av)
{ int i;
i=*av; //获取av所指的结点
*av=space[*av].cur; //av后移一步
return i;
}
主体解读:用一个变量获取*av指向的新结点,返回这个结点并让av后移一位
回收结点
本质是备用链表中增加一个结点
void Freenode(StaticList space,int *av,int k){ //回收序号为k的结点
space[k].cursor=*av;
*av=k;
}
主体解读:回收序号为k的头结点,先将需要回收的结点游标指向av(其实就是备用表头指针),再让av指向k,这样原来k的地方就被av指着(回收完成),并且原来av指的地方就变成了备用链表的第二个结点
摘自: 耿国华老师 https://www.bilibili.com/video/BV1kx411h7pF?p=20