线性表【单循环链表的实现】(带头结点,带头尾指针)

1.定义:是一种头尾相接的单链表(即:表中最后一个结点的指针域指向头结点,整个链表形成一个环)

图解如下:
在这里插入图片描述

单循环链表为空表时:其头结点的next域指向它自己,即头结点next等于头指针

2.单循环链表的优劣

1.优点:

<1>节省内存空间:单循环链表的最后一个结点指向头结点,因此不需要使用额外的标志或指针来表示链表的结束位置,相对于普通单链表,节省了空间。
<2>方便链表的循环处理: 因为最后一个结点的 next 指针指向链表的头结点,任何结点都可以作为起点进行循环处理,特别适合处理需要循环操作的数据结构,比如约瑟夫环问题。
<3>易于从任意结点遍历: 在循环链表中,可以从任意结点开始遍历整个链表,这对于某些特定的算法非常方便。
<4>适合队列的环形缓冲区实现: 单循环链表的结构天然适合用来实现环形缓冲区,这在一些队列的实现中很常见。

2.缺点:

<1>不易处理链表的结束条件: 由于链表是循环的,没有明确的终止标志,某些操作(如判断链表长度)可能会复杂一些,需要额外判断条件以避免无限循环。
<2>实现和维护较复杂: 由于循环结构的特殊性,某些操作(如插入和删除)需要小心处理边界情况,代码实现和调试可能比普通单链表更复杂。
<3>不易找到尾结点: 如果没有维护一个尾指针,那么从头结点遍历到尾结点需要遍历整个链表。维护尾指针也增加了复杂性。
<4>可能造成死循环: 如果处理不当,比如误将链表设为循环或未正确处理边界条件,容易引发死循环,导致程序无法正常结束。

3.单循环链表的注意事项

由于循环链表中没有 NULL指针,故涉及遍历操作时,其终止条件就不再像非循环链表那样判断 p 或p->next 是否为空,而是判断它们是否等于头指针
在这里插入图片描述

4.带尾指针的单循环链表:

表的操作常常是在表的首尾位置上进行,为了查找操作的方便于是就有了带尾指针的单循环链表
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

5.单循环链表的整表创建及相关操作

1.单循环链表的结构定义:

与单链表的结构定义完全一致

#include<stdio.h>
#include<stdlib.h>

typedef int ElemType;

#define bool int
#define false 0
#define true 1

//1.单循环链表的结构定义不变
typedef struct LNode
{
	ElemType data;//数据域
	struct LNode* next;//指针域
}LNode, * LinkHead,* LinkTail;
//LNode *用来定义结点指针;
//LinkHead 用来定义头指针;
//LinkTail 用来定义尾指针;

2.单循环链表的初始化(带头结点,头尾指针的单循环链表)

注意:要改变主函数中头尾指针的指向,故传入二级指针

InitLinkList(LinkHead* L, LinkTail* T)

2.算法步骤:

(1) 生成新结点作头结点 并使 (头指针L)指向头结点
(2) 初始化头结点指针域指向自己
(3) 初始化尾指针指向头结点

注意与单链表初始化的区别

//2.单循环链表的初始化(仍然是初始化头结点)
//注意初始化尾指针的指向
bool InitLinkList(LinkHead* L, LinkTail* T)
{
	//[1]创建头结点
	*L = (LinkHead)malloc(sizeof(LNode));

	if (*L == NULL)
	{
		printf("内存分配失败!\n");
		return false;
	}

	(*L)->data = 0;//头结点数据域可以自行赋值,一般不进行赋值

//以下操作与单链表初始化有所不同:
	
    //[2]初始化头结点指针域指向自己
	(*L)->next = (*L);
	
	//[3]初始化尾指针指向头结点
	(*T) = (*L);
	return true;
}

3. 判断单循环链表链表是否为空

方法1: 判断头结点的next域是否指向自己

 LinkListEmpty1(LinkHead L)

方法2: 判断尾指针是否指向头结点

 LinkListEmpty2( LinkTail T)
//[方法1:判断头结点的next域是否指向自己]
bool LinkListEmpty1(LinkHead L)
{
	//[0]判断单循环链表头结点是否存在
	//若头结点不存在,也认为该表为空表
	if (L == NULL)
	{
		return true;
	}
	//[1]判断头结点的next域是否指向自己
	//若头结点的next域指向自己,则说明单循环链表为空,返回true
	if (L->next == L)
	{
		return true;
	}
    //否则说明单循环链表不为空,返回false
	else 
	{
		return false;
	}
}
//[方法2:判断尾指针是否指向头结点]
bool LinkListEmpty2( LinkTail T)
{
	//[0]判断单循环链表头结点是否存在
	//若头结点不存在,也认为该表为空表
	if (T == NULL)
	{
		return true;
	}
	//[1]判断尾指针是否指向头结点
	//若尾指针指向头结点(也就是它本身),则说明单循环链表为空,返回true
	if (T == T->next)
	{
		return true;
	}
	//否则单循环链表不为空,返回false
	else
	{
		return false;
	}
}

4.单循环链表的销毁

注意:在销毁完所有结点(包括头结点时),要重置头尾指针指向NULL,故传入二级指针

DestroyLinkList(LinkHead* L,LinkTail  *T)
1.算法思路:从头结点开始,依次释放所有结点
3.算法步骤:

(1)定义了两个临时指针p,q
//p:存储待销毁结点的地址
//q:存储下一个待销毁结点的地址
(2)初始化p指向首元结点
(3)依次销毁除头结点外的所有结点
(4)清空完单循环链表后释放头结点
(5)更新头指针和尾指针,避免悬挂指针问题

//4.单循环链表的销毁
bool DestroyLinkList(LinkHead* L,LinkTail  *T)
{
	//[0]判断单循环链表头结点是否存在
	//一个带有头尾指针的单循环链表是由头指针和尾指针组成的,所以要判断头指针和尾指针是否为空
	if (*L == NULL|| *T == NULL)
	{
		return false;
	}

//以下操作与单链表清空操作相似;但是要注意:
// 循环终止条件应为(p!=*L)
// 在销毁完毕后,要更新头尾指针,避免指针悬挂问题

	//[1]定义了两个临时指针p,q
	//p:存储待销毁结点的地址
	//q:存储下一个待销毁结点的地址
	LNode* p, * q;

	//[2]初始化p指向首元结点
	//若首元结点不存在(空表),(*L)->next会使p指向头结点(*L),不会进入循环
	p = (*L)->next;

	//[3]依次销毁除头结点外的所有结点
	while (p != *L)
		//注意这里不能写(q!=(*L)),因为在这条语句之前q并未初始化(是一个野指针)
		//p !=(*L)也要区别于单链表清空操作中循环的终止条件p!=NULL
	{
		//q 记录下一个结点的位置
		q = p->next;
		// 释放当前结点
		free(p);
		//p 移动到下一个结点
		p = q;
	}

//在清空完单循环链表后释放头结点
	//[4]释放头结点
	free(*L);
	
	//[5]更新头指针和尾指针,避免悬挂指针问题
	*L = NULL;
	*T = NULL;
	return true;
}

