带头双向循环链表ListNode详解

数据结构-带头结点双向循环链表ListNode详解(增删改查)

1.带头双向循环链表

这种数据结构很大程度上弥补了单链表的缺点,使增加和删除节点的时间复杂度减小,使用头节点也解决了函数传参的二级指针问题。

image-20231219232721793

2头文件以及接口的实现

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
// 带头+双向+循环链表增删查改实现
typedef int LTDataType;
typedef struct ListNode
{
	LTDataType _data;
	struct ListNode* _next;
	struct ListNode* _prev;
}ListNode;

// 创建返回链表的头结点.
ListNode* ListCreate();
// 双向链表销毁
void ListDestory(ListNode* pHead);
// 双向链表打印
void ListPrint(ListNode* pHead);
// 双向链表尾插
void ListPushBack(ListNode* pHead, LTDataType x);
// 双向链表尾删
void ListPopBack(ListNode* pHead);
// 双向链表头插
void ListPushFront(ListNode* pHead, LTDataType x);
// 双向链表头删
void ListPopFront(ListNode* pHead);
// 双向链表查找
ListNode* ListFind(ListNode* pHead, LTDataType x);
// 双向链表在pos的前面进行插入
void ListInsert(ListNode* pos, LTDataType x);
// 双向链表删除pos位置的节点
void ListErase(ListNode* pos);
//在堆空间申请一个节点
ListNode* create(LTDataType x);

2.1在堆空间申请一个节点

动态申请一个sizeof(ListNode)大小的节点并返回节点地址

ListNode* create(LTDataType x) {
	ListNode* listnode =(ListNode*)malloc(sizeof(ListNode));
	listnode->_data = x;
	listnode->_next = NULL;
	listnode->_prev = NULL;
	return listnode;
}

2.2创建并返回链表的头节点

初始化链表,动态申请一个节点,节点的值可以是任意值(搜索的时候会做特殊判断,不会搜索到头节点,头节点的值是无效的),节点的prev和next必须指向自己,并且返回这个节点

ListNode* ListCreate() {
	ListNode* listnode = create(-1);
	listnode->_next = listnode;
	listnode->_prev = listnode;
	return listnode;
}

2.3双向链表的销毁

双向链表中的节点全都是通过malloc创建的,所以在销毁的时候需要全部free掉,但是由于外面仍然有变量指向头节点,所以将全部节点free之后需要exit。防止内存泄露。先free掉节点元素,最后free掉头节点。

void ListDestory(ListNode* pHead) {
	ListNode* listnode = pHead->_next;
	ListNode* tmp = NULL;
	while (listnode!=pHead)
	{

		listnode->_prev->_next = listnode->_next;
		listnode->_next->_prev = listnode->_prev;
		tmp = listnode->_next;
		free(listnode);
		listnode = tmp;
	}
	free(pHead);
	exit(0);
}

2.4双向链表的打印

从head->next节点开始打印,到head节点结束

void ListPrint(ListNode* pHead) {
	ListNode* listnode = pHead->_next;
	while (listnode!=pHead)
	{
		printf("%d<=>", listnode->_data);
		listnode = listnode->_next;
	}
	printf("\n");
}

2.5 双向链表尾插

head->prev就是双向链表的最后一个节点,将新节点的prev指向双向链表的最后一个节点,新节点的next指向head,将没插入前的最后一个节点的next指向新节点,head节点的prev指向新节点。

void ListPushBack(ListNode* pHead, LTDataType x) {
	assert(pHead);
	ListNode* listnode = create(x);
	listnode->_next = pHead;
	listnode->_prev = pHead->_prev;
	pHead->_prev->_next = listnode;
	pHead->_prev = listnode;
}

测试

void test1()
{
	ListNode* plist = ListCreate();
	ListPushBack(plist, 1);
	ListPushBack(plist, 2);
	ListPushBack(plist, 3);
	ListPushBack(plist, 4);
	ListPushBack(plist, 5);
	ListPrint(plist);
	ListDestory(plist);
}

img

2.6双向链表的的尾删

head->prev代表最后一个节点,将这个节点的前一个节点X的next指向head,将head的prev指向X,然后free掉最后一个节点即可

void ListPopBack(ListNode* pHead) {
	assert(pHead->_next != pHead);
	ListNode* listnode = pHead->_prev;
	//printf("%d  \n", listnode->_prev->_next->_data);
	listnode->_prev->_next = pHead;
	pHead->_prev = listnode->_prev;
}

测试

