数据结构初阶——双向带头循环链表

本文详细介绍如何构建双向带头循环双链表,并提供完整的代码实现。包括结点创建、初始化、插入、删除、查找等核心操作。

之前的文章中讲解了如何构造一个单链表,今天来讲述如何构造一个双向带头循环双链表。

双向带头循环双链表

目录

双向带头循环双链表

定义数据变量类型和创建结点的结构体

创建链表的结点

链表的结点初始化

链表的尾插

链表的打印 

链表的尾删

链表的头插

链表的头删

链表的查找

链表在pos之前插入

链表删除pos位置

判断链表是否为空

计算链表长度的大小

链表的销毁

整体代码


定义数据变量类型和创建结点的结构体

//定义数据变量类型
typedef int LTDataType;//假设定义存储的数据类型为int,当后面需要修改时可以直接进行修改

//创建链表的结点的结构体
typedef struct ListNode
{
	struct ListNode* prev;//一个指向前面元素的前指针
	struct ListNode* next;//一个指向后面元素的后指针
	LTDataType data;
}ListNode;

创建链表的结点

ListNode* BuyListNode(LTDataType x)
{
	ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
	if (newnode == NULL)
	{
		perror("malloc failed");
		exit(-1);
	}
	newnode->next = NULL;
	newnode->prev = NULL;
	newnode->data = x;
	return newnode;
}

这一步与之前的单链表创建结点是相似的,使用malloc函数来开辟一个自定义结构体空间的大小的内存。然后将前指针与后指针都先置为空,将创建的空间的地址返回。

链表的结点初始化

由于在这里创建的是带头的双向循环链表,因此需要一个头结点来存储链表第一个数据的地址。我们需要先将其初始化。

ListNode* ListNodeInit()
{
	//创建带哨兵位的头结点
	ListNode* guard = BuyListNode(-1);

	//令哨兵位的prev指针与next指针都指向结点本身 - 
	guard->next = guard;
	guard->prev = guard;
	return guard;
}

在初始化中,需要特别注意的点就是要将头结点的前置指针与后置指针都指向自己而不是指向空。这个操作会给我们后面的数据插入和删除带来很多的方便。

 

链表的尾插

void ListNodePushBack(ListNode* head, LTDataType x)
{
	//assert(head);
	//ListNode* tail = head->prev;
	//ListNode* newnode = BuyListNode(x);
	//tail->next = newnode;
	//newnode->prev = tail;
	//head->prev = newnode;
	//newnode->next = head;

	ListNodeInsert(head, x);
}

在执行尾插的功能时,我们需要找到链表的尾节点,这里比较方便的就是,因为是双向循环我们可以直接通过头结点一步找到尾节点。然后就可以正常的进行插入的操作。

在我们将pos位置的插入程序完成时,可以将尾插当做在pos为head位置的插入。

链表的打印 

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

由于是循环链表,因此不太好判定什么时候链表结束,我们可以使用 cur->next != head和cur = cur->next,结合来判定是否循环重复走到了头结点。

链表的尾删

void ListNodePopBack(ListNode* head)
{
	//assert(head);
	//assert(head != head->next);
	//ListNode* n_tail = head->prev->prev;
	//free(n_tail->next);
	//n_tail->next = head;
	//head->prev = n_tail;

	//assert(head);
	//assert(head != head->next);
	//ListNode* tail = head->prev;
	//ListNode* tailPrev = tail->prev;
	//tailPrev->next = head;
	//head->prev = tailPrev;
	//free(tail);

	ListNodeErase(head->prev);
}

尾删需要注意的就是我们要记录尾节点前面的一个结点的地址,还有要防止删除的时候将头节点也释放了,要加assert来判断。

链表的头插

void ListNodePushFront(ListNode* head, LTDataType x)
{
	//ListNode* newnode = BuyListNode(x);
	//newnode->next = head->next;
	//head->next->prev = newnode;
	//head->next = newnode;
	//newnode->prev = head;

	ListNodeInsert(head->next, x);
}

在头插中需要注意的是,插入时的顺序如果不记录原先head->next的地址,那么就需要先将d1与newnode进行链接,然后在链接头结点和newnode。 

链表的头删

void ListNodePopFront(ListNode* head)
{
	//assert(head);
	//assert(head != head->next);
	//ListNode* next = head->next->next;
	//free(head->next);
	//head->next = next;
	//next->prev = head;

	//assert(head);
	//assert(head != head->next);
	//ListNode* first = head->next;
	//ListNode* second = first->next;
	//free(first);
	//head->next = second;
	//second->prev = head;

	ListNodeErase(head->next);
}