5.单循环链表的清空

注意

清空后要更新尾指针指向头结点,故传入尾指针的二级指针

清空后要更新头结点的next域指向头结点,不改变头指针的指向,故传入头指针

ClearLinkList(LinkHead L,LinkTail* T)
1.算法思路:依次释放所有结点,并将头结点指针域重置指向自己,更新尾指针的指向
2.算法步骤:

(1)定义了两个临时指针p,q
//p:存储待销毁结点的地址
//q: 存储下一个待销毁结点的地址
(2)初始化p指向首元结点
(3)依次销毁除头结点外的所有结点
(4)重新初始化头尾指针(这一步是与单循环链表的销毁操作唯一的不同)

//5.单循环链表的清空
//实际上就是销毁除了头结点的其余结点
bool ClearLinkList(LinkHead L,LinkTail* T) 
{
	//[0]判断单循环链表头结点是否存在
	//一个带有头尾指针的单循环链表是由头指针和尾指针组成的,所以要判断头指针和尾指针是否为空
	if (L == NULL||(*T) == NULL)
	{
		return false;
	}

	//[1]定义了两个临时指针p,q
	//p:存储待销毁结点的地址
	//q:存储下一个待销毁结点的地址
	LNode* p, * q;
	
	//[2]初始化p指向首元结点
	//若首元结点不存在(空表),(*L)->next会使p指向头结点(*L),不会进入循环
	p = L->next;

	//[3]依次销毁除头结点外的所有结点
	while (p != L) 
	{
		//q 记录下一个结点的位置
		q = p->next; 
		// 释放当前结点
		free(p); 
		//p 移动到下一个结点
		p = q;      
	}

//循环销毁完毕后: 当前 p 指向头结点,因此不需要释放头结点,因为头结点应该保留在链表中
	
    //[4]重新初始化头尾指针
	L->next = L; // 确保头结点的 next 指针指向自身,表示链表为空
	(*T) = L;// 确保尾指针指向头结点,表示链表为空
	return true;
}

6.求单循环链表的表长

可以从任意结点开始遍历,只要设置好正确的终止条件即可,这里使用头指针遍历

LinkListLength(LinkHead L)
1.算法步骤:

(1)定义一个临时指针p,并让其指向首元结点
(2)初始化计数器为0(首元结点不存在时,没有进行循环操作,直接返回0)
(3)循环遍历,统计结点数

//6.求单循环链表的表长
//返回L中的元素个数
//可以从任意结点开始遍历,这里使用头指针遍历,与单链表操作相似(但要注意判空操作:p!=L;)
int LinkListLength(LinkHead L)
{
	//[0]判断单循环链表头结点是否存在
	if (L == NULL)
	{
		return 0;
	}

	//[1]定义一个临时指针p,并让其指向首元结点
	//若首元结点不存在(空表),(*L)->next会使p指向头结点(*L),不会进入循环
	LNode* p = L->next;
	
	//[2]初始化计数器为0,当首元结点不存在时,没有进行循环操作,直接返回0
	int i = 0;
	//[3]循环遍历,统计结点数
	while (p != L )
	{
		i++;//进入循环,证明存在首元结点,故先叠加
		p = p->next;//当p指向空,计数器已经将表长记录成功
	}
	return i;
}

7.单循环链表的按位查找

方法一用头指针遍历

GetElemLinkList_H(LinkHead L, int i, ElemType* e)

方法二用尾指针遍历

GetElemLinkList_T(LinkTail T, int i, ElemType* e)
1.头指针遍历------算法步骤:

(1)初始化 p 指向首元结点
(2)初始化计数器为 1
(3)循环查找第i个元素
(4)异常判断
// p == L
//1.表示首元结点不存在时,异常退出
//2.表示 i > 链表长度时,异常退出
// j < i
// 表示 i < 1时,异常退出
(5)获取第 i 个结点的值

//7.单循环链表的按位查找
//取单循链表中第i个元素的内容
// [方法1:从头指针开始遍历]与单链表的查找操作一致
bool GetElemLinkList_H(LinkHead L, int i, ElemType* e)
{
	//[0]判断单循环链表头结点是否存在
	if (L == NULL)
	{
		return false;
	}

	//[1] 初始化 p 指向首元结点
	//若首元结点不存在(空表),L->next会使p指向头结点L,不会进入循环
	LNode* p = L->next;
	
	//[2] 初始化计数器为 1
	int j = 1;

	//[3] 查找
    // p != L:
		// 1. 保证首元结点不存在时,不进入循环
		// 2. 保证 i 大于链表长度时,在 p 指向头结点时终止循环
	// j < i:
		//正常情况下: 保证循环终止时 p 指向第 i 个结点
	while (p != L && j < i)
	{
		p = p->next; // 移动到下一个结点
		j++;         // 计数器叠加
	}

	//[4] 异常判断
	// p == L 
		//1.表示首元结点不存在时,异常退出
		//2.表示 i > 链表长度时,异常退出
	// j < i 
	// 表示 i < 1时,异常退出
	if (p == L || j > i)
	{
		return false;
	}

	//[5] 获取第 i 个结点的值
	*e = p->data;

	return true;
}
2.尾指针遍历------算法步骤:

(1)初始化 p 指向首元结点 :T->next->next
(2)初始化计数器为 1
(3)循环查找第i个元素
(4)异常判断
// p == T->next
//1.表示首元结点不存在时,异常退出
//2.表示 i > 链表长度时,异常退出
// j < i
// 表示 i < 1时,异常退出
(5)获取第 i 个结点的值

// [方法2:从尾指针开始遍历]
//单循环链表只有尾指针时,只能使用尾指针
bool GetElemLinkList_T(LinkTail T, int i, ElemType* e)
{
	//[0]判断单循环链表头结点是否存在
	if (T == NULL)
	{
		return false;
	}

	//[1] 初始化 p 指向首元结点
	//若首元结点不存在(空表),T->next->next会使p指向头结点T(T->next),不会进入循环
	LNode* p = T->next->next;
	//[2] 初始化计数器为 1
	int j = 1;

	// [3] 查找
	 // p != T->next:
		// 1. 保证首元结点不存在时,不进入循环
		// 2. 保证 i 大于链表长度时,在 p 指向头结点时终止循环
	// j < i:
		//正常情况下: 保证循环终止时 p 指向第 i 个结点
	while (p != T->next && j < i)
	{
		p = p->next;// 移动到下一个结点
		j++;//计数器叠加
	}

	// [4] 异常判断
	// p == T->next 
		//1.表示首元结点不存在时,异常退出
		//2.表示 i > 链表长度时,异常退出
	// j < i 
	    // 表示 i < 1时,异常退出
	if (p == T->next || j > i)
	{
		return false;
	}

	// [5] 获取第 i个结点的值
	*e = p->data;

	return true;
}

