带头结点的双向循环链表及相应的操作

Posted 蚍蜉撼树谈何易

tags:

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

双向循环链表与单链表的区别:
1.单链表中仅含有一个指针域,便是next(后继元素)指针域。而双向循环链表中有两个指针域,一个指向前驱(pre),一个指向后继(next)。
2.单链表是以指针走到NULL值作为链表遍历结束条件的,双向循环链表是以从head->next开始访问,直到该指针重新指向head时,循环结束。
3.单链表在删除和插入时只可在传入位置的下一个位置进行执行插入与删除,因为其没有前驱结点,所以不可对传入的结点进行头插(传入结点之前),或者是当前位置元素删除。但双向循环链表可以这样做,因为其有一个pre指针域。
双向循环链表的作用
** 由于它良好的对称性,使得对某个结点的前后结点的操作,带来了方便,可以有效的提高算法的时间性能,通俗的讲,就是以空间换时间。**
双向链表示例图:
在这里插入图片描述
双向循环链表相关函数说明:

.h中结构体相应的定义

#pragma once
typedef int eletype;
struct dlist
{
	eletype val;
	struct dlist* prev;//记录的是结点的前一个元素
	struct dlist* next;//记录的是结点的后一个元素
};
typedef struct dlist* LinkNode;
typedef struct dlist Node;

首先先看头结点创建

//初始化头结点
LinkNode init();
//因为头结点也要有空间,所以在my_malloc函数中先开辟出该部分空间
LinkNode my_malloc(eletype data)
{
	LinkNode newnode = (LinkNode)malloc(sizeof(Node));
	if (newnode == NULL)
	{ 
		printf("内存开辟不成功\\n");
		return;
	}
	newnode->val = data;
	newnode->prev = NULL;
	newnode->next = NULL;
	return newnode;
}
LinkNode init()
{
	LinkNode pheader = my_malloc(0);//此时返回新开辟结点
	pheader->prev = pheader;//相比于单链表,此时在仅含有头结点的情况下,此时pheader->prev与pheader->next都应指向它自身。
	pheader->next = pheader;
	return pheader;//返回头结点,方便后续函数调用
}

接下来看任意位置的插入

void insert_num(LinkNode pheader, eletype data);
void insert_num(LinkNode pos, eletype data)
{
	LinkNode newnode = NULL;
	if (pos == NULL)
	{
		return;
	}
	newnode = my_malloc(data);
	newnode->next = pos;
	newnode->prev = pos->prev;
	newnode->prev->next = newnode;
	pos->prev = newnode;

}

在这里插入图片描述
链表的头插与尾插

//此时可以调用上面提到的任意位置的插入,因为插入是在当前元素之前的
//。所以想要在头插的话,就传的是header->next,同理尾插的话,传入的是pheader,因为pheader->pre就是对应的尾部。
void pushfront(LinkNode header, eletype data)
{
	insert_num(header->next, data);//
}
void pushback(LinkNode pheader, eletype data)
{
	insert_num(pheader, data);

}

链表的元素的删除:

void delete_num(LinkNode pos)
{
	if (pos == NULL)
	{
		return;
	}
	pos->prev->next = pos->next;
	pos->next->prev = pos->prev;
	free(pos);
}
//执行的是头删的操作
void popfront(LinkNode pheader)
{
	delete_num(pheader->next);
}
//尾删
void popback(LinkNode pheader)
{
	delete_num(pheader->prev);
}

在这里插入图片描述
统计双向链表的有效元素个数

int cal_num(LinkNode pheader)
{
	if (pheader == NULL || pheader->next == pheader)
	{
		return 0;
	}
	LinkNode cur = pheader->next;
	int count = 0;
	while (cur != pheader)
	{
		count++;
		cur = cur->next;
	}
	return count;
}

在这里插入图片描述
查找某一元素出现的位置

LinkNode search(LinkNode pheader,eletype data)
{
	if (pheader == NULL || pheader->next == pheader)
	{
		return NULL;
	}
	LinkNode cur = pheader->next;//从第一个有效元素开始
	while (cur != pheader)
	{
		if (cur->val == data)
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}

修改元素的值

void  rectify(LinkNode p, eletype olddata, eletype newdata)
{
	LinkNode recitf_num=search(p,olddata);//利用search函数先找到该元素,在recitf_num不为空的情况下
	if (recitf_num)
	{
		recitf_num->val = newdata;
	}
	else
	{
		printf("元素不存在,修改失败\\n");
		return;
	}
}

链表的打印

void print(LinkNode pheader)
{
	if (pheader == NULL || pheader->next == pheader)
	{
		printf("链表无元素\\n");
		return;
	}
	LinkNode cur = pheader->next;
	LinkNode pnext = NULL;
	while (cur != pheader)
	{
		pnext = cur->next;
		printf("%d\\t", cur->val);
		cur = pnext;
	}
	printf("\\n");
}

链表的清空:(清空与销毁区别):清空是做的是除头结点之外全部删除,销毁是将pheader也同时删除掉

//链表的清空
void clean(LinkNode pheader)
{
	if (pheader == NULL || pheader->next == pheader)
	{
		return;
	}
	LinkNode cur = pheader->next;
	LinkNode* pnext = NULL;
	while (cur != pheader)
	{
		pnext = cur->next;
		free(cur);
		cur = pnext;
	}
	pheader->next = pheader;
	pheader->prev = pheader;

}

链表的销毁

//链表的销毁
void destory(LinkNode pheader)
{
	if (pheader == NULL)
	{
		return;
	}
	else
	{
		clean(pheader);//因为避免了用户的错误销毁,只销毁头结点,并未对后续结点销毁,造成内存的泄露
		free(pheader);
		pheader->next = NULL;
		pheader->prev = NULL;
	}
}

//测试文件

void test()
{
	LinkNode pheader = init();
	pushfront(pheader, 1);
	pushfront(pheader, 2);
	pushfront(pheader, 3);
	pushfront(pheader, 4);
	pushfront(pheader, 5);
	
	print(pheader);
	rectify(pheader, 5, 6);
	print(pheader);
	printf("当前元素个数为%d\\n", cal_num(pheader));
	LinkNode find = search(pheader, 4);
	printf("查找结果为%d\\n", find->val);
	popfront(pheader);
	popfront(pheader);
	popfront(pheader);
	print(pheader);
	pushback(pheader, 6);
	pushback(pheader, 7);
	print(pheader);
	popback(pheader);
	popback(pheader);
	popback(pheader);
	print(pheader);
	clean(pheader);
	print(pheader);
}

双向循环链表相对于单链表来说操作更加简单、多变,其核心思想是以空间换时间。其操作也更加多变。

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

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

java实现单向循环链表

超实用链表(带头节点双向循环链表)

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

《链表》之带头双向循环链表

数据结构之 带头双向循环链表的增删查改