循环链表/双向循环链表 C语言实现

循环链表

特点

  • 表中最后一个节点指针域指向头结点
  • 从表中任一结点出发均可找到其他节点
  • 循环终止判断条件: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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值