8.单循环链表的按值查找(同样可以用头尾指针分别遍历,这里采用头指针)

方法1返回地址

LNode* LocateLinkList1(LinkHead L, ElemType e)

方法1返回位序

int LocateLinkList2(LinkHead L, ElemType e)
1.返回地址------算法步骤:

(1)初始化 p 指向首元结点
(2)遍历链表,并寻找与元素e相同的结点
(3)循环完毕,未找到对应元素时,返回NULL
(4)异常判断
//p == L:单链表在这里是可以直接返回p的,但单循环链表不能这样返回,因为p指向的是头结点
(4)查找成功,返回对应地址

// 8.1返回地址(与单链表类似)
LNode* LocateLinkList1(LinkHead L, ElemType e)
{
	//[0]判断单循环链表头结点是否存在
	if (L == NULL)
	{
		return NULL;
	}


	//[1] 初始化 p 指向首元结点
	//若首元结点不存在(空表),L->next会使p指向头结点L,不会进入循环
	LNode* p = L->next;

	//[2]遍历链表,并寻找与元素e相同的结点
	//p!=L:
		// 1.保证首元结点不存在时,不进入循环  
		// 2.保证链表中无与元素e相同结点时 在p指向尾结点next时(实际上现在指向头结点)终止循环
	//p->data!=e:
		// 保证循环终止时p指向与元素e相同的结点
	while (p != L && p->data != e)
	{
		p = p->next;
	}


	//[3]循环完毕,未找到对应元素时,返回NULL
	//单链表在这里是可以直接返回p的,但单循环链表不能这样返回,因为p指向的是头结点
	if (p == L)
	{
		printf("表中无对应元素!\n");
		return NULL;
	}

	//[4]查找成功,返回对应地址
	return p;

}
2.返回位序------算法步骤:

(1)初始化 p 指向首元结点
(2)初始化计数器为1
(3)遍历链表,并寻找与元素e相同的结点
(4)异常判断
//p == L:
//1.首元结点不存在的情况
//2.链表中无与元素e相同结点的情况
(5)查找成功,返回位序

// 8.2返回位序(与单链表类似)
int LocateLinkList2(LinkHead L, ElemType e)
{
	//[0] 判断单循环链表头结点是否存在
	if (L == NULL)
	{
		return 0;
	}

	//[1] 初始化 p 指向首元结点
	//若首元结点不存在(空表),L->next会使p指向头结点L,不会进入循环
	LNode* p = L->next;

	//[2]初始化计数器为1
	int count = 1;


	//[3]遍历链表,并寻找与元素e相同的结点
	//p!=NULL:
		// 1.保证首元结点不存在时,不进入循环  
		// 2.保证链表中无与元素e相同结点时 在p指向尾结点next(实际上现在指向头结点)时终止循环
	//p->data != e:
		// 保证循环终止时p指向第与元素e相同的结点
	while (p != L && p->data != e)
	{
		p = p->next;
		count++;
	}

	//[4]异常判断
	//p == L:
		//1.首元结点不存在的情况
		//2.链表中无与元素e相同结点的情况
	if (p == L)
	{
		return 0;//返回0表示查找失败
	}
	//[5]查找成功,返回位序
	return count;
}

9.单循环链表的按位插入

注意:若插入位置为单循环链表表尾,则需要更新尾指针的指向,故传入尾指针的二级指针

InsertLinkList(LinkHead L, LinkTail *T,int i, ElemType e)
1.算法步骤:

(1)初始化临时指针p指向头结点
(2) 初始化计数器j等于0:此计数器用来控制循环----寻找第i-1个结点
(3)循环寻找第i-1个结点
注意这里终止条件
//p->next!=L:
// 1.保证查找一周后,在p指向尾结点时结束循环
// 2.保证传入的i 大于等于 表长加1时,循环到p指向尾结点时终止循环
//j<i-1:
// 保证循环终止时p指向第i-1个结点
(4)异常判断
//j!=i:
//1.i<1的情况:不进入循环,最终j==0,不等于i-1
//2.i>表长加1的情况:进入循环,最终p指向尾结点,j等于表长
(5)为新结点动态分配内存
(6)初始化新结点数据域
(7)将新结点插入到第i-1个结点之后
(8)如果在表尾插入元素:需要改变尾指针的指向,指向新的尾结点

//9.单循环链表的按位插入
bool InsertLinkList(LinkHead L, LinkTail *T,int i, ElemType e)
{
	//[0]判断单循环链表头结点是否存在
	if (L == NULL)
	{
		return false;
	}

	//[1]初始化临时指针p指向头结点
	LNode* p = L;

	//[2]初始化计数器j等于0:此计数器用来控制循环----寻找第i-1个结点
	int j = 0;

	//[3]循环寻找第i-1个结点
	//p->next!=L:
		// 1.保证查找一周后,在p指向尾结点时结束循环
		// 2.保证传入的i 大于等于 表长加1时,循环到p指向尾结点时终止循环
	//j<i-1:
		// 保证循环终止时p指向第i-1个结点
	while (p->next != L && j < i - 1)
	{
		p = p->next;
		j++;
	}

	//[4]异常判断
	//1.i<1的情况:不进入循环,最终j==0,不等于i-1
	//2.i>表长加1的情况:进入循环,最终p指向尾结点,j等于表长
	//例:表长为5,i=7,               j=5         j!=i-1即5!=6
	if (j != i - 1) 
	{
		return false;
	}

	//[5]为新结点动态分配内存
	LNode* s = (LNode*)malloc(sizeof(LNode));

	if (s == NULL)
	{
		printf("内存分配失败!\n");
		return false;
	}

	//[6]初始化新结点数据域
	s->data = e;
    
	//[7]将新结点插入到第i-1个结点之后(这里顺序不能互换,互换后会断链)
	//这里包含了在表头和表尾插入的情况
	s->next = p->next;
	p->next = s;

	//注意:在改变完指针的指向后,判断是否在表尾进行操作
	//如果在表尾插入元素:需要改变尾指针的指向,指向新的尾结点
	if (p==*T)
	{
		*T = s;
	}

	return true;
}

10.单循环链表的按位删除

注意:若删除位置为单循环链表表尾,则需要更新尾指针指向待删除位置的前一个元素,故传入尾指针的二级指针

DeleteLinkList(LinkHead L, LinkTail* T, int i,ElemType *e)
1.算法步骤:

