循环链表

一、解析

将单链表中终端结点的指针端由空指针改为指向头结点,就使整个链表形成一个环,这种头尾相接的单链表成为单循环链表,简称循环链表(circular linked list)。

循环链表解决了一个很麻烦的问题,就是如何从当中任意一个结点除非,访问到链表的全部结点。

为了使空链表与非空链表处理一致,我们通常设一个头结点,当然,这并不是说,循环链表一定要头结点。

其实循环链表和单链表的主要差异就在于循环的判断条件上,原来是判断p->next是否为空,现在则是p->next不等于头结点。


对于非空的循环链表就如下图所示:


其实循环链表和单链表的主要差异就在于循环的判断上,原来是判断p->next是否为空,现在则是p->next不等于头节点,则循环没有结束。

在单链表中有了头节点时,可以用O(1)的时间访问第一个节点,对于要访问到最后一个节点需要O(n),因为需要从头节点到尾节点都扫描一遍,如何利用O(1)的时间有链表指针访问到最后一个节点?这里只需要修改一下链表结构即可,也就是不使用头指针,而是用指向终端结点的尾指针来表示,如下图所示:


从上图可以看到,终端结点用尾指针rear指示,则查找终端结点的复杂度是O(1),而开始节点其实就是rear->next->next,时间复杂度也是O(1)

尾指针的存在对于两个链表合并的时候非常实用,如下例子所示:

有两个循环链表,它们的尾指针分表是rearArearB,如图所示:


想要合并这两个链表只需如下操作即可:


P=rearA->next;//保存A表的头节点,即

rearA->next=rearB->next->next;//将本是指向B表的第一个节点(不是头节点)赋值给rearA->next,

rearB->next=p;//将原A表的头节点赋值给rearB->next,

free(p);//释放p

 

二、循环链表初始化

初始化算法:

1. 分配一个节点的内存

2. 指针域指向头结点

三、元素插入

L的第i个位置之前插入元素e算法:

    1. p指向头结点

2.检测位置i是否在链表长度之外

3. 寻找第i-1个结点

4. 生成新结点

5.将元素e插入L

6. 改变尾结点指针

 

四、元素读取

获取链表中第i个元素算法:

1. p指向第一个结点

2. 顺指针向后查找,直到p指向第i个元素

3. 取第i个元素

五、获取元素e的前驱

获取元素e的前驱算法:

1. p指向第一个结点,q=p->next

2. 循环:p没到表尾

如果当前元素值是元素e则返回该元素前驱p->data;否则p=q;q=q->next;

 

六、获取元素e的后继

算法:

1. p指向第一个结点

2. 循环:p没到表尾

如果p->data是元素e则返回p->next->data;否则p=p->next继续循环

 

七、删除L的第i个元素

算法:

1. p指向头结点

2. 寻找第i-1个结点

3. q指向待删除结点,修改表尾指针

4. 释放待删除结点

 

C语言实现双向链表:

//list.h
struct SigClNode   
{   
    ElemType data;   
    struct SigClNode *next;   
}SigClNode; 
typedef struct SigClNode *SigClList;//定义SigList

/* 操作结果:构造一个空的线性表L */
int SigClListInit(SigClList *L);
/* 操作结果:销毁线性表L */
int SigClListDestroy(SigClList *L);
/* 初始条件:线性表L已存在。操作结果:将L重置为空表 */
int SigClListClear(SigClList *L);
 /* 初始条件:线性表L已存在。
 操作结果:若L为空表,则返回OK,否则返回ERROR */
int SigClListEmpty(SigClList L);
/* 初始条件:L已存在。操作结果:返回L中数据元素个数 */
int SigClListLength(SigClList L);
/* 当第i个元素存在时,其值赋给e并返回OK,否则返回ERROR */
int SigClListGetElem(SigClList L,int i,ElemType *e);
/* 初始条件:线性表L已存在,SigClListCompare()是数据元素判定函数 */
/* 操作结果:返回L中第1个与e满足关系SigClListCompare()的数据元素的位序。 */
/*           若这样的数据元素不存在,则返回值为0 */
int SigClListLocateElem(SigClList L,ElemType e,int(*SigClListCompare)(ElemType,ElemType));
/* 初始条件:线性表L已存在 */
/* 操作结果:若cur_e是L的数据元素,且不是第一个,则用pre_e返回它的前驱, */
/*           否则操作失败,pre_e无定义 */
int SigClListPriorElem(SigClList L,ElemType cur_e,ElemType *pre_e);
/* 初始条件:线性表L已存在 */
/* 操作结果:若cur_e是L的数据元素,且不是最后一个,则用next_e返回它的后继, */
/*           否则操作失败,next_e无定义 */
int SigClListNextElem(SigClList L,ElemType cur_e,ElemType *next_e);
/* 在L的第i个位置之前插入元素e */
int SigClListInsert(SigClList *L,int i,ElemType e);
/* 删除L的第i个元素,并由e返回其值 */
int SigClListDelete(SigClList *L,int i,ElemType *e);
int SigClListTraverse(SigClList L,void(*vi)(ElemType)); 
int SigClListCompare(ElemType c1,ElemType c2);
void SigClListVisit(ElemType c);

