双向带头循环链表

Posted 别碰我的宏定义

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了双向带头循环链表相关的知识,希望对你有一定的参考价值。

双向带头循环链表:
特点,带头,循环,双向,可以在任意位置插入,除了初始化和销毁需要修改头节点的内容外,其他时候不需要动头指针。
大概就是下面这么个玩意,图画的有点丑,在这里插入图片描述
根据这个图就可以看出这个链表的基本组成结构了,两个指针域一个数据域,类型完成定义了就将创建节点的函数也一定定义好
在这里插入图片描述

typedef int LTDataType;
typedef struct ListNode
{
	LTDataType data;
	struct ListNode* next;
	struct ListNode* prev;
}ListNode;
// 创建链表节点
ListNode* ListCreate(LTDataType date)
{
	ListNode *node = (ListNode *)malloc(sizeof(ListNode));
	if (node == NULL)
	{
		printf("节点申请失败\\n");
		assert(0);
	}
	node->next = NULL;
	node->prev = NULL;
	node->data = date;
	return node;
}

基本的类型定义完成后,就需要来初始化了,先把头节点创好
在这里插入图片描述
注意头节点的两个指针,不能指向空,在没有元素的时候指向本身
,需要让链表循环起来

//初始化头节点
void ListInit(ListNode **Head)
{
	assert(Head != NULL);
	*Head = ListCreate(0);
	(*Head)->next = *Head;
	(*Head)->prev = *Head;
}

头插添加新节点:有头节点了,接下来就要实现他的一些关于链表的基本功能了
先把申请的节点挂在链表上,再动链表的指针,不然指针就可能玩飞了。
在这里插入图片描述
然后将node的next的prev指针指向node,后一个节点的前指针就挂好了,再将前一个指针的后一个指针挂好,节点添加即可完成。
在这里插入图片描述

void ListPushFront(ListNode* pHead, LTDataType x)
{
	assert(pHead != NULL);
	ListNode *node = ListCreate(x);
	node->prev = pHead;
	node->next = pHead->next;
	node->next->prev = node;
	pHead->next = node;
}

尾插肯定少不了和头插一样也是先挂链,再动原链表的指针
在这里插入图片描述

// 双向链表尾插
void ListPushBack(ListNode* pHead, LTDataType x)
{
	assert(pHead != NULL);
	ListNode *node = ListCreate(x);
	node->prev = pHead->prev;
	node->next = pHead;
	node->prev->next = node;
	pHead->prev = node;
	
}

插入完了,调一个打印函数来看看是不是成功插入,注意,犹豫期是循环链表,遍历链表,开始的元素是头节点的下一个,结束的元素是头节点

// 双向链表打印
void ListPrint(ListNode* pHead)
{
	assert(pHead != NULL);
	ListNode *node = pHead->next;
	if (node == pHead)
	{
		printf("链表已空,无元素\\n");
		return ;
	}
	while (node!= pHead)
	{
		printf("%d ", node->data);
		node = node->next;
	}
	printf("\\n");
}

尾删链表元素:1.判空,为空则不可操作,2.动态申请的内存必须要free掉
在这里插入图片描述

// 双向链表尾删
void ListPopBack(ListNode* pHead)
{
	assert(pHead != NULL);
	ListNode *node = pHead->prev;
	if (node == pHead)
	{
		printf("链表已空,不可操作\\n");
		return;
	}
	pHead->prev = node->prev;
	node->prev->next = pHead;
	free(node);
}

双向链表头删链表元素:1.判链表空(头指针的next和prev指针是否都指向自己),为空则不可操作,2.动态申请的内存必须要free掉
在这里插入图片描述

// 双向链表头删
void ListPopFront(ListNode* pHead)
{
	assert(pHead != NULL);
	ListNode *node = pHead->next;
	if (node == pHead)
	{
		printf("链表已空,不可操作\\n");
		return;
	}
	pHead->next = node->next;
	node->next->prev = node->prev;
	free(node);
}

双向循环链表查找

// 双向链表查找
ListNode* ListFind(ListNode* pHead, LTDataType x)
{
	assert(pHead != NULL);
	ListNode *node = pHead->next;
	if (node == pHead)
	{
		printf("没有元素,不可查找\\n");
		return NULL;
	}
	while (node != pHead)
	{
		if (node->data == x)
			return node;
		node = node->next;
	}
	return NULL;
}
void display(ListNode *node)
{
	if (node)
		printf("%d\\n", node->data);
	else
		printf("Not Find\\n");
}

双向循环链表的某个位置元素删除,单向的只能删除其位置之后的元素。双向的就可以删除本位置的元素。

// 双向链表删除pos位置的节点
void ListErase(ListNode* pos)
{
	assert(pos != NULL);
	pos->prev->next = pos->next;
	pos->next->prev = pos->prev;
	free(pos);
}

把他们整理到一起
#LTNode_H_

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

// 创建返回链表的头结点.
ListNode* ListCreate();
//带头双相链表的初始化
void ListInit(ListNode **Head);
// 双向链表销毁
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);
//节点查找结果显示
void display(ListNode *node);
void test2();