(1)初始化临时指针p指向头结点(p用来寻找第i-1个结点)
(2)定义临时指针q(q用来销毁待删除结点)
(3)初始化计数器j等于0:此计数器用来控制循环----寻找第i-1个结点
(4)循环寻找第i-1个结点
//p->next!=L:
// 1.保证首元结点不存在时,不进入循环
// 2.保证传入的i超过表长时,循环到p指向尾结点时(p->next为L)终止循环
//j<i-1
// 保证循环终止时p指向第i-1个结点
(5)异常判断
//p->next == L:
//1.首元结点不存在的情况
//2.i大于链表长度的情况(i==链表长度+1:p指向尾结点,p->next指向L),无法删除不存在的结点
//j > i - 1
//1.传入的i小于1的情况
//比如i=0:不进入循环,0>-1,进入分支,返回异常
(6)用q指向待删除结点并记录数据
(7)若在表尾删除结点:需要改变尾指针的指向,指向新的尾结点
(8)删除结点(注意不要断链)

bool DeleteLinkList(LinkHead L, LinkTail* T, int i,ElemType *e)
{
	//[0]判断单循环链表头结点是否存在
	if (L == NULL)
	{
		return false;
	}

	//[1]初始化临时指针p指向头结点(p用来寻找第i-1个结点)
	LNode* p = L;
	//[2]定义临时指针q(q用来销毁待删除结点)
	LNode* q;
	//[3]初始化计数器j等于0:此计数器用来控制循环----寻找第i-1个结点
	int j = 0;

	//[4]循环寻找第i-1个结点
	//p->next!=L:
		// 1.保证首元结点不存在时,不进入循环  
		// 2.保证传入的i超过表长时,循环到p指向尾结点时(p->next为L)终止循环
	//j<i-1:
		// 保证循环终止时p指向第i-1个结点
	while (p->next != L && j < i - 1)
	{
		p = p->next;// 移动到下一个结点
		j++;//计数器加一
	}

	//[5]异常判断
	//p->next == L:
		//1.首元结点不存在的情况
		//2.i大于链表长度的情况(i==链表长度+1:p指向尾结点,p->next指向L),无法删除不存在的结点
	//j > i - 1
		//1.传入的i小于1的情况
		//比如i=0:不进入循环,0>-1,进入分支,返回异常
	if (p->next==L||j > i - 1)
	{
		return false;
	}

	//[6]用q指向待删除结点并记录数据
	q = p->next;
	*e = q->data;
	
	//[7]若在表尾删除结点:需要改变尾指针的指向,指向新的尾结点
	if (q == *T)
	{
		*T = p;
	}

	//[8]删除结点
	p->next = q->next;
	free(q);

	
	return true;
}

11.头插法建立单循环链表

注意头尾指针的更新,传入二级指针

CreateLinkList_H(LinkHead* L, LinkTail* T, int n)
1.算法步骤:

(1)建立一个空的头结点并初始化,且初始化尾指针
(2)循环建立新结点并插入到表头
[[1]]建立新结点并初始化data域
[[2]]将新结点插入到表头
[[3]]注意头插法建立单循环链表时,只需要更新一次尾指针 指向第一次插入的新结点
(3)插入完成后确保尾结点的 next 指向头结点

//11.头插法建立单循环链表:
bool CreateLinkList_H(LinkHead* L, LinkTail* T, int n)
{
	//[1]建立一个空的头结点并初始化,且初始化尾指针
	*L = (LinkHead)malloc(sizeof(LNode));
	
	if (*L == NULL)
	{
		printf("内存分配失败!\n");
		return false;
	}

	(*L)->next = *L;//初始化头结点next域指向自己

	*T = *L;//初始化尾指针指向头结点

	//[2]循环建立新结点并插入到表头
	LNode* p;
	
	for (int i = 0; i < n; i++)
	{
		//[[1]]建立新结点并初始化data域
		p = (LNode*)malloc(sizeof(LNode));

		if (p == NULL)
		{
			printf("内存分配失败!\n");
			return false;
		}

		scanf_s("%d", &p->data);

		//[[2]]将新结点插入到表头
		p->next = (*L)->next;//新结点的next域应指向原来的首元结点
		(*L)->next = p;//头结点的next域指向新结点

		//[[3]]注意头插法建立单循环链表时,只需要更新一次尾指针 指向第一次插入的新结点
		if (i == 0)
		{
			*T = p;
		}
		
	}

	//[3]插入完成后确保尾结点的 next 指向头结点(实际上在循环中已经实现过了,可以省略)
	(*T)->next = *L;

	return true;
}

12.尾插法建立单循环链表

CreateLinkList_T(LinkHead* L, LinkTail* T, int n)
1.算法步骤:

(1)建立一个空的头结点并初始化,且初始化尾指针
(2)循环建立新结点并插入到表尾
[[1]]建立新结点并初始化data域
[[2]]将新结点插入到表尾
[[3]]注意尾插法建立单循环链表时,每插入一个新结点尾指针 都要更新指向新插入的结点
(3)插入完成后确保尾结点的 next 指向头结点

bool CreateLinkList_T(LinkHead* L, LinkTail* T, int n)
{
	//[1]建立一个空的头结点并初始化,且初始化尾指针
	*L = (LinkHead)malloc(sizeof(LNode));
	
	if (*L == NULL)
	{
		printf("内存分配失败!\n");
		return false;
	}

	(*L)->next = (*L);//初始化头结点next域指向自己
	
	*T = *L;//初始化尾指针指向头结点


	//[2]循环建立新结点并插入到表尾

	LNode* p;
	
	for (int i = 0; i < n; i++)
	{
		//[[1]]建立新结点并初始化data域
		p = (LNode*)malloc(sizeof(LNode));
		
		if (p == NULL)
		{
			printf("内存分配失败!\n");
			return false;
		}

		scanf_s("%d", &p->data);

		//[[2]]将新结点插入到表尾
		p->next = *L;//新结点的next域应指向头结点
		(*T)->next = p;//原来的尾结点的next域指向新结点
		*T = p;//更新尾指针指向新的尾结点
	}
	//[3]插入完成后确保尾结点的 next 指向头结点(实际上在循环中已经实现过了,可以省略)
	(*T)->next = *L;
	return true;
}

13.输出单循环链表的所有元素

方法1:头指针遍历输出

printLinkList_H(LinkHead L)

方法2:尾指针遍历输出

printLinkList_T(LinkTail T)
1.头指针遍历输出------算法步骤:

(1)初始化 p 指向头结点
(2)判断链表是否为空(即只有头结点)
(3)遍历链表并输出所有结点元素,直到最后一个结点