void test2()
{
	ListNode* plist = ListCreate();
	ListPushBack(plist, 1);
	ListPushBack(plist, 2);
	ListPushBack(plist, 3);
	ListPushBack(plist, 4);
	ListPushBack(plist, 5);
	
	/*ListDestory(plist);*/
	ListPopBack(plist);
	ListPopBack(plist);
	ListPrint(plist);
}

image-20231219235748191

2.7双向链表的头插

head->next代表双向链表的第一个元素,在第一个元素前插入,首先将新元素的prev指向head,将新元素的next指向head->next,然后head->next->prev指向新节点,将head->next指向新节点

void ListPushFront(ListNode* pHead, LTDataType x) {
	assert(pHead);
	ListNode* listnode = create(x);
	listnode->_prev = pHead;
	listnode->_next = pHead->_next;
	pHead->_next->_prev = listnode;
	pHead->_next = listnode;
}

test

void ListPushFront(ListNode* pHead, LTDataType x) {
	assert(pHead);
	ListNode* listnode = create(x);
	listnode->_prev = pHead;
	listnode->_next = pHead->_next;
	pHead->_next->_prev = listnode;
	pHead->_next = listnode;
}

image-20231220000102360

2.8双向链表的头删

删除head->next,将head->next->prev指向head,将head->prev指向head->next->next,在将head->next节点free掉

void ListPopFront(ListNode* pHead) {
	assert(pHead);
	assert(pHead != pHead->_next);
	ListNode* listnode = pHead->_next;
	listnode->_next->_prev = pHead;
	pHead->_next = listnode->_next;
	free(listnode);
}

test

void ListPushFront(ListNode* pHead, LTDataType x) {
	assert(pHead);
	ListNode* listnode = create(x);
	listnode->_prev = pHead;
	listnode->_next = pHead->_next;
	pHead->_next->_prev = listnode;
	pHead->_next = listnode;
}

image-20231220000406792

2.9双向链表的查找

从head->next位置开始循环遍历,循环结束条件为node==head,这样不会遍历头节点,所以头节点的值是无效的。找到返回节点,没找到返回NULL。

ListNode* ListFind(ListNode* pHead, LTDataType x) {
	assert(pHead);
	ListNode* listnode = pHead->_next;
	while (listnode!=pHead)
	{
		if (listnode->_data == x)return listnode;
		listnode = listnode->_next;
	}
	return NULL;
}

test

void test5()
{
	ListNode* plist = ListCreate();
	ListPushBack(plist, 1);
	ListPushBack(plist, 2);
	ListPushBack(plist, 3);
	ListPushBack(plist, 4);
	ListPushBack(plist, 5);
	/*ListDestory(plist);*/
	printf("%d", ListFind(plist, 3)->_data);
	
	//ListPrint(plist);
}

image-20231220000904050

2.10双向链表在pos节点前插入

将新节点的next指向pos,新节点prev指向pos->prev,将pos->prev->next指向新节点,将pos->prev指向新节点。

void ListInsert(ListNode* pos, LTDataType x) {
	assert(pos);
	ListNode* newnode = create(x);
	newnode->_prev = pos->_prev;
	newnode->_next = pos;
	pos->_prev->_next = newnode;
	pos->_prev = newnode;
}

test

void test6()
{
	ListNode* plist = ListCreate();
	ListPushBack(plist, 1);
	ListPushBack(plist, 2);
	ListPushBack(plist, 3);
	ListPushBack(plist, 4);
	ListPushBack(plist, 6);
	/*ListDestory(plist);*/
	ListPrint(plist);
	ListInsert(ListFind(plist,1), 7);
	ListInsert(ListFind(plist, 6), 5);
	ListPrint(plist);
}

image-20231220001848089

2.11双向链表删除一个节点

删除节点pos,将pos->prev->next指向pos->next,将pos->next->prev=pos->prev,free掉pos

void ListErase(ListNode* pos) {
	assert(pos);
	pos->_prev->_next = pos->_next;
	pos->_next->_prev = pos->_prev;
	free(pos);
}

test

void test7()
{
	ListNode* plist = ListCreate();
	ListPushBack(plist, 1);
	ListPushBack(plist, 2);
	ListPushBack(plist, 3);
	ListPushBack(plist, 4);
	ListPushBack(plist, 6);
	/*ListDestory(plist);*/
	ListPrint(plist);
	ListErase(ListFind(plist, 1));
	ListErase(ListFind(plist, 3));
	ListErase(ListFind(plist, 6));
	ListPrint(plist);
}

image-20231220002119478

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值