循环链表
特点
- 表中最后一个节点指针域指向头结点
- 从表中任一结点出发均可找到其他节点
- 循环终止判断条件:p != L 或 p->next != L
- 可增设一尾指针,将访问最后一个元素的时间复杂度由o(n)缩小到o(1)
具体实现
相对单链表,在最后一个元素增加指向头节点指针
初始化
- 头结点指向自身
void InitList(CLinkList& CL){
CL = (CLNode*)malloc(sizeof(CLNode));
if(CL == NULL)
return;
CL->next = CL; //头结点指向自身
}
插入
尾端插入时,仍为将待插入temp->next指针指向p->next(即CL),因而仍与单链表相同
void Insert(CLinkList& CL,int i,int e){
CLNode* temp = (CLNode*)malloc(sizeof(CLNode));
temp->data = e;
CLNode* p = CL;
for(int j = 1;j < i;j++){
p = p->next;
}
temp->next = p->next; //包含尾端插入
p->next = temp;
}
删除
与单链表类似
void Delete(CLinkList& CL,int i){
if(i < 1 || i > CListLen(CL))
return;
CLNode* p = CL;
for(int j = 1;j < i;j++){
p = p->next;
}
CLNode* q = p->next;
p->next = q->next;
free(q);
}
前插法
第一个插入元素,即链表最后一个元素指向CL
void CreateList_H(CLinkList& CL,int n){ //在头结点与首元结点间插入
CLNode* temp;
for(int i = 1;i <= n;i++){
temp = (CLNode*)malloc(sizeof(CLNode));
temp->data = i;
temp->next = CL->next;
CL->next = temp;
}
}
后插法
void CreateList_R(CLinkList& CL,int n){ //使用尾指针
CLNode* r = CL;
CLNode* temp;
for(int i = 1;i <= n;i++){
temp = (CLNode*)malloc(sizeof(CLNode));
temp->data = i;
temp->next = CL;
r->next = temp;
r = temp; //更新尾指针
}
}
合并链表
- 遍历得到尾指针
- 第一个表尾指针指向第二个表首元结点
- 第二个表尾指针指向第一个表头指针
void merge(CLinkList& CL1,CLinkList& CL2){
CLNode* r1,r2;
if(r1->next == CL1 && r2->next == CL2)
return; //均为空链表
while(r1->next != CL1){ //找到尾指针
r1 = r1->next;
}
while(r2->next != CL2){
r2 = r2->next;
}
r2->next = r1->next;
r1->next = CL2->next; //跳过第二个链表的头结点
}
补充display函数(与单链表部分相同),并check
int main(){
CLinkList CL1,CL2;
InitList(CL1);
InitList(CL2);
//前插法
CreateList_H(CL1,5);
printf("前插法创建链表1:\n");
display(CL1);
//后插法
CreateList_R(CL2,5);
printf("后插法创建链表2:\n");
display(CL2);
//合并两链表
printf("合并链表12:\n");
merge(CL1,CL2);
display(CL1);
//插入
Insert(CL1,1,0);
Insert(CL1,12,6);
printf("插入:\n");
display(CL1);
//删除
Delete(CL1,1);
Delete(CL1,11);
printf("删除:\n");
display(CL1);
}
输出如下:
前插法创建链表1:
5 4 3 2 1
后插法创建链表2:
1 2 3 4 5
合并链表12:
5 4 3 2 1 1 2 3 4 5
插入:
0 5 4 3 2 1 1 2 3 4 5 6
删除:
5 4 3 2 1 1 2 3 4 5
双向循环链表
在单链表或循环链表中,查找直接后继的时间复杂度为O(1),而查找直接前驱的时间复杂度为O(n)
由此引出双向链表
具体实现
定义
两个指针域,一个指向直接前驱,另一个指向直接后继
typedef struct DuLNode{
int data;
struct DuLNode* prior;
struct DuLNode* next;
}DLNode,*DuLinkList;
初始化
DL的直接前驱仍为DL
void InitList(DuLinkList& DL){
DL = (DuLNode*)malloc(sizeof(DuLNode));
if(DL == NULL)
return;
DL->next = DL; //头结点指向自身
DL->prior = DL;
}
前插法创建链表
头结点与首元结点间插入
包含空链表插入首个元素情况
一定要注意指针更改的顺序,DL->next最后更改
vvoid CreateList_H(DuLinkList DL,int n){ //在头结点与首元结点间插入
DuLNode* temp;
for(int i = 1;i <= n;i++){
//printf("%d\n",i);
temp = (DuLNode*)malloc(sizeof(DuLNode));
temp->data = i;
temp->next = DL->next;
temp->prior = DL;
DL->next->prior = temp; //空链表插入第一个元素 为 DL->prior = temp;
DL->next = temp; //DL->next最后更改
}
}
后插法
void CreateList_R(DuLinkList DL,int n){ //使用尾指针
DuLNode* r = DL;
DuLNode* temp;
for(int i = 1;i <= n;i++){
temp = (DuLNode*)malloc(sizeof(DuLNode));
temp->data = i;
//next
temp->next = DL; //DL = r->next
r->next = temp;
//prior
temp->prior = r;
DL->prior = temp;
r = temp; //更新尾指针
}
}
插入
需要修改4个指针
void Insert(DuLinkList& DL,int i,int e){
DuLNode* temp = (DuLNode*)malloc(sizeof(DuLNode));
temp->data = e;
DuLNode* p = DL;
for(int j = 1;j < i;j++){
p = p->next; //找到第i-1个元素
}
temp->next = p->next;
temp->prior = p;
p->next->prior = temp; //包含尾端插入情况
p->next = temp;
}
注意:双向循环链表中,可访问直接前驱,因而无需严格在第i-1个元素后进行插入。
若在第i个元素前插入,则在找到第i个元素后(不同),将上述代码对应更改为
void Insert(DuLinkList& DL,int i,int e){
DuLNode* temp = (DuLNode*)malloc(sizeof(DuLNode));
temp->data = e;
DuLNode* p = DL;
for(int j = 0;j < i;j++){
p = p->next; //找到第i个元素
}
temp->next = p;
p->prior->next = temp;
//prior
temp->prior = p->prior;
p->prior = temp; //包含尾端插入情况
}
删除
void Delete(DuLinkList& DL,int i){
if(i < 1 || i > DuListLen(DL))
return;
DuLNode* p = DL;
for(int j = 1;j < i;j++){
p = p->next; //找到第i-1个元素
}
DLNode* q = p->next;
p->next = q->next;
q->next->prior = p;
free(q);
}
类似地
void Delete(DuLinkList& DL,int i){
if(i < 1 || i > DuListLen(DL))
return;
DuLNode* p = DL;
for(int j = 0;j < i;j++){
p = p->next; //找到第i个元素
}
p->prior->next = p->next;
p->next->prior = p->prior;
free(p);
}
合并链表
相比循环链表,双向循环链表访问可直接访问尾指针
void merge(DuLinkList DL1,DuLinkList DL2){
DLNode* r1 = DL1->prior;
DLNode* r2 = DL2->prior;
DL1->prior = r2;
r2->next = DL1;
r1->next = DL2->next;
DL2->next->prior = r1;
}
CHECK
int main(){
DuLinkList DL,DL1;
InitList(DL);
InitList(DL1);
CreateList_H(DL,5);
printf("前插法创建链表1:\n");
desplay(DL);
//后插法
CreateList_R(DL1,5);
printf("后插法创建链表2:\n");
desplay(DL1);
//合并两链表
printf("合并链表12:\n");
merge(DL,DL1);
desplay(DL);
//插入
Insert(DL,1,0);
Insert(DL,12,6);
printf("插入:\n");
desplay(DL);
//删除
Delete(DL,1);
Delete(DL,11);
printf("删除:\n");
desplay(DL);
}
输出如下
前插法创建链表1:
5 4 3 2 1
后插法创建链表2:
1 2 3 4 5
合并链表12:
5 4 3 2 1 1 2 3 4 5
插入:
0 5 4 3 2 1 1 2 3 4 5 6
删除:
5 4 3 2 1 1 2 3 4 5