//13.1头指针法
bool printLinkList_H(LinkHead L)
{
	//[0]判断单循环链表头结点是否存在
	if (L == NULL)
	{
		return false;
	}

	//[1]初始化 p 指向头结点
	LNode* p = L->next;

	//[2]判断链表是否为空(即只有头结点)
	if (p == L)
	{
		printf("NULL LinkList!\n");// 输出 NULL LinkList! 代表链表为空
		return false;// 直接返回,不继续执行后续代码
	}

	//[3]遍历链表并输出所有结点元素,直到最后一个结点
	while (p!=L)
	{
		printf("%d-->", p->data); // 输出当前结点的数据
		p = p->next;// 移动指针到下一个结点
	}

	printf("end\n");

	return true;
}
1.尾指针遍历输出------算法步骤:

(1)初始化 p 指向头结点 T->next->next
(2)判断链表是否为空(即只有头结点) p == T->next
(3)遍历链表并输出所有结点元素,直到最后一个结点

//13.2尾指针法
bool printLinkList_T(LinkTail T)
{
	//[0]判断单循环链表头结点是否存在
	if (T == NULL||T->next==NULL)
	{
		return false;
	}

	//[1]初始化 p 指向头结点
	LNode* p = T->next->next;

	//[2]判断链表是否为空(即只有头结点)
	if (p == T->next)
	{
		printf("NULL List!\n");
		return false;
	}

	//[3]遍历链表并输出所有结点元素,直到最后一个结点
	while (p != T->next)
	{
		printf("%d-->", p->data);// 输出当前结点的数据
		p = p->next;// 移动指针到下一个结点
	}

	printf("end\n");
	return true;

}

14.带尾指针的循环链表的合并

注意:合并后返回整表的尾指针

LNode * ConnectLinkList(LinkTail Ta, LinkTail Tb)
1.算法步骤:

(1)用p保存Ta的头结点
(2)将Tb的表头接在Ta的表尾
(3)释放Tb的头结点
(4)修改Tb的尾结点next域指向Ta的头结点
(5)返回合并后的链表

2.图解:

在这里插入图片描述

//14.带尾指针的循环链表的合并
//假设Ta和Tb是两个非空的单循环链表,要求合并Ta和Tb,合并后的链表依然为循环链表
LNode * ConnectLinkList(LinkTail Ta, LinkTail Tb)
{
	//[1]用p保存Ta的头结点
	LNode* p = Ta->next;
	//[2]将Tb的表头接在Ta的表尾
	Ta->next = Tb->next->next;
	//[3]释放Tb的头结点
	free(Tb->next);
	//[4]修改Tb的尾结点next域指向Ta的头结点
	Tb->next = p;
	return Tb;//Tb是合并后的链表(尾指针)
}

15.所有操作代码如下:

//单循环链表的实现(带头结点,带头尾指针)
#include<stdio.h>
#include<stdlib.h>

typedef int ElemType;

#define bool int
#define false 0
#define true 1

//1.单循环链表的结构定义不变
typedef struct LNode
{
	ElemType data;//数据域
	struct LNode* next;//指针域
}LNode, * LinkHead,* LinkTail;
//LNode *用来定义结点指针;
//LinkHead 用来定义头指针;
//LinkTail 用来定义尾指针;



//2.单循环链表的初始化(仍然是初始化头结点)
//注意初始化尾指针的指向
bool InitLinkList(LinkHead* L, LinkTail* T)
{
	//[1]创建头结点
	*L = (LinkHead)malloc(sizeof(LNode));

	if (*L == NULL)
	{
		printf("内存分配失败!\n");
		return false;
	}

	(*L)->data = 0;//头结点数据域可以自行赋值,一般不进行赋值

//以下操作与单链表初始化有所不同:
	
    //[2]初始化头结点指针域指向自己
	(*L)->next = (*L);
	
	//[3]初始化尾指针指向头结点
	(*T) = (*L);
	return true;
}


//3.判断单循环链表是否为空
//[方法1:判断头结点的next域是否指向自己]
bool LinkListEmpty1(LinkHead L)
{
	//[0]判断单循环链表头结点是否存在
	if (L == NULL)
	{
		return false;
	}
	//[1]判断头结点的next域是否指向自己
	//若头结点的next域指向自己,则说明单循环链表为空,返回true
	if (L->next == L)
	{
		return true;
	}
    //否则说明单循环链表不为空,返回false
	else 
	{
		return false;
	}
}

//[方法2:判断尾指针是否指向头结点]
bool LinkListEmpty2( LinkTail T)
{
	//[0]判断单循环链表头结点是否存在
	if (T == NULL)
	{
		return false;
	}
	//[1]判断尾指针是否指向头结点
	//若尾指针指向头结点(也就是它本身),则说明单循环链表为空,返回true
	if (T == T->next)
	{
		return true;
	}
	//否则单循环链表不为空,返回false
	else
	{
		return false;
	}
}


//4.单循环链表的销毁
bool DestroyLinkList(LinkHead* L,LinkTail  *T)
{
	//[0]判断单循环链表头结点是否存在
	//一个带有头尾指针的单循环链表是由头指针和尾指针组成的,所以要判断头指针和尾指针是否为空
	if (*L == NULL|| *T == NULL)
	{
		return false;
	}

//以下操作与单链表清空操作相似;但是要注意:
// 循环终止条件应为(p!=*L)
// 在销毁完毕后,要更新头尾指针,避免指针悬挂问题

	//[1]定义了两个临时指针p,q
	//p:存储待销毁结点的地址
	//q:存储下一个待销毁结点的地址
	LNode* p, * q;

	//[2]初始化p指向首元结点
	//若首元结点不存在(空表),(*L)->next会使p指向头结点(*L),不会进入循环
	p = (*L)->next;

	//[3]依次销毁除头结点外的所有结点
	while (p != *L)
		//注意这里不能写(q!=(*L)),因为在这条语句之前q并未初始化(是一个野指针)
		//p !=(*L)也要区别于单链表清空操作中循环的终止条件p!=NULL
	{
		//q 记录下一个结点的位置
		q = p->next;
		// 释放当前结点
		free(p);
		//p 移动到下一个结点
		p = q;
	}

//在清空完单循环链表后释放头结点
	//[4]释放头结点
	free(*L);
	
	//[5]更新头指针和尾指针,避免悬挂指针问题
	*L = NULL;
	*T = NULL;
	return true;
}

