【C语言简单实现数据结构】带头循环双向链表

【C语言简单实现数据结构】带头循环双向链表

心有所想,日复一日,必有精进



前言

  1. 无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等等。另外这种结构在笔试面试中出现很多。
  2. 带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带来很多优势,实现反而简单了。
    接下来,在实现过程中就会体会到,如果少一个条件实现就会麻烦不少,但这些条件正好实现起来是非常巧妙。

之前已经实现了单链表,具体原理差不多,就不一一赘述:


双向链表接口

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

typedef int ListDataType;

typedef struct ListNode{
	ListDataType data;
	struct ListNode* prev;
	struct ListNode* next;
}ListNode;

//初始化
ListNode* ListInit(void);
//打印
void ListPrint(ListNode* phead);
//头插
void ListPushFront(ListNode* phead, ListDataType x);
//尾插
void ListPushBack(ListNode* phead, ListDataType x);
//尾删
void ListPopBack(ListNode* phead);
//头删
void ListPopFront(ListNode* phead);
//插入pos结点
void ListInsert(ListNode* pos, ListDataType x);
//删除pos结点
void ListErase(ListNode* pos);
//销毁
void ListDestory(ListNode* phead);

具体实现

开辟新结点

//开辟新结点
ListNode*  newNode(ListDataType x){
	ListNode* newNode = (ListNode*)malloc(sizeof(ListNode));
	if ( newNode == NULL){
		perror("malloc fail");
		exit(-1);
	}
	newNode->next = NULL;
	newNode->prev = NULL;
	newNode->data = x;
	return newNode;
}

初始化

//初始化
ListNode*  ListInit(void){
	ListNode* phead = (ListNode*)malloc(sizeof(ListNode));
	phead->next = phead;
	phead->prev = phead;
	return phead;
}

打印

//打印
void ListPrint(ListNode* phead){
	assert(phead);
	ListNode* cur = phead->next;
	printf("guard<=>");
	while (cur != phead){
		printf("%d<=>", cur->data);
		cur = cur->next;
	}
	printf("\n");
}

头插

//头插
void ListPushFront(ListNode* phead, ListDataType x){
	/*assert(phead);
	ListNode* newnode = newNode(x);
	ListNode* cur = phead->next;
	phead->next = newnode;
	newnode->next = cur;
	newnode->prev = phead;
	cur->prev = newnode;*/
	ListInsert(phead->next,x);	
}

尾插

//尾插
void ListPushBack(ListNode* phead, ListDataType x){
	/*assert(phead);
	ListNode* newnode = newNode(x);
	ListNode* tail = phead->prev;
	tail->next = newnode;
	newnode->prev = tail;
	newnode->next = phead;
	phead->prev = newnode;*/
	ListInsert(phead, x);
}

尾删

//尾删
void ListPopBack(ListNode* phead){
	/*assert(phead);
	ListNode* tail = phead->prev;
	phead->prev = tail->prev;
	tail->prev->next = phead;
	free(tail);
	tail = NULL;*/
	ListErase(phead->prev);
}

头删

//头删
void ListPopFront(ListNode* phead){
	/*assert(phead);
	ListNode* cur = phead->next;
	phead->next = cur->next;
	cur->next->prev = phead;
	free(cur);*/
	ListErase(phead->next);
}

查找

//查找
void ListFind(ListNode* phead ,ListDataType x){
	ListNode* cur = phead->next;
	while (cur != phead){
		if (cur->data == x)
			return cur;
		cur = cur->next;
	}
	return -1;
}

在pos结点之前插入结点

//在pos结点之前插入结点
void ListInsert(ListNode* pos, ListDataType x){
	ListNode * newnode = newNode(x);
	ListNode *cur = pos->prev;
	cur->next = newnode;
	newnode->prev = cur;
	newnode->next = pos;
	pos->prev = newnode;
}

删除pos结点

//删除pos结点
void ListErase(ListNode* pos){
	ListNode *pre = pos->prev;
	ListNode *cur = pos->next;
	pre ->next = cur;
	cur->prev = pre;
	free(pos);
}

判空/求链表长度

bool ListEmpty(ListNode* phead)
{
	assert(phead);

	/*if (phead->next == phead)
	return true;
	else
	return false;*/

	return phead->next == phead;
}

size_t ListSize(ListNode* phead)
{
	assert(phead);
	size_t n = 0;
	ListNode* cur = phead->next;
	while (cur != phead)
	{
		++n;
		cur = cur->next;
	}

	return n;
}

销毁

//销毁
// 可以传二级,内部置空头结点
// 建议:也可以考虑用一级指针,让调用ListDestory的人置空  (保持接口一致性)
void ListDestory(ListNode* phead){
	ListNode* cur = phead->data;
	while (cur != phead){
		ListNode* next = cur->next;
		free(cur);
		cur = next;
	}
	free(phead);
}

总结

带头双向循环链表,已经解决了大多数顺序表的缺点,我们对顺序表对链表进行对比:

不同点顺序表链表
存储空间物理上一定连续逻辑上连续,但物理上不一定连续
随机访问支持O(1)不支持:O(N)
任意位置插入或者删除元素可能需要搬移元素,效率低O(N)只需修改指针指向
插入动态顺序表,空间不够时需要扩容没有容量的概念
应用场景元素高效存储+频繁访问任意位置插入和删除频繁
缓存利用率

备注:缓存利用率参考存储体系结构 以及 局部原理性
在这里插入图片描述
全部代码放在了Gitee这个链接上了,有需要的可以当作参考

  • 9
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值