头删与头插一致都需要记录d1结点的地址来防止链表找不到后面的数据。

链表的查找

ListNode* ListNodeFind(ListNode* head, LTDataType x)
{
	//assert(head);
	//ListNode* cur = head;
	//while (cur->next != head)
	//{
	//	if (cur->next->data == x)
	//	{ 
	//		return cur->next;
	//		printf("已找到");
	//	}
	//	cur = cur->next;
	//}
	//printf("未找到");
	//return NULL;

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

链表在pos之前插入

void ListNodeInsert(ListNode* pos, LTDataType x)
{
	assert(pos);
	ListNode* newnode = BuyListNode(x);
	ListNode* Prev = pos->prev;
	Prev->next = newnode;
	newnode->prev = Prev;
	newnode->next = pos;
	pos->prev = newnode;
}

链表删除pos位置

void ListNodeErase(ListNode* pos)
{
	assert(pos);
	ListNode* Prev = pos->prev;
	ListNode* Next = pos->next;
	Prev->next = Next;
	Next->prev = Prev;
}

删除pos位置时,与之前一样需要记录两边的地址,然后直接相连。 

判断链表是否为空

bool ListNodeEmpty(ListNode* head)
{
	return head == head->next;
}

在C语言中判断是否为空需要添加头文件stdbool.h。

计算链表长度的大小

size_t ListNodeSize(ListNode* head)
{
	assert(head);

	size_t size = 0;
	ListNode* cur = head->next;
	while (cur != head)
	{
		++size;
		cur = cur->next;
	}
	return size;
}

在这里我们计算链表的长度的时候,不应该讲长度的大小放入头结点的数据位中,因为链表中存储的数据不一定是int类型的数据,可能是double或者char又或者是结构体等,这些都会让这个链表出错。

链表的销毁

void ListNodeDestroy(ListNode* head)
{
	assert(head);

	ListNode* cur = head->next;
	while (cur != head)
	{
		ListNode* next = cur->next;
		free(cur);
		cur = next;
	}
	free(head);
	//phead = NULL;
}

整体代码

//List.h
#include "List.h"

ListNode* BuyListNode(LTDataType x)
{
	ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
	if (newnode == NULL)
	{
		perror("malloc failed");
		exit(-1);
	}
	newnode->next = NULL;
	newnode->prev = NULL;
	newnode->data = x;
	return newnode;
}

ListNode* ListNodeInit()
{
	//创建带哨兵位的头结点
	ListNode* guard = BuyListNode(-1);

	//令哨兵位的prev指针与next指针都指向结点本身 - 
	guard->next = guard;
	guard->prev = guard;
	return guard;
}

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

void ListNodePushBack(ListNode* head, LTDataType x)
{
	//assert(head);
	//ListNode* tail = head->prev;
	//ListNode* newnode = BuyListNode(x);
	//tail->next = newnode;
	//newnode->prev = tail;
	//head->prev = newnode;
	//newnode->next = head;

	ListNodeInsert(head, x);
}

void ListNodePopBack(ListNode* head)
{
	//assert(head);
	//assert(head != head->next);
	//ListNode* n_tail = head->prev->prev;
	//free(n_tail->next);
	//n_tail->next = head;
	//head->prev = n_tail;

	//assert(head);
	//assert(head != head->next);
	//ListNode* tail = head->prev;
	//ListNode* tailPrev = tail->prev;
	//tailPrev->next = head;
	//head->prev = tailPrev;
	//free(tail);

	ListNodeErase(head->prev);
}

void ListNodePushFront(ListNode* head, LTDataType x)
{
	//ListNode* newnode = BuyListNode(x);
	//newnode->next = head->next;
	//head->next->prev = newnode;
	//head->next = newnode;
	//newnode->prev = head;

	ListNodeInsert(head->next, x);
}

void ListNodePopFront(ListNode* head)
{
	//assert(head);
	//assert(head != head->next);
	//ListNode* next = head->next->next;
	//free(head->next);
	//head->next = next;
	//next->prev = head;

	//assert(head);
	//assert(head != head->next);
	//ListNode* first = head->next;
	//ListNode* second = first->next;
	//free(first);
	//head->next = second;
	//second->prev = head;

	ListNodeErase(head->next);
}

ListNode* ListNodeFind(ListNode* head, LTDataType x)
{
	//assert(head);
	//ListNode* cur = head;
	//while (cur->next != head)
	//{
	//	if (cur->next->data == x)
	//	{ 
	//		return cur->next;
	//		printf("已找到");
	//	}
	//	cur = cur->next;
	//}
	//printf("未找到");
	//return NULL;

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

void ListNodeInsert(ListNode* pos, LTDataType x)
{
	assert(pos);
	ListNode* newnode = BuyListNode(x);
	ListNode* Prev = pos->prev;
	Prev->next = newnode;
	newnode->prev = Prev;
	newnode->next = pos;
	pos->prev = newnode;
}

void ListNodeErase(ListNode* pos)
{
	assert(pos);
	ListNode* Prev = pos->prev;
	ListNode* Next = pos->next;
	Prev->next = Next;
	Next->prev = Prev;
}

bool ListNodeEmpty(ListNode* head)
{
	return head == head->next;
}

size_t ListNodeSize(ListNode* head)
{
	assert(head);

	size_t size = 0;
	ListNode* cur = head->next;
	while (cur != head)
	{
		++size;
		cur = cur->next;
	}
	return size;
}

void ListNodeDestroy(ListNode* head)
{
	assert(head);

	ListNode* cur = head->next;
	while (cur != head)
	{
		ListNode* next = cur->next;
		free(cur);
		cur = next;
	}
	free(head);
	//phead = NULL;
}
//List.c
#pragma once

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

//定义数据变量类型
typedef int LTDataType;

//创建链表的结点的结构体
typedef struct ListNode
{
	struct ListNode* prev;
	struct ListNode* next;
	LTDataType data;
}ListNode;

//链表结点的初始化
ListNode* ListNodeInit();

//创建链表的结点
ListNode* BuyListNode(LTDataType x);

//链表的尾插
void ListNodePushBack(ListNode* head, LTDataType x);

//链表的尾删
void ListNodePopBack(ListNode* head);

//链表打印
void ListNodePrint(ListNode* head);

//链表头插
void ListNodePushFront(ListNode* head, LTDataType x);

//链表头删
void ListNodePopFront(ListNode* head);

//链表数据查找
ListNode* ListNodeFind(ListNode* head, LTDataType x);

//在pos之前插入
void ListNodeInsert(ListNode* pos, LTDataType x);

//删除pos位置
void ListNodeErase(ListNode* pos);

//链表是否为空
bool ListNodeEmpty(ListNode* head);

//链表大小
size_t ListNodeSize(ListNode* head);

//链表销毁
void ListNodeDestroy(ListNode* head);
//test.c
#include "List.h"

//测试链表初始化
void TestListNode1()
{
	ListNode* head = ListNodeInit();
}

//测试链表尾插、尾删
void TestListNode2()
{
	ListNode* head = ListNodeInit();
	ListNodePushBack(head, 1);
	ListNodePrint(head);
	ListNodePushBack(head, 2);
	ListNodePrint(head);
	ListNodePushBack(head, 3);
	ListNodePrint(head);
	ListNodePushBack(head, 4);
	ListNodePrint(head);
	ListNodePushBack(head, 5);
	ListNodePrint(head);
	ListNodePopBack(head);
	ListNodePrint(head);
	ListNodePopBack(head);
	ListNodePrint(head);
	ListNodePopBack(head);
	ListNodePrint(head);
	ListNodePopBack(head);
	ListNodePrint(head);
	ListNodePopBack(head);
	ListNodePrint(head);

}

//测试链表头插、头删
void TestListNode3()
{
	ListNode* head = ListNodeInit();
	ListNodePushFront(head, 1);
	ListNodePrint(head);
	ListNodePushFront(head, 2);
	ListNodePrint(head);
	ListNodePushFront(head, 3);
	ListNodePrint(head);
	ListNodePushFront(head, 4);
	ListNodePrint(head);
	ListNodePushFront(head, 5);
	ListNodePrint(head);
	ListNodePopFront(head);
	ListNodePrint(head);
	ListNodePopFront(head);
	ListNodePrint(head);
	ListNodePopFront(head);
	ListNodePrint(head);
	ListNodePopFront(head);
	ListNodePrint(head);
	ListNodePopFront(head);
	ListNodePrint(head);

}

//测试查找
void TestListNode4()
{
	ListNode* head = ListNodeInit();
	ListNodePushFront(head, 1);
	ListNodePushFront(head, 2);
	ListNodePushFront(head, 3);
	ListNodePushFront(head, 4);
	ListNodePushFront(head, 5);
	ListNodePrint(head);

	ListNode* ret = ListNodeFind(head, 5);
	ListNodePrint(ret);
}
int main(void)
{
	TestListNode2();
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值