//5.单循环链表的清空
//实际上就是销毁除了头结点的其余结点
bool ClearLinkList(LinkHead L,LinkTail* T) 
{
	//[0]判断单循环链表头结点是否存在
	//一个带有头尾指针的单循环链表是由头指针和尾指针组成的,所以要判断头指针和尾指针是否为空
	if (L == NULL||(*T) == NULL)
	{
		return false;
	}

	//[1]定义了两个临时指针p,q
	//p:存储待销毁结点的地址
	//q:存储下一个待销毁结点的地址
	LNode* p, * q;
	
	//[2]初始化p指向首元结点
	//若首元结点不存在(空表),(*L)->next会使p指向头结点(*L),不会进入循环
	p = L->next;

	//[3]依次销毁除头结点外的所有结点
	while (p != L) 
	{
		//q 记录下一个结点的位置
		q = p->next; 
		// 释放当前结点
		free(p); 
		//p 移动到下一个结点
		p = q;      
	}

//循环销毁完毕后: 当前 p 指向头结点,因此不需要释放头结点,因为头结点应该保留在链表中
	
    //[4]重新初始化头尾指针
	L->next = L; // 确保头结点的 next 指针指向自身,表示链表为空
	(*T) = L;// 确保尾指针指向头结点,表示链表为空
	return true;
}


//6.求单循环链表的表长
//返回L中的元素个数
//可以从任意结点开始遍历,这里使用头指针遍历,与单链表操作相似(但要注意判空操作:p!=L;)
int LinkListLength(LinkHead L)
{
	//[0]判断单循环链表头结点是否存在
	if (L == NULL)
	{
		return 0;
	}

	//[1]定义一个临时指针p,并让其指向首元结点
	//若首元结点不存在(空表),(*L)->next会使p指向头结点(*L),不会进入循环
	LNode* p = L->next;
	
	//[2]初始化计数器为0,当首元结点不存在时,没有进行循环操作,直接返回0
	int i = 0;
	//[3]循环遍历,统计结点数
	while (p != L )
	{
		i++;//进入循环,证明存在首元结点,故先叠加
		p = p->next;//当p指向空,计数器已经将表长记录成功
	}
	return i;
}


//7.单循环链表的按位查找
//取单循链表中第i个元素的内容
// [方法1:从头指针开始遍历]与单链表的查找操作一致
bool GetElemLinkList_H(LinkHead L, int i, ElemType* e)
{
	//[0]判断单循环链表头结点是否存在
	if (L == NULL)
	{
		return false;
	}

	//[1] 初始化 p 指向首元结点
	//若首元结点不存在(空表),L->next会使p指向头结点L,不会进入循环
	LNode* p = L->next;
	
	//[2] 初始化计数器为 1
	int j = 1;

	//[3] 查找
    // p != L:
		// 1. 保证首元结点不存在时,不进入循环
		// 2. 保证 i 大于链表长度时,在 p 指向头结点时终止循环
	// j < i:
		//正常情况下: 保证循环终止时 p 指向第 i 个结点
	while (p != L && j < i)
	{
		p = p->next; // 移动到下一个结点
		j++;         // 计数器叠加
	}

	//[4] 异常判断
	// p == L 
		//1.表示首元结点不存在时,异常退出
		//2.表示 i > 链表长度时,异常退出
	// j < i 
	// 表示 i < 1时,异常退出
	if (p == L || j > i)
	{
		return false;
	}

	//[5] 获取第 i 个结点的值
	*e = p->data;

	return true;
}


// [方法2:从尾指针开始遍历]
//单循环链表只有尾指针时,只能使用尾指针
bool GetElemLinkList_T(LinkTail T, int i, ElemType* e)
{
	//[0]判断单循环链表头结点是否存在
	if (T == NULL)
	{
		return false;
	}

	//[1] 初始化 p 指向首元结点
	//若首元结点不存在(空表),T->next->next会使p指向头结点T(T->next),不会进入循环
	LNode* p = T->next->next;
	//[2] 初始化计数器为 1
	int j = 1;

	// [3] 查找
	 // p != T->next:
		// 1. 保证首元结点不存在时,不进入循环
		// 2. 保证 i 大于链表长度时,在 p 指向头结点时终止循环
	// j < i:
		//正常情况下: 保证循环终止时 p 指向第 i 个结点
	while (p != T->next && j < i)
	{
		p = p->next;// 移动到下一个结点
		j++;//计数器叠加
	}

	// [4] 异常判断
	// p == T->next 
		//1.表示首元结点不存在时,异常退出
		//2.表示 i > 链表长度时,异常退出
	// j < i 
	    // 表示 i < 1时,异常退出
	if (p == T->next || j > i)
	{
		return false;
	}

	// [5] 获取第 i个结点的值
	*e = p->data;

	return true;
}

//8.单循环链表的按值查找(同样可以用头尾指针分别遍历,这里采用头指针)

// 8.1返回地址(与单链表类似)
LNode* LocateLinkList1(LinkHead L, ElemType e)
{
	//[0]判断单循环链表头结点是否存在
	if (L == NULL)
	{
		return NULL;
	}


	//[1] 初始化 p 指向首元结点
	//若首元结点不存在(空表),L->next会使p指向头结点L,不会进入循环
	LNode* p = L->next;

	//[2]//[2]遍历链表,并寻找与元素e相同的结点
	//p!=L:
		// 1.保证首元结点不存在时,不进入循环  
		// 2.保证链表中无与元素e相同结点时 在p指向尾结点next时(实际上现在指向头结点)终止循环
	//p->data!=e:
		// 保证循环终止时p指向与元素e相同的结点
	while (p != L && p->data != e)
	{
		p = p->next;
	}


	//[3]循环完毕,未找到对应元素时,返回NULL
	//单链表在这里是可以直接返回p的,但单循环链表不能这样返回,因为p指向的是头结点
	if (p == L)
	{
		printf("表中无对应元素!\n");
		return NULL;
	}

	//[4]查找成功,返回对应地址
	return p;

}

// 8.2返回位序(与单链表类似)
int LocateLinkList2(LinkHead L, ElemType e)
{
	//[0] 判断单循环链表头结点是否存在
	if (L == NULL)
	{
		return 0;
	}

	//[1] 初始化 p 指向首元结点
	//若首元结点不存在(空表),L->next会使p指向头结点L,不会进入循环
	LNode* p = L->next;

	//[2]初始化计数器为1
	int count = 1;


	//[3]遍历链表,并寻找与元素e相同的结点
	//p!=NULL:
		// 1.保证首元结点不存在时,不进入循环  
		// 2.保证链表中无与元素e相同结点时 在p指向尾结点next(实际上现在指向头结点)时终止循环
	//p->data != e:
		// 保证循环终止时p指向第与元素e相同的结点
	while (p != L && p->data != e)
	{
		p = p->next;
		count++;
	}

	//[4]异常判断
	//p == L:
		//1.首元结点不存在的情况
		//2.链表中无与元素e相同结点的情况
	if (p == L)
	{
		return 0;//返回0表示查找失败
	}

	//[5]查找成功,返回位序
	return count;
}




