双向链表的实现(带头双向循环链表)

带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都
是带头双向循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带
来很多优势,实现反而简单了。

typedef int LTDataType; //类型可以根据需求更改 更方便

typedef struct ListNode
{
	struct ListNode* prve; //指向前一个节点
	struct ListNode* next; //指向下一个节点
	LTDataType data;       //节点元素
}LTNode;

因为是带头双向循环链表,所以先创建返回链表的头节点哨兵位

//创建返回链表的头节点哨兵位
LTNode* LTCreate()
{
	LTNode* head = malloc(sizeof(LTNode)); //在堆上开辟一块哨兵位节点空间
	if (head == NULL)
	{
		perror(" LTCreate malloc");
		exit(-1);
	}
	head->data = -1;  //可以给上-1
	head->prve = head; //前指针指向自己
	head->next = head; //后指针指向自己
	return head;
}

用后头节点之后我们可以开始创建节点进行尾插:

//双链表尾插
void LTPushBack(LTNode* phead, LTDataType x)
{
	assert(phead);

	LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));
	if (newnode == NULL)    //是否开辟成功
	{
		perror("malloc fail");
		exit(-1);
	}
	newnode->data = x;        // 给值
	newnode->next = NULL;     //新的节点 指针可以置空 在连接时指向正确的位置
	newnode->prve = NULL;

	LTNode* tail = phead->prve; //因为是尾插 双向循环链表哨兵位的前指针指向最后一个节点
                                //很容易就找最后一个节点,不需要像单链表去遍历查找
	tail->next = newnode;       //最后一个节点的next指向新节点
	newnode->prve = tail;       //新节点的prve指向最后一个节点
	newnode->next = phead;      //此时新节点已经是最后一个节点了 
                                // 处理一下新的尾节点的next  新节点的next指向哨兵位
	phead->prve = newnode;      //最后将头节点的prve指向新的节点
}

掌握了尾插之后,实现一下头插:

LTNode* BuyNewNode(LTDataType x) //封装的创建新节点函数
{
	LTNode* node = (LTNode*)malloc(sizeof(LTNode));
	if (node == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}
	node->data = x;
	node->next = NULL;
	node->prve = NULL;
	return node;
}

//双向链表头插
void LTPushFront(LTNode* phead, LTDataType x)
{
	assert(phead);

	LTNode* newnode = BuyNewNode(x); //因为需要头插尾插 将创建新节点封装一下

    //新增头插节点,其实就跟尾插相似,但是需要注意,新节点必须先链接哨兵位的next的节点
	newnode->next = phead->next; 
	phead->next->prve = newnode;
	newnode->prve = phead;
	phead->next = newnode;
    //如果容易出错,可以保存哨兵位的next的节点,如下
    //LTNode* nextnode = head->next;
    //newnode->next = nextnode;
    //nextnode->prve = newnode;
	//newnode->prve = phead;
	//phead->next = newnode;
}

单链表的头插比尾插效率高,而双向链表的头插和尾插效率相同。

实现尾删与头删

bool Empty(LTNode* plist)
{
	assert(plist);

	return plist->next == plist;//哨兵位的next如果指向哨兵位,则为空链表
}

//双链表尾删
void LTPopBack(LTNode* phead)
{
	assert(phead);
	assert(!Empty(phead));  //需要判断链表不为空才能进行操作,创建一个判断函数

	LTNode* tail = phead->prve; //找到需要删除的尾节点

	tail->prve->next = phead;    //尾节点的前一个节点链接头节点
	phead->prve = tail->prve;    //哨兵位的prve指针指向尾节点的前一个节点
	free(tail);                    //释放
	tail = NULL;
}

//双向链表头删
void LTPopFront(LTNode* phead)
{
	assert(phead);
	assert(!Empty(phead)); //判断链表不为空

	LTNode* cur = phead->next; //保存删除的节点

	phead->next->next->prve = phead;    
	phead->next = cur->next;
	free(cur);
	cur = NULL;
}

尾插和头插,尾删和头删,在双向循环链表中效率相同。

实现Find函数:

//双向链表查找
LTNode* LTFind(LTNode* phead, LTDataType x)
{
	assert(phead);

	LTNode* node = phead->next; //遍历查找传参的节点
	while (node != phead)
	{
		if (node->data == x)
		{
			return node;
		}
		node = node->next;
	}
	return NULL;
}

接下来创建配合Find函数使用的INSERT函数,根据查找的POS位置,在POS位置前插入新节点:

//双向链表在pos的前面插入 
void LTInsert(LTNode* pos, LTDataType x)
{            //使用find函数查找
	assert(pos);

	LTNode* newnode = BuyNewNode(x); //创建新节点

	LTNode* cur = pos->prve;    //找到pos的前一个节点
    //插入新节点,将新节点与前后节点指针进行链接
	cur->next = newnode;
	newnode->prve = cur;
	newnode->next = pos;
	pos->prve = newnode;
}

再利用Find,创建Erase函数,删除Pos位置节点;