//list.cpp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "list.h"


/* 操作结果:构造一个空的线性表L */
int SigClListInit(SigClList *L)
{ 
	/* 产生头结点,并使L指向此头结点 */
	*L=(SigClList)malloc(sizeof(struct SigClNode)); 
	/* 存储分配失败 */
	if(!*L) 
	{
		return ERROR;
	}
	/* 指针域指向头结点 */
	(*L)->next=*L; 
	return OK;
}

/* 操作结果:销毁线性表L */
int SigClListDestroy(SigClList *L)
{ 
	/* p指向头结点 */
	SigClList q,p=(*L)->next; 

	/* 没到表尾 */
	while(p!=*L) 
	{
		q=p->next;
		free(p);
		p=q;
	}
	free(*L);
	*L=NULL;
	return OK;
}


/* 初始条件:线性表L已存在。操作结果:将L重置为空表 */
int SigClListClear(SigClList *L) /* 改变L */
{ 
	SigClList p,q;
	*L=(*L)->next; // L指向头结点 
	p=(*L)->next; // p指向第一个结点
	while(p!=*L) // 没到表尾 
	{
		q=p->next;
		free(p);
		p=q;
	}
	(*L)->next=*L; // 头结点指针域指向自身 
	return OK;
}


 /* 初始条件:线性表L已存在。
 操作结果:若L为空表,则返回OK,否则返回ERROR */
int SigClListEmpty(SigClList L)
{ 
	if(L->next==L) // 空 
	{
		return OK;
	}
	else
	{
		return ERROR;
	}
}


/* 初始条件:L已存在。操作结果:返回L中数据元素个数 */
int SigClListLength(SigClList L)
{ 
	int i=0;
	SigClList p=L->next; // p指向头结点 
	while(p!=L) // 没到表尾 
	{
		i++;
		p=p->next;
	}
	return i;
}


/* 当第i个元素存在时,其值赋给e并返回OK,否则返回ERROR */
int SigClListGetElem(SigClList L,int i,ElemType *e)
{ 
	int j=1; /* 初始化,j为计数器 */
	SigClList p=L->next->next; /* p指向第一个结点 */
	if(i<=0||i>SigClListLength(L)) /* 第i个元素不存在 */
	{
		return ERROR;
	}
	while(j<i)
	{ /* 顺指针向后查找,直到p指向第i个元素 */
		p=p->next;
		j++;
	}
	*e=p->data; /* 取第i个元素 */
	return OK;
}


/* 初始条件:线性表L已存在,SigClListCompare()是数据元素判定函数 */
/* 操作结果:返回L中第1个与e满足关系SigClListCompare()的数据元素的位序。 */
/*           若这样的数据元素不存在,则返回值为0 */
int SigClListLocateElem(SigClList L,ElemType e,int(*SigClListCompare)(ElemType,ElemType))
{ 
	int i=0;
	SigClList p=L->next->next; //p指向第一个结点 
	while(p!=L->next)
	{
		i++;
		if(SigClListCompare(p->data,e)) // 满足关系  
		return i;
		p=p->next;
	}
	return 0;
}

/* 初始条件:线性表L已存在 */
/* 操作结果:若cur_e是L的数据元素,且不是第一个,则用pre_e返回它的前驱, */
/*           否则操作失败,pre_e无定义 */
int SigClListPriorElem(SigClList L,ElemType cur_e,ElemType *pre_e)
{ 
	SigClList q,p=L->next->next; // p指向第一个结点 
	q=p->next;
	while(q!=L->next) // p没到表尾 
	{
		if(q->data==cur_e)
		{
			*pre_e=p->data;
			return OK;
		}
		p=q;
		q=q->next;
	}
	return ERROR;
}


/* 初始条件:线性表L已存在 */
/* 操作结果:若cur_e是L的数据元素,且不是最后一个,则用next_e返回它的后继, */
/*           否则操作失败,next_e无定义 */
int SigClListNextElem(SigClList L,ElemType cur_e,ElemType *next_e)
{ 
	SigClList p=L->next->next; // p指向第一个结点

	/* p没到表尾 */  
	while(p!=L) 
	{
		if(p->data==cur_e)
		{
			*next_e=p->next->data;
			return OK;
		}
		p=p->next;
	}
	return ERROR;
}