//9.单循环链表的按位插入
bool InsertLinkList(LinkHead L, LinkTail *T,int i, ElemType e)
{
	//[0]判断单循环链表头结点是否存在
	if (L == NULL)
	{
		return false;
	}

	//[1]初始化临时指针p指向头结点
	LNode* p = L;

	//[2]初始化计数器j等于0:此计数器用来控制循环----寻找第i-1个结点
	int j = 0;

	//[3]循环寻找第i-1个结点
	//p->next!=L:
		// 1.保证查找一周后,在p指向尾结点时结束循环
		// 2.保证传入的i 大于等于 表长加1时,循环到p指向尾结点时终止循环
	//j<i-1:
		// 保证循环终止时p指向第i-1个结点
	while (p->next != L && j < i - 1)
	{
		p = p->next;
		j++;
	}

	//[4]异常判断
	//1.i<1的情况:不进入循环,最终j==0,不等于i-1
	//2.i>表长加1的情况:进入循环,最终p指向尾结点,j等于表长
	//例:表长为5,i=7,               j=5         j!=i-1即5!=6
	if (j != i - 1) 
	{
		return false;
	}

	//[5]为新结点动态分配内存
	LNode* s = (LNode*)malloc(sizeof(LNode));

	if (s == NULL)
	{
		printf("内存分配失败!\n");
		return false;
	}

	//[6]初始化新结点数据域
	s->data = e;
    
	//[7]将新结点插入到第i-1个结点之后(这里顺序不能互换,互换后会断链)
	//这里包含了在表头和表尾插入的情况
	s->next = p->next;
	p->next = s;

	//注意:在改变完指针的指向后,判断是否在表尾进行操作
	//如果在表尾插入元素:需要改变尾指针的指向,指向新的尾结点
	if (p==*T)
	{
		*T = s;
	}

	return true;
}


//10.单循环链表的按位删除
bool DeleteLinkList(LinkHead L, LinkTail* T, int i,ElemType *e)
{
	//[0]判断单循环链表头结点是否存在
	if (L == NULL)
	{
		return false;
	}

	//[1]初始化临时指针p指向头结点(p用来寻找第i-1个结点)
	LNode* p = L;
	//[2]定义临时指针q(q用来销毁待删除结点)
	LNode* q;
	//[3]初始化计数器j等于0:此计数器用来控制循环----寻找第i-1个结点
	int j = 0;

	//[4]循环寻找第i-1个结点
	//p->next!=L:
		// 1.保证首元结点不存在时,不进入循环  
		// 2.保证传入的i超过表长时,循环到p指向尾结点时(p->next为L)终止循环
	//j<i-1:
		// 保证循环终止时p指向第i-1个结点
	while (p->next != L && j < i - 1)
	{
		p = p->next;// 移动到下一个结点
		j++;//计数器加一
	}

	//[5]异常判断
	//p->next == L:
		//1.首元结点不存在的情况
		//2.i大于链表长度的情况(i==链表长度+1:p指向尾结点,p->next指向L),无法删除不存在的结点
	//j > i - 1
		//1.传入的i小于1的情况
		//比如i=0:不进入循环,0>-1,进入分支,返回异常
	if (p->next==L||j > i - 1)
	{
		return false;
	}

	//[6]用q指向待删除结点并记录数据
	q = p->next;
	*e = q->data;
	
	//[7]若在表尾删除结点:需要改变尾指针的指向,指向新的尾结点
	if (q == *T)
	{
		*T = p;
	}

	//[8]删除结点
	p->next = q->next;
	free(q);

	
	return true;
}




//11.头插法建立单循环链表:
bool CreateLinkList_H(LinkHead* L, LinkTail* T, int n)
{
	//[1]建立一个空的头结点并初始化,且初始化尾指针
	*L = (LinkHead)malloc(sizeof(LNode));
	
	if (*L == NULL)
	{
		printf("内存分配失败!\n");
		return false;
	}

	(*L)->next = *L;//初始化头结点next域指向自己

	*T = *L;//初始化尾指针指向头结点

	//[2]循环建立新结点并插入到表头
	LNode* p;
	
	for (int i = 0; i < n; i++)
	{
		//[[1]]建立新结点并初始化data域
		p = (LNode*)malloc(sizeof(LNode));

		if (p == NULL)
		{
			printf("内存分配失败!\n");
			return false;
		}

		scanf_s("%d", &p->data);

		//[[2]]将新结点插入到表头
		p->next = (*L)->next;//新结点的next域应指向原来的首元结点
		(*L)->next = p;//头结点的next域指向新结点

		//[[3]]注意头插法建立单循环链表时,只需要更新一次尾指针 指向第一次插入的新结点
		if (i == 0)
		{
			*T = p;
		}
		
	}

	//[3]插入完成后确保尾结点的 next 指向头结点(实际上在循环中已经实现过了,可以省略)
	(*T)->next = *L;

	return true;
}




//12.尾插法建立单循环链表
bool CreateLinkList_T(LinkHead* L, LinkTail* T, int n)
{
	//[1]建立一个空的头结点并初始化,且初始化尾指针
	*L = (LinkHead)malloc(sizeof(LNode));
	
	if (*L == NULL)
	{
		printf("内存分配失败!\n");
		return false;
	}

	(*L)->next = (*L);//初始化头结点next域指向自己
	
	*T = *L;//初始化尾指针指向头结点


	//[2]循环建立新结点并插入到表尾

	LNode* p;
	
	for (int i = 0; i < n; i++)
	{
		//[[1]]建立新结点并初始化data域
		p = (LNode*)malloc(sizeof(LNode));
		
		if (p == NULL)
		{
			printf("内存分配失败!\n");
			return false;
		}

		scanf_s("%d", &p->data);

		//[[2]]将新结点插入到表尾
		p->next = *L;//新结点的next域应指向头结点
		(*T)->next = p;//原来的尾结点的next域指向新结点
		*T = p;//更新尾指针指向新的尾结点
	}
	//[3]插入完成后确保尾结点的 next 指向头结点(实际上在循环中已经实现过了,可以省略)
	(*T)->next = *L;
	return true;
}


//13.1输出单循环链表的所有元素
//头指针法
bool printLinkList_H(LinkHead L)
{
	//[0]判断单循环链表头结点是否存在
	if (L == NULL)
	{
		return false;
	}

	//[1]初始化 p 指向头结点
	LNode* p = L->next;

	//[2]判断链表是否为空(即只有头结点)
	if (p == L)
	{
		printf("NULL LinkList!\n");// 输出 NULL LinkList! 代表链表为空
		return false;// 直接返回,不继续执行后续代码
	}

	//[3]遍历链表并输出所有结点元素,直到最后一个结点
	while (p!=L)
	{
		printf("%d-->", p->data); // 输出当前结点的数据
		p = p->next;// 移动指针到下一个结点
	}

	printf("end\n");

	return true;
}


