循环链表的相关操作

一、定义

    循环链表是另一种形式的链式存储结构。它的特点是表中最后一个节点的指针域指向头结点,整个链表形成一个环

二、循环链表的数据结构

    循环链表的数据结构和单链表相同,分为数据域和指针域,指针域指向当前节点的下一节点,代码如下:

typedef struct Node
{
	int data;
	struct Node *next;
}Node,*LinkList;

三、循环链表的相关操作

   在进行循环链表相关操作前必须先清楚,在操作循环链表过程中,什么时候使用一级指针,什么时候使用二级指针:需要从头结点开始进行的操作需要以二级指针为参数,否则用一级指着即可。

    用二级指针来代表整个链表,在遍历、插入等需要从头结点开始的操作中,参数用能代表整个链表的二级指针,只需要进行一次解引用即可得到头结点的地址。

     可以类比二维数组来理解,假设存在int arr[3][4] 和 Node **p(由上文可知Node **p 和 LinkList *p中p意义相同)

 无解引用

一次解引用

           两次解引用 

二维数组

arr

*arr

**arr

整个数组首地址第一行地址第一个元素值
循环链表

*p

*p

**p或(*p)->

整个链表首地址头节点地址头节点指针域(注意4)

注意:

1、二维数组arr并不是二级指针,这一点并不同于p

2、*和->都有解引用的意思

3、头结点一般没有值,因此只有头结点的指针域有意义

1、初始化

bool InitList(LinkList *List)
{
        /*通过对代表整个个链表的二级指针List进行一次解引用,进入代表的头节点的(*List),可以改变代表头节点的指向(即指向申请的内存空间)*/
	*List = (LinkList)malloc(sizeof(Node));
	if((*List) == NULL)
	{
		printf("申请内存失败\n");
		exit(0);
	}
        /*通过一次解引用进入头节点,再通过一次解引用(->)改变头节点的值(指针域)*/
	(*List)->next = *List;
	return true;
}

2、判断是否为空

   判断链表是否为空并不需要修改指针,因此参数只需要给出某一节点的地址,表示从该节点开始判断,如果该节点的指针域指向自己,即为空

void ListEmpty(LinkList List)
{
	if(List->next == List)//该节点的指针域指向自己
	{
		printf("循环链表为空!\n");
	}
	else
	{
		printf("循环链表为非空!\n");
	}
}

3、插入

    在第i个节点前插入元素e,在插入前需要先找到第i-1个节点,因此需要设置计数器,从头节点开始遍历,知道第i-1个节点,然后将建立新的节点插在第i-1个节点后面。

     因为要从头结点开始计数,因此需要知道头结点地址,故参数为二级指针L,对L一次解引用即可得到头结点的地址 

bool ListInsert(LinkList *L, int i, int e)
{
	//在第i个元素前插入元素e
	LinkList p = (*L)->next; //p指向头结点
	LinkList s;
	int j = 0;
	if(i <= 0 || i > ListLength(*L) + 1)//i值不合法
	{
		return false;
	}
	while(j < i-1)//寻找第i个节点
	{
		p = p->next;
		j++;
	}
	s = (LinkList)malloc(sizeof(Node));
	s->data = e;
	s->next = p->next;
	p->next = s;
	if(p == *L)//头结点改变
	{
		*L = s;
	}
	return true;
}

4、求表长

   求表长并不需要从头结点开始,从任意节点都可以,从某一节点遍历,直到便利到某一节点的指针域指向了该节点,即到达表尾,在遍历过程中设置计数器记录结点个数即可

int ListLength(LinkList L)
{
	int i = 0;
	LinkList p = L->next;
	while(p != L)//没到表尾
	{
		i++;
		p = p->next;
	}
	return i;
}

5、查找

     查找是指找出从头开始第i个节点中的值,需从头结点开始,使用二级指针作为参数,首先判断i是否大于该表的长度或小于等于0,i有意义时:从该节点节点开始遍历查找第i个节点,找到后赋值给*e,

bool GetElem(LinkList *L,int i,int *e)
{
	//当第i个元素存在时,赋值给e
	int j = 1;//计数器
	LinkList p = (*L)->next;//p指向第一个节点
	if(i <= 0 || i > ListLength(*L))
	{
		return false;
	}
	while(j < i)
	{
		//顺指针向后查找,知道第i个元素
		p = p->next;
		j++;
	}
	*e = p->data;
	return true;
}

6、遍历输出

遍历需要从头结点开始,因此使用二级指针

bool ListTraverse(LinkList *L)
{
	LinkList p = (*L)->next;//p指向头结点后第一个节点
	while(p != *L)//当p不等于头节点时循环输出
	{
		printf("%d ",p->data);
		p = p->next;
	}
	printf("\n");
	return true;
}

7、求元素前驱

      求元素前驱,即为找出某个元素所在节点的前一个节点中元素值,定义两个节点牌,q,p,p始终指向q,因此在遍历查找过程中,只要q的元素值为要查找的值,那么p中的元素值即为所求。   

bool PriorElem(LinkList *L,int cur_e,int *pre_e)
{
	LinkList q;
	LinkList p = (*L)->next;//p指向第一个节点
	q = p->next;//p指向q
	while(q != *L)
	{
		if(q->data == cur_e)
		{
			*pre_e = p->data;
			return true;
		}
		p = q;
		q = q->next;
	}
	return  false;
}

8、求元素后继

求元素后继只需从头结点开始遍历,直到便利到某一节点的值为所求值,则该节点的后继(即该节点指针域所指的节点)即为所求

bool NextElem(LinkList *L,int cur_e,int *next_e)
{
	LinkList p = (*L)->next;//p指向第一个节点
	while(p != *L)//p没有到表尾
	{
		if(p->data == cur_e)
		{ 
			*next_e = p->next->data;
			return true;
		}
		p = p->next;
	}
	return false;
}

9、删除

删除第i个节点。

1、首先判断i是否有意义。

2、利用指针p进行遍历,找到第i-1个节点

3、指针q指向第i个节点(q = p -> next)

4、删除第i个节点(p = q -> next)

bool ListDelete(LinkList *L,int i,int *e)
{
	LinkList p = (*L)->next;//p指向头结点
	LinkList q;
	int j = 0;

	if(i <= 0 || i > ListLength(*L))
	{
		return false;
	}
	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 true;
}

10、清空链表

        从头结点开始,free掉除头结点外每一个节点,最后头结点指向自己

bool ClearList(LinkList *L)
{
	//将L置为空表
	LinkList p,q;
	*p = (*L)->next;//指向头结点
	while(p != *L)
	{
		q = p->next;
		free(p);
		p = q;
	}
	(*L)->next = *L;//头结点指针域指向自身
	return true;
}

11、销毁链表

       从头结点开始向后遍历直到表尾,free掉每一节点,最后free掉头结点

bool DestoryList(LinkList *L)
{
	//销毁链表
	LinkList q,p = (*L)->next;
	while(p != *L)
	{
		q = p->next;
		free(p);
		p = q;
	}
	free(*L);
	*L = NULL;
	return true;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值