/* 在L的第i个位置之前插入元素e */
int SigClListInsert(SigClList *L,int i,ElemType e) /* 改变L */
{ 
	SigClList p=(*L)->next,s; // p指向头结点 
	int j=0;

	/* 无法在第i个元素之前插入 */
	if(i<=0||i>SigClListLength(*L)+1) 
	{
		return ERROR;
	}

	/* 寻找第i-1个结点 */
	while(j<i-1) 
	{
	p=p->next;
	j++;
	}
	s=(SigClList)malloc(sizeof(struct SigClNode)); //生成新结点 
	s->data=e; // 插入L中 
	s->next=p->next;
	p->next=s;
	if(p==*L) // 改变尾结点 
	{
		*L=s;
	}
	return OK;
}

/* 删除L的第i个元素,并由e返回其值 */
int SigClListDelete(SigClList *L,int i,ElemType *e) /* 改变L */
{ 
	SigClList p=(*L)->next,q; // p指向头结点 
	int j=0;

	/* 第i个元素不存在 */
	if(i<=0||i>SigClListLength(*L)) 
	{
		return ERROR;
	}

	/* 寻找第i-1个结点 */
	while(j<i-1) 
	{
		p=p->next;
		j++;
	}
	q=p->next; // q指向待删除结点 
	p->next=q->next;
	*e=q->data;

	/* 删除的是表尾元素 */
	if(*L==q) 
	{
		*L=p;
	}
	free(q); // 释放待删除结点 
	return OK;
}

int SigClListTraverse(SigClList L,void(*vi)(ElemType))
{ 
	SigClList p=L->next->next;
	while(p!=L->next)
	{
		vi(p->data);
		p=p->next;
	}
	printf("\n");
	return OK;
}

 
int SigClListCompare(ElemType c1,ElemType c2)
{
	if(c1==c2)
	{
		return OK;
	}
	else
	{
		return ERROR;
	}
}
 
void SigClListVisit(ElemType c)
{
	printf("[%s %d]%d ",__FUNCTION__,__LINE__,c);
}
int main()
{
	SigClList L;
	ElemType e;
	int j = 0;
	int i = 0;
	
	i=SigClListInit(&L); /* 初始化单循环链表L */
	printf("[%s %d]初始化单循环链表L i=%d (1:初始化成功)\n",__FUNCTION__,__LINE__,i);
	
	i=SigClListEmpty(L);
	printf("[%s %d]L是否空 i=%d(1:空 -1:否)\n",__FUNCTION__,__LINE__,i);
	
	SigClListInsert(&L,1,3); /* 在L中依次插入3,5 */
	SigClListInsert(&L,2,5);
	
	i=SigClListGetElem(L,1,&e);
	j=SigClListLength(L);
	printf("[%s %d]L中数据元素个数=%d,第1个元素的值为%d。\n",__FUNCTION__,__LINE__,j,e);
	printf("[%s %d]L中的数据元素依次为:",__FUNCTION__,__LINE__);
	
	SigClListTraverse(L,SigClListVisit);
	SigClListPriorElem(L,5,&e); // 求元素5的前驱 
	printf("[%s %d]5前面的元素的值为%d。\n",__FUNCTION__,__LINE__,e);
	SigClListNextElem(L,3,&e); // 求元素3的后继
	printf("[%s %d]3后面的元素的值为%d。\n",__FUNCTION__,__LINE__,e);
	
	printf("[%s %d]L是否空 %d(1:空 -1:否)\n",__FUNCTION__,__LINE__,SigClListEmpty(L));
	
	j=SigClListLocateElem(L,5,SigClListCompare);
	if(j)
	{
		printf("[%s %d]L的第%d个元素为5。\n",__FUNCTION__,__LINE__,j);
	}
	else
	{
		printf("[%s %d]不存在值为5的元素\n",__FUNCTION__,__LINE__);
	}
	
	i=SigClListDelete(&L,2,&e);
	printf("[%s %d]删除L的第2个元素:\n",__FUNCTION__,__LINE__);
	if(i)
	{
		printf("[%s %d]删除的元素值为%d,现在L中的数据元素依次为:",__FUNCTION__,__LINE__,e);
		SigClListTraverse(L,SigClListVisit);
	}
	else
	{
		printf("[%s %d]删除不成功!\n",__FUNCTION__,__LINE__);
	}
	
	printf("[%s %d]清空L:%d(1: 成功)\n",__FUNCTION__,__LINE__,SigClListClear(&L));
	printf("[%s %d]清空L后,L是否空:%d(1:空 -1:否)\n",__FUNCTION__,__LINE__,SigClListEmpty(L));
	printf("[%s %d]销毁L:%d(1: 成功)\n",__FUNCTION__,__LINE__,SigClListDestroy(&L));
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值