//13.2尾指针法
bool printLinkList_T(LinkTail T)
{
	//[0]判断单循环链表头结点是否存在
	if (T == NULL||T->next==NULL)
	{
		return false;
	}

	//[1]初始化 p 指向头结点
	LNode* p = T->next->next;

	//[2]判断链表是否为空(即只有头结点)
	if (p == T->next)
	{
		printf("NULL List!\n");
		return false;
	}

	//[3]遍历链表并输出所有结点元素,直到最后一个结点
	while (p != T->next)
	{
		printf("%d-->", p->data);// 输出当前结点的数据
		p = p->next;// 移动指针到下一个结点
	}

	printf("end\n");
	return true;

}

//14.带尾指针的循环链表的合并
//假设Ta和Tb是两个非空的单循环链表,要求合并Ta和Tb,合并后的链表依然为循环链表
LNode * ConnectLinkList(LinkTail Ta, LinkTail Tb)
{
	//[1]用p保存Ta的头结点
	LNode* p = Ta->next;
	//[2]将Tb的表头接在Ta的表尾
	Ta->next = Tb->next->next;
	//[3]释放Tb的头结点
	free(Tb->next);
	//[4]修改Tb的尾结点next域指向Ta的头结点
	Tb->next = p;
	return Tb;//Tb是合并后的链表(尾指针)
}


int main()
{
	LinkHead La;//定义单循环链表Ta的头指针
	LinkTail Ta;//定义单循环链表Ta的尾指针

	LinkHead Lb;//定义单循环链表Tb的头指针
	LinkTail Tb;//定义单循环链表Tb的尾指针

	//头插法创建单循环链表Ta
	printf("头插法创建单循环链表Ta:\n");
	CreateLinkList_H(&La, &Ta, 5);
	printLinkList_H(La);

	printf("\n");
	//尾插法创建单循环链表Tb
	printf("尾插法创建单循环链表Tb:\n");
	CreateLinkList_T(&Lb, &Tb, 6);
	printLinkList_H(Lb);




	printf("\n");
	//求单循环链表的长度
	printf("单循环链表Ta的长度为%d:\n",LinkListLength(La));
	printf("单循环链表Tb的长度为%d:\n",LinkListLength(Lb));
	


	printf("\n");
	//判断单循环链表Ta是否为空
	printf("判断单循环链表Ta是否为空:\n");
	if (LinkListEmpty1(La))
	{
		printf("单循环链表Ta为空\n");
	}
	else
	{
		printf("单循环链表Ta不为空\n");
	}



	printf("\n");
	//查找单循环链表Ta的第3个元素
	printf("查找单循环链表Ta的第3个元素:\n");
	ElemType  e1;
	GetElemLinkList_H(La, 3, &e1);
	printf("单循环链表Ta的第3个元素为:%d\n", e1);

	printf("\n");
	//查找单循环链表Tb的第5个元素
	printf("查找单循环链表Tb的第5个元素:\n");
	ElemType e2;
	GetElemLinkList_T(Tb, 5, &e2);
	printf("单循环链表Tb的第5个元素为:%d\n", e2);

	//查找与单循环链表Ta中与3相等的元素
	printf("\n");
	printf("查找单循环链表Ta中与3相等的元素的地址:\n");
	LNode* m1=LocateLinkList1(La, 3);
	printf("单循环链表Ta中与3相等的元素为:%d\n", m1->data);


	//查找单循环链表Tb中与4相等的元素
	printf("\n");
	printf("查找单循环链表Tb中与4相等的元素的位序:\n");
	int m2 = LocateLinkList2(Lb, 4);
	printf("单循环链表Tb中与4相等的元素的位序为:%d\n", m2);


	//在单循环链表Tb中插入一些元素
	printf("\n");
	printf("在单循环链表Tb中插入一些元素\n");
	printf("第1位插入12:\n");
	InsertLinkList(Lb, &Tb, 1, 12);
	printLinkList_H(Lb);
	
	printf("第8位插入12:\n");
	InsertLinkList(Lb, &Tb, 8, 12);
	printLinkList_H(Lb);
	
	printf("第4位插入13:\n");
	InsertLinkList(Lb, &Tb, 4, 13);

	printf("插入后的单循环链表Tb为:\n");
	printLinkList_H(Lb);
	
	
	
	//在单循环链表Tb中删除一些元素
	printf("\n");
	printf("在单循环链表Tb中删除一些元素\n");
	printf("删除第1位:\n");
	ElemType r1;
	DeleteLinkList(Lb, &Tb, 1, &r1);
	printf("删除的元素值为:%d\n", r1);
	printLinkList_H(Lb);

	printf("删除第3位:\n");
	ElemType r2;
	DeleteLinkList(Lb, &Tb, 3, &r2);
	printf("删除的元素值为:%d\n", r2);
	printLinkList_H(Lb);

	printf("删除第2位:\n");
	ElemType r3;
	DeleteLinkList(Lb, &Tb, 2, &r3);
	printf("删除的元素值为:%d\n", r3);
	printLinkList_H(Lb);

	printf("删除后的单循环链表Tb为:\n");
	printLinkList_H(Lb);


	


	printf("\n");
	//销毁单循环链表Ta
	printf("销毁单循环链表Ta:\n");
	if (DestroyLinkList(&La, &Ta))
	{
		printf("销毁单循环链表Ta成功!\n");
	}
	else
	{
		printf("销毁单循环链表Ta失败!\n");
	}

	printf("\n");
	//清空单循环链表Tb
	printf("清空单循环链表Tb:\n");
	if (ClearLinkList(Lb, &Tb))
	{
		printf("清空单循环链表Tb成功!\n");
	}
	else
	{
		printf("清空单循环链表Tb失败!\n");
	}
	
	
	
	
	printf("\n");
	LinkHead Lc;//定义单循环链表Tc的头指针
	LinkTail Tc;//定义单循环链表Tc的尾指针

	LinkHead Ld;//定义单循环链表Td的头指针
	LinkTail Td;//定义单循环链表Td的尾指针

	//头插法创建单循环链表Tc
	printf("头插法创建单循环链表Tc:\n");
	CreateLinkList_H(&Lc, &Tc, 3);
	printLinkList_H(Lc);

	printf("\n");
	//尾插法创建单循环链表Td
	printf("尾插法创建单循环链表Td:\n");
	CreateLinkList_T(&Ld, &Td, 4);
	printLinkList_H(Ld);
	
	printf("\n");
	//连接两个单循环链表Tc和Td
	LinkTail Tb1=ConnectLinkList(Tc, Td);
	
	
	printf("\n");
	printf("合并后的单循环链表Tb1:\n");
	printLinkList_T(Tb1);

	printf("\n");
	//判断单循环链表Tb1是否为空
	printf("判断单循环链表Tb1是否为空:\n");
	if (LinkListEmpty2(Tb1))
	{
		printf("单循环链表Tb1为空\n");
	}
	else
	{
		printf("单循环链表Tb1不为空\n");
	}
	return 0;
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值