链表之双向链表的实现

铁汁们大家好,我们上一篇博客学习了单链表,这节课让我们继续往深学习,学习一下双线链表,话不多说,我们开始吧!



目录

1.双向链表

2.顺序表和链表的优缺点

3.双向链表的实现


1.双向链表

1.我们要实现的双线链表是带头双向循环链表,它的结构最复杂,一般用在单独的存储数据。我们实际中使用的链表数据结构都是带头双向循环链表。

2.它虽然结构复杂,但是在我们用代码实现过程中,它比单链表简单。

3.相信很多铁汁不清楚双向链表的结构是什么,如下图:

2.顺序表和链表的优缺点

我们在这里总结一下这两种线性表,方便之后的学习。

顺序表:

优点:空间连续,支持随机访问

缺点:中间或前面部分的插入和删除,时间复杂度是O(n);

           增容很不方便,代价较大。

链表:

优点:任意位置的插入删除,时间复杂度为O(1);

           没有增容销耗,按需申请节点空间,不用了直接释放。

缺点:以节点为单位存储,不支持随机访问

3.双向链表的实现

经过上面的铺垫,我们来实现一个带头双向循环链表

List.h文件

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>

typedef int ADataType;
typedef struct ListNode
{
	ADataType data;
	struct ListNode* prev;//双线链表前驱指针
	struct ListNode* next;//后继指针
}LN;

//双向链表初始化
LN* ListInit();
//为节点开辟空间
LN* BuyListCapacity(ADataType x);
//链表的销毁
void ListDestory(LN* phead);
//头插
void ListPushFront(LN* phead, ADataType x);
//尾插
void ListPushBack(LN* phead, ADataType x);
//打印
void ListPrint(LN* phead);
//头删
void ListPopFront(LN* phead);
//尾删
void ListPopBack(LN* phead);
//查找链表中的数据
LN* ListSearch(LN* phead, ADataType x);
//修改找到的数据
void ListModify( LN* pos, ADataType y);
//在这个位置后插入数据
void ListInsert(LN* pos, ADataType x);
//删除这个位置之后的数据
void ListErase(LN* pos);
//判断链表是否为空
bool ListEmpty(LN* phead);

List.c文件

#include"List.h"

//为节点开辟空间
LN* BuyListCapacity(ADataType x)
{
	LN* newnode = (LN*)malloc(sizeof(LN));
	if (newnode == NULL)
	{
		perror("malloc is false!\n");
		exit(-1);
	}
	newnode->data = x;
	newnode->next = NULL;
	newnode->prev = NULL;
	return newnode;
}

//双向链表初始化
LN* ListInit()
{
	//开辟空间
	LN* phead = BuyListCapacity(0);
	//让头节点的前驱和后继都指向自己,是一个循环
	phead->next = phead;
	phead->prev = phead;
	return phead;
}

//链表的销毁
void ListDestory(LN* phead)
{
	assert(phead);
	LN* tail = phead->next;
	while (tail != phead)//链表的头尾相连,当尾等于头时,说明链表空了
	{
		LN* next = tail->next;
		free(tail);
		tail = next;
	}
	free(phead);
	phead = NULL;
}
//头插
void ListPushFront(LN* phead, ADataType x)
{
	assert(phead);
	LN* newnode = BuyListCapacity(x);
	//若这里不使用新的变量来存储原来第一个节点的值,就先链接后,在链接前
	newnode->next = phead->next;
	phead->next->prev = newnode;
	phead->next = newnode;
	newnode->prev = phead;
}
//尾插
void ListPushBack(LN* phead, ADataType x)
{
	assert(phead);
	LN* newnode = BuyListCapacity(x);
	//找到尾,进行插入节点
	LN* tail = phead->prev;
	tail->next = newnode;
	newnode->prev = tail;
	phead->prev = newnode;
	newnode->next = phead;
}
//打印
void ListPrint(LN* phead)
{
	assert(phead);
	LN* tail = phead->next;
	while (tail != phead)
	{
		printf(" %d ", tail->data);
		tail = tail->next;
	}
	printf("\n");
}
//头删
void ListPopFront(LN* phead)
{
	assert(phead);
	//判断链表是否为空,为空则删不了
	if (phead->next == phead)
	{
		printf("List is NULL!\n");
		return;
	}
	//先记录下后一个节点
	LN* first = phead->next;
	LN* second = first->next;
	phead->next = second;
	second->prev = phead;
	free(first);
	first = NULL;
}
//尾删
void ListPopBack(LN* phead)
{
	assert(phead);
	//判断链表是否为空
	if (phead->prev == phead)
	{
		printf("List is NULL!\n");
		return;
	}
	LN* tail = phead->prev;
	LN* prev = tail->prev;
	prev->next = phead;
	phead->prev = prev;
	free(tail);
	tail = NULL;
}
//查找链表中的数据
LN* ListSearch(LN* phead, ADataType x)
{
	assert(phead);
	LN* cur = phead->next;
	while (cur->data != x)
	{
		cur = cur->next;
	}
	if (cur->data == x)
	{
		return cur;
	}
	return NULL;
}
//修改找到的数据
void ListModify( LN* pos, ADataType y)
{
	assert(pos);
	pos->data = y;
}
//在这个位置后插入数据
void ListInsert(LN* pos, ADataType x)
{
	assert(pos);
	LN* newnode = BuyListCapacity(x);
	LN* next = pos->next;
	pos->next = newnode;
	newnode->prev = pos;
	newnode->next = next;
	next->prev = newnode;
}
//删除这个位置之后的数据
void ListErase(LN* pos)
{
	assert(pos);
	LN* cur = pos->next;
	LN* next = cur->next;
	pos->next = next;
	next->prev = pos;
	free(cur);
	cur = NULL;
}
//判断链表是否为空
bool ListEmpty(LN* phead)
{
	assert(phead);
	if (phead->prev == phead || phead->next == phead)
	{
		return true;
	}
	return false;
}