附上功能模块代码

#include"HTNode.h"
// 创建链表节点
ListNode* ListCreate(LTDataType date)
{
	ListNode *node = (ListNode *)malloc(sizeof(ListNode));
	if (node == NULL)
	{
		printf("节点申请失败\\n");
		assert(0);
	}
	node->next = NULL;
	node->prev = NULL;
	node->data = date;
	return node;
}
//初始化头节点
void ListInit(ListNode **Head)
{
	assert(Head != NULL);
	*Head = ListCreate(0);
	(*Head)->next = *Head;
	(*Head)->prev = *Head;
}
// 双向链表销毁
void ListDestory(ListNode **Head)
{
	assert((*Head) != NULL);
	ListNode *node = (*Head)->next;
	while (node != (*Head))
	{
		ListPopFront(*Head);
		node = (*Head)->next;
	}
	free(*Head);
}
// 双向链表打印
void ListPrint(ListNode* pHead)
{
	assert(pHead != NULL);
	ListNode *node = pHead->next;
	if (node == pHead)
	{
		printf("链表已空,无元素\\n");
		return ;
	}
	while (node!= pHead)
	{
		printf("%d ", node->data);
		node = node->next;
	}
	printf("\\n");
}
// 双向链表头插
void ListPushFront(ListNode* pHead, LTDataType x)
{
	assert(pHead != NULL);
	ListNode *node = ListCreate(x);
	node->prev = pHead;
	node->next = pHead->next;
	node->next->prev = node;
	pHead->next = node;
}
// 双向链表头删
void ListPopFront(ListNode* pHead)
{
	assert(pHead != NULL);
	ListNode *node = pHead->next;
	if (node == pHead)
	{
		printf("链表已空,不可操作\\n");
		return;
	}
	pHead->next = node->next;
	node->next->prev = node->prev;
	free(node);
}
// 双向链表尾插
void ListPushBack(ListNode* pHead, LTDataType x)
{
	assert(pHead != NULL);
	ListNode *node = ListCreate(x);
	node->prev = pHead->prev;
	node->next = pHead;
	node->prev->next = node;
	pHead->prev = node;
}
// 双向链表尾删
void ListPopBack(ListNode* pHead)
{
	assert(pHead != NULL);
	ListNode *node = pHead->prev;
	if (node == pHead)
	{
		printf("链表已空,不可操作\\n");
		return;
	}
	pHead->prev = node->prev;
	node->prev->next = pHead;
	free(node);
}
// 双向链表查找
ListNode* ListFind(ListNode* pHead, LTDataType x)
{
	assert(pHead != NULL);
	ListNode *node = pHead->next;
	if (node == pHead)
	{
		printf("没有元素,不可查找\\n");
		return NULL;
	}
	while (node != pHead)
	{
		if (node->data == x)
			return node;
		node = node->next;
	}
	return NULL;
}
void display(ListNode *node)
{
	if (node)
		printf("%d\\n", node->data);
	else
		printf("Not Find\\n");
}
// 双向链表在pos的前面进行插入
void ListInsert(ListNode* pos, LTDataType x)
{
	assert(pos);
	ListNode *node = ListCreate(x);
	node->prev = pos->prev;
	node->next = pos;
	pos->prev->next = node;
	pos->prev = node;
}
// 双向链表删除pos位置的节点
void ListErase(ListNode* pos)
{
	assert(pos != NULL);
	pos->prev->next = pos->next;
	pos->next->prev = pos->prev;
	free(pos);
}

void test2()
{
	ListNode *Head;
	ListInit(&Head);
	ListPushBack(Head, 10);
	ListPushBack(Head, 9);
	ListPushBack(Head, 8);
	ListPushBack(Head, 7);
	ListPushBack(Head, 6);
	ListPrint(Head);
	display(ListFind(Head, 10));
	display(ListFind(Head, 12));
	ListInsert(ListFind(Head, 10), 12);
	ListPrint(Head);
	ListErase(ListFind(Head, 12));
	ListPrint(Head);
	ListPopBack(Head);
	ListPrint(Head);
	ListPopBack(Head);
	ListPrint(Head);
	ListPopBack(Head);
	ListPrint(Head);
	ListPopBack(Head);
	ListPrint(Head);
	ListPopBack(Head);
	ListPrint(Head);
	ListPopBack(Head);
	ListPushFront(Head, 10);
	ListPushFront(Head, 9);
	ListPushFront(Head, 8);
	ListPushFront(Head, 7);
	ListPushFront(Head, 6);
	ListPrint(Head);
	ListDestory(&Head);
}

贴上主函数

#include"HTNode.h"
int main()
{
	//test1();
	test2();
	system("pause");
	return 0;
}

有不完善的地方,希望大家多多指点。

以上是关于双向带头循环链表的主要内容,如果未能解决你的问题,请参考以下文章

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

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

C语言实现双向非循环链表(带头结点尾结点)的节点插入

C语言带头双向循环链表增删改查的实现

C语言带头双向循环链表增删改查的实现

数据结构:如何用C语言快速实现带头双向循环链表