双向带头循环链表

今天去考了科一 好久没学习了呜呜呜感觉有点忘了 !!写篇博客回忆一下-------------------------------

双向循环链表

顾名思义链表的一种,也叫双链表,他的每个数据节点中有两个指针 一个指向直接后继,一个指向后继前驱。

如图:👇👇👇

 所以 在定义双向链表时 在一个节点中需要存储两个指针,一个next 保存下一个节点的地址,prev保存上一个节点的指针。另外,头指针要的prev指向最后一个节点,最后一个节点的next指向头节点。(头节点也可以称作哨兵位,不存储有效数据)

从增删查改的角度来实现双向带头循环链表;

接口如下:

#pragma once

#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdlib.h>
#include <stdbool.h>
typedef int LTDataType;
typedef struct ListNode
{
	LTDataType _data;
	struct ListNode* _next;
	struct ListNode* _prev;
}ListNode;
ListNode* BuyListNode(LTDataType x);
// 创建返回链表的头结点.
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);

代码的实现:

1、创建一个要复用的创建节点的函数以及链表的初始化:

在初始化中,链表的头指向自己 尾也指向自己

ListNode* BuyListNode(LTDataType x)
{
	ListNode* Node = (ListNode*)malloc(sizeof(ListNode));
	if (Node == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}
	Node->_data = x;
	Node->_next = NULL;
	Node->_prev = NULL;

	return Node;
}

ListNode* ListCreate()
{
	ListNode* Phead = BuyListNode(-1);
	Phead->_next = Phead;
	Phead->_prev = Phead;

	return Phead;
}

2、链表的头插和尾插

//尾插
void ListPushBack(ListNode* pHead, LTDataType x)
{
	assert(pHead);
	ListNode* newnode = BuyListNode(x);
//哨兵位的prev指向的就是最后一个节点的指针 链接在后面
	ListNode* Tail = pHead->_prev;
	Tail->_next = newnode;
	newnode->_prev = Tail;
	newnode->_next = pHead;
	pHead->_prev = newnode;
}
//头插
void ListPushFront(ListNode* pHead, LTDataType x)
{
	ListNode* newnode = BuyListNode(x);
//保存原来节点的地址
	ListNode* Next = pHead->_next;
//将新节点链接在哨兵位头节点之后
	newnode->_next = Next;
	Next->_prev = newnode;

	pHead->_next = newnode;
	newnode->_prev = pHead;

}

3、头删和尾删

//尾删
void ListPopBack(ListNode* pHead)
{
	assert(pHead);
	assert(pHead->_next != pHead);

	ListNode* Tail = pHead->_prev;
	ListNode* Prev = Tail->_prev;

	Prev->_next = pHead;
	pHead->_prev = Prev;

	free(Tail);

}

//头删
void ListPopFront(ListNode* pHead)
{
	assert(pHead);
	assert(pHead->_next != pHead);

	ListNode* Next = pHead->_next;
	ListNode* Listing = Next->_next;
	pHead->_next = Listing;
	Listing->_prev = pHead;

	free(Next);
}

4、链表的查找

//查找
ListNode* ListFind(ListNode* pHead, LTDataType x)
{
	assert(pHead);
	
	ListNode* cur = pHead->_next;
	while (cur != pHead)
	{
		if (cur->_data == x)
		{
//找到则返回该节点
			return cur;
		}
		
		cur = cur->_next;
	}
	return NULL;
	
}

5、可以将上查找节点的地址存储在pos中 后在pos的前后进行插入 删除操作

//在pos位置插入
void ListInsert(ListNode* pos, LTDataType x)
{
	ListNode* newnode = BuyListNode(x);

	ListNode* Prev = pos->_prev;
	Prev->_next = newnode;
	newnode->_prev = Prev;

	newnode->_next = pos;
	pos->_prev = newnode;

}

//在pos位置删除
void ListErase(ListNode* pos)
{

	assert(pos);
	ListNode* Prev = pos->_prev;
	ListNode* Next = pos->_next;
	Prev->_next = Next;
	Next->_prev = Prev;
	free(pos);
}

效果:

 6、链表的打印

void ListPrint(ListNode* pHead)
{
	ListNode* cur = pHead->_next;
	//由于是循环链表 当cur走到phead(哨兵位)时 即走完一次链表,为停止条件
	while (cur != pHead)
	{
		printf("%d ", cur->_data);
		cur = cur->_next;
	}
	printf("\n");
}

相对于双向带头链表来说 优缺点有如下👇

优点:

(1)可以在任意位置插入删除 高效方便;时间复杂度O(1);

(2)可以按需要申请空间

缺点:

(1)不支持随机访问

(2)快排、二分查找的方法不合适

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值