//双向链表删除pos位置节点
void LTErase(LTNode* pos)
{
	assert(pos);

	LTNode* prve = pos->prve; //找到pos的前一个节点
	LTNode* next = pos->next; //找到pos的后一个节点
	
	prve->next = next; //前后节点链接
	next->prve = prve;
	free(pos);           //删除pos位置
	pos = NULL;
}

最后创建一个DESTROY函数,结束程序前释放空间

//双向链表销毁
void LTDestroy(LTNode* phead)
{
	assert(phead);

	LTNode* cur = phead->next;
	while (cur != phead)
	{
		LTNode* node = cur->next;
		free(cur);
		cur = NULL;
		cur = node;
	}
	free(phead); 
}

---------------------------------------------------------------------------------------------------------------------------------

下面是完整代码:

//List.h

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

typedef int LTDataType;

typedef struct ListNode
{
	struct ListNode* prve;
	struct ListNode* next;
	LTDataType data;
}LTNode;

//创建返回链表的头节点哨兵位
LTNode* LTCreate();

//双向链表销毁
void LTDestroy(LTNode* plist);

//双向链表打印
void LTPrint(LTNode* plist);

//双向链表尾插
void LTPushBack(LTNode* plist, LTDataType x);

//双向链表尾删
void LTPopBack(LTNode* plist);

//双向链表头插
void LTPushFront(LTNode* plist, LTDataType x);

//双向链表头删
void LTPopFront(LTNode* plist);

//双向链表查找
LTNode* LTFind(LTNode* plist, LTDataType x);

//双向链表在pos的前面插入
void LTInsert(LTNode* pos, LTDataType x);

//双向链表删除pos位置节点
void LTErase(LTNode* pos);
//List.c

#include "List.h"

//创建返回链表的头节点哨兵位
LTNode* LTCreate()
{
	LTNode* head = malloc(sizeof(LTNode));
	if (head == NULL)
	{
		perror(" LTCreate malloc");
		exit(-1);
	}
	head->data = -1;
	head->prve = head;
	head->next = head;
	return head;
}

//双链表打印
void LTPrint(LTNode* phead)
{
	assert(phead);
	LTNode* tail = phead->next;
	printf("<=head=>");
	while (tail != phead)
	{
		printf("%d<=>", tail->data);
		tail = tail->next;
	}
	printf("\n");
}

LTNode* BuyNewNode(LTDataType x)
{
	LTNode* node = (LTNode*)malloc(sizeof(LTNode));
	if (node == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}
	node->data = x;
	node->next = NULL;
	node->prve = NULL;
	return node;
}

//双链表尾插
void LTPushBack(LTNode* phead, LTDataType x)
{
	assert(phead);

	LTNode* newnode = BuyNewNode(x);
	LTNode* tail = phead->prve;

	tail->next = newnode;
	newnode->prve = tail;
	newnode->next = phead;
	phead->prve = newnode;
}

bool Empty(LTNode* plist)
{
	assert(plist);

	return plist->next != plist;
}

//双链表尾删
void LTPopBack(LTNode* phead)
{
	assert(phead);
	assert(Empty(phead));

	LTNode* tail = phead->prve;

	tail->prve->next = phead;
	phead->prve = tail->prve;
	free(tail);
	tail = NULL;
}

//双向链表头插
void LTPushFront(LTNode* phead, LTDataType x)
{
	assert(phead);

	LTNode* newnode = BuyNewNode(x);

	newnode->next = phead->next;
	phead->next->prve = newnode;
	newnode->prve = phead;
	phead->next = newnode;
}

//双向链表头删
void LTPopFront(LTNode* phead)
{
	assert(phead);
	assert(Empty(phead));

	LTNode* cur = phead->next;

	phead->next->next->prve = phead;
	phead->next = cur->next;
	free(cur);
	cur = NULL;
}

//双向链表查找
LTNode* LTFind(LTNode* phead, LTDataType x)
{
	assert(phead);
	LTNode* node = phead->next;
	while (node != phead)
	{
		if (node->data == x)
		{
			return node;
		}
		node = node->next;
	}
	return NULL;
}


//双向链表在pos的前面插入
void LTInsert(LTNode* pos, LTDataType x)
{
	assert(pos);

	LTNode* newnode = BuyNewNode(x);
	LTNode* cur = pos->prve;
	cur->next = newnode;
	newnode->prve = cur;
	newnode->next = pos;
	pos->prve = newnode;
}


//双向链表删除pos位置节点
void LTErase(LTNode* pos)
{
	assert(pos);

	LTNode* prve = pos->prve;
	LTNode* next = pos->next;
	
	prve->next = next;
	next->prve = prve;
	free(pos);
	pos = NULL;
}


//双向链表销毁
void LTDestroy(LTNode* phead)
{
	assert(phead);

	LTNode* cur = phead->next;
	while (cur != phead)
	{
		LTNode* node = cur->next;
		free(cur);
		cur = NULL;
		cur = node;
	}
	free(phead);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值