小猪的数据结构辅助教程——2.4 线性表中的循环链表

小猪的数据结构辅助教程——2.4 线性表中的循环链表

标签(空格分隔): 数据结构


本节学习路线图与学习要点

学习要点

  • 1.了解单链表存在怎样的缺点,暴露出来的问题
  • 2.知道什么是循环单链表,掌握单链表的特点以及存储结构
  • 3.掌握循环链表的一些基本操作的实现逻辑,最好能手撕代码

1.循环单链表的引入


2.循环链表的特点以及存储结构

循环链表的特点:

上面也说了,比单链表稍微高比格一点的地方就是:
链表最后一个结点的指针域指向了头结点而已,这样形成所谓的环,就是循环单链表了,呵呵!
特点的话有:
我们之前判断单链表是否为空:head -> next 是否为NULL即可
而循环单链表只需:head -> next 是否为head(自身)即可
相关的操作和单链表还是比较相似的!

存储结构:(和单链表一样~)

typedef int ElemType;
typedef int Status;  
typedef struct LNode
{
	ElemType data;         //数据域
	struct LNode *next;   //指针域 
}LNode;  
typedef struct LNode *LinkList;

单链表的结构图

如上图,加入我们的链表很长的话,从表头找到表尾是很费时的,所以循环链表往往是设置尾指针
而不是设置头指针!尾指针尾指针尾指针


3.相关基本操作的代码实现

1)构造空表

Status InitList(LinkList L)
{
	L = (LinkList)malloc(sizeof(LNode));
	if(!L)exit(ERROR);
	L ->next = L;   //自己指自己~(头节点指针域指向头结点) 
	return OK;
}

2)将表置空

void ClearList(LinkList L)
{
	LinkList p,q;
	L = L ->next;     //指向头结点 
	p = L ->next;    //指向第一个结点 
	while(p!=L)
	{
		q = p ->next;
		free(p);
		p = q;
	}
	L ->next = L; //自己指自己 
} 

3)判断是否为空表

有头节点的哦~

Status ListEmpty(LinkList L)
{
	return L!=L ->next?FALSE:TRUE; 
}

4)销毁表

void DestoryList(LinkList L) 
{
	ClearList(L);  //将表置空
	free(L);    //释放头节点 
	L = NULL; 
}

5)获得表长度

int ListLength(LinkList L)
{
	int i = 0;
	LinkList p = L ->next;  //指向头结点 
	while(p != L)
	{
		i++;
		p = p ->next;
	} 
	return i;
} 

6)获得表中第i个元素的值

Status GetElem(LinkList L,int i,ElemType *e)
{
	int j = 1;
	LinkList p = L ->next ->next;	//指向第一个结点
	if(i <= 0||i >ListLength)return ERROR;  //判断插入位置是否合法 
	while(j < i)
	{
		j++;
		p = p ->next;
	} 
	e = p ->data;
	return OK;
} 

7)查找表中是否存在满足条件的元素

int LocateElem(LinkList L,ElemType e,Status(*compare)(ElemType,ElemType)) 
{
	int i = 0;
	LinkList p = L ->next ->next;  //指向第一个结点
	while(p != L ->next)
	{
		i++;
		if(compare(p->data,e))return i;
		p = p ->next; 
	} 
	return 0;   //找不到,返回0 
}

8)获得某个节点的直接前驱

Status BeforeElem(LinkList L,ElemType choose,ElemType *before)
{
	LinkList q,p = L ->next ->next;  //指向第一个结点
	q = p ->next;  
	while(q != L ->next)
	{		
		if(q ->data == choose)
		{
			before = p ->data;
			return OK;
		}
		p = q; //继续后移
		q = q ->next; 
	}
	return ERROR;  	
}

9)获得某个节点的直接后继

Status NextElem(LinkList L,ElemType choose,ElemType *behind)
{
	LinkList p = L ->next ->next;  //指向第一个结点
	while(p != L)
	{
		if(p ->data == choose)
		{
			behind = p ->next ->data;
			return OK;
		}
	}
}

10)往第i个位置插入元素

Status ListInsert(LinkList L,int i,ElemType e)
{
	LinkList s,p = L ->next;
	int j = 0;
	if(i <= 0 || i > ListLength(L) + 1)return ERROR;  //判断插入位置是否合法
	//寻找插入结点的前一个结点 
	while(j < i - 1) 
	{
		j++;
		p = p ->next;	
	}
	//生成新结点
	s = (LinkList)malloc(sizeof(LNode)); 
	s ->data = e; //将e赋值给新结点
	s ->next = p ->next; //新结点指向原来的第i个结点 
	p ->next = s;   //原i - 1个结点指向新结点 
	//假如插入的位置是表尾,把新的表尾地址给尾指针 
	if(p == L)
	{
		L = s;
	}
	return OK;
} 

步骤解析

普通的插入和单链表都是大同小异,普通结点插入的流程:

而特殊情况就是,假如插入位置是尾结点的话,那么还需要让尾指针指向这个新插入的尾结点
就是上面的:L = s;

11)删除表中第i个元素

Status ListDelete(LinkList L,int i,ElemType *e)
{
	LinkList s,p = L ->next;
	int j = 0;
	if(i <= 0||i > ListLength(L))return ERROR;//判断删除位置是否合法 
	//寻找插入结点的前一个结点 
	while(j < i - 1) 
	{
		j++;
		p = p ->next;
	}
	s = p ->next;
	p ->next = s ->next; 
	e = s ->data;
	if(s == L)
	L = p;
	free(q);  //释放结点
	return OK; 
}

步骤解析

和插入一样,删除完后,还要考虑尾指针指向的位置

假如删除的是尾结点的话,那么还要让**L = p;**指向删除结点的前一个节点~

12)遍历循环链表里的所有元素

void ListTraverser(LinkList L,void(*visit)(ElemType))
{
	LinkList p = L ->next ->next;   //指向第一个结点 
    while(p != L ->next)
    {
        visit(p ->data);
        p = p ->next;
    }
    printf("\n");
}

4.本节代码下载:

https://github.com/coder-pig/Data-structure-auxiliary-tutorial/blob/master/List/list4.c

  • 7
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值