Test.c文件

#include"List.h"
//带头双向循环链表的实现

void Test1()
{
	LN* head = ListInit();
	ListPushFront(head, 33);
	ListPushFront(head, 22);
	ListPushFront(head, 11);
	ListPushBack(head, 4);
	ListPushBack(head, 5);
	ListPushBack(head, 6);
	ListPushBack(head, 7);
	ListPushBack(head, 8);
	ListPushBack(head, 9);
	ListPushBack(head, 10);
	printf("ListNode:> ");
	ListPrint(head);

	ListPopFront(head);
	ListPopBack(head);
	printf("ListNode:> ");
	ListPrint(head);

	LN* pos = ListSearch(head, 7);
	if (pos == NULL)
	{
		printf("Not Find!\n");
	}
	else
	{
		printf("the number is %d\n", pos->data);
		ListModify(pos, 77);
		printf("ListNode:> ");
		ListPrint(head);

		ListInsert(pos, 13);
		ListInsert(pos, 14);
		ListInsert(pos, 15);
		printf("ListNode:> ");
		ListPrint(head);
		ListErase(pos);
		printf("ListNode:> ");
		ListPrint(head);
	}
	if (ListEmpty(head))
	{
		printf("List is NULL!\n");
	}
	else
	{
		printf("List is Notnull!\n");
	}

	ListDestory(head);
	printf("List is disory!\n");
}

int main()
{
	Test1();
	return 0;
}

结果:结果就是这样的,大家可以自己尝试一下!


这就是双向链表的实现,大家还是要自己敲一遍代码,帮助自己更好的掌握知识点。

谢谢铁汁们的支持,咱们下期再见!!!

相关推荐

  1. 双向实现

    2024-04-08 00:38:01       42 阅读
  2. 双向实现

    2024-04-08 00:38:01       37 阅读
  3. C实现双向队列

    2024-04-08 00:38:01       52 阅读
  4. 带头循环双向实现

    2024-04-08 00:38:01       46 阅读
  5. python实现循环双向

    2024-04-08 00:38:01       34 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-04-08 00:38:01       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-04-08 00:38:01       101 阅读
  3. 在Django里面运行非项目文件

    2024-04-08 00:38:01       82 阅读
  4. Python语言-面向对象

    2024-04-08 00:38:01       91 阅读

热门阅读

  1. 第十五题:最大距离

    2024-04-08 00:38:01       64 阅读
  2. 【算法】求平方根 - 二分法/牛顿迭代

    2024-04-08 00:38:01       36 阅读
  3. 专业虚拟社区用户知识共享行为影响因素研究

    2024-04-08 00:38:01       39 阅读
  4. 数据库基础教程 第三版 —建表

    2024-04-08 00:38:01       39 阅读
  5. 谷歌Rust生产力高于C++两倍?

    2024-04-08 00:38:01       38 阅读
  6. 2024.2.17力扣每日一题——N叉树的层序遍历

    2024-04-08 00:38:01       34 阅读
  7. SQL高级教程

    2024-04-08 00:38:01       39 阅读