最复杂的链表(带哨兵位的双向循环链表)

双向循环链表

  • 双向循环链表接口
    • 1.链表定义
    • 2.链表初始化
    • 3.链表的销毁
    • 4.链表的打印
    • 5.链表的查找
    • 6.链表的插入
    • 7.链表的删除
    • 8.判断链表是否为空
    • 9.获取链表元素个数
  • 2.总结:

双向循环链表接口

1.链表定义

双向链表就在这里可以体现,也就是双指针,next和prev

typedef int LTDataType;
typedef struct ListNode
{
	LTDataType data;
	struct ListNode* next;
	struct ListNode* prev;
}ListNode;

2.链表初始化

链表初始化,这里可以体现循环这一特点以及头结点的建立。

ListNode* ListInit()
{
	ListNode* phead = BuyListNode(-1);//头结点
	phead->next = phead;//循环
	phead->prev = phead;
	return phead;
}

3.链表的销毁

这里对于链表销毁,无非就是链表遍历,但主要是我们要明白如何遍历:
这里我们会选择从头结点的下一个结点(cur)开始遍历,直到回到头结点,完成一次遍历,每遍历一个元素,就释放一个元素,所以我们为了方便一般好会有个(next)指针指向cur下一个位置

void ListDestroy(ListNode* phead)
{
	assert(phead);//什么时候断言:当phead不可能为空的时候断言
	
	ListNode* cur = phead->next;
	ListNode* next = cur->next;
	while (cur != phead)
	{
		free(cur);//销毁cur指向的结点
		cur = next;
		next = cur->next;
	}
	free(phead);
	phead = NULL;
}

4.链表的打印

同样的道理,遍历打印即可。

void ListPrint(ListNode* phead)
{
	assert(phead);
	
	ListNode* cur = phead->next;
	printf("phead<=>");
	while (cur!=phead) 
	{
		printf("%d<=>", cur->data);
		cur = cur->next;
	}
	printf("\n");
}

5.链表的查找

遍历对比即可。

ListNode* ListFind(ListNode* phead, LTDataType x)
{
	assert(phead);

	ListNode* cur = phead->next;
	while (cur != phead)
	{
		if (x == cur->data)
		{
			return cur;
		 }
	}
	return NULL;
}

6.链表的插入

链表任意位置插入,需要结合上述接口,链表的查找,找到pos位置,然后在pos前后插入,这里是pos前插入。

void ListInsert(ListNode* pos, LTDataType x)
{
	assert(pos);

	ListNode* node = BuyListNode(x);
	ListNode* cur = pos->prev;//优化插入过程
	//pos前结点与新节点建立联系
	cur->next = node;
	node->prev = cur;
	//新节点与pos建立联系
	pos->prev = node;
	node->next = pos;
}

我们来思考一下,如果我们没有ListNode* cur = pos->prev;//优化插入过程这串代码,会有什么麻烦。

  • 麻烦在于我们必须考虑连接的先后顺序,不可以先改变pos位置指针与前一个结点的关系,不然会增大找到上一个结点的难度(只能遍历寻找)。
  • 只能先建立前一个结点与newnode结点的关系 。

7.链表的删除

这里思路也更加简单,记录pos位置前后,然后释放pos,在直接用指针链接前后关系。

void ListErase(ListNode* pos)
{
	assert(pos);
	ListNode* prev = pos->prev;
	ListNode* next = pos->next;

	prev->next = next;
	next->prev = prev;
	free(pos);
	pos = NULL;
}

8.判断链表是否为空

注意带库声明,其余都比较简单

bool ListEmpty(ListNode* phead)
{
	assert(phead);

	return phead->next == phead;
}

9.获取链表元素个数

遍历并且带着一个count记录即可。

int ListsSize(ListNode* phead)
{
	assert(phead);

	ListNode* cur = phead->next;
	int count = 0;
	while (cur != phead)
	{
		count++;
		cur = cur->next;
	}
	return count;
}

2.总结:

带头双向循环链表在我们学习了单链表以后显得十分简单,因为带头结点,插入也不需要二级指针。具体原因如下:

插入过程中并没有改变头节点的位置,因此无需通过二级指针来更新头节点的指向。只需要通过头指针即可访问到头节点,并进行相应的操作。phead并未改变。

你可能感兴趣的:(链表,数据结构,算法)