当前位置:   article > 正文

玩转数据结构之双向循环链表_双向链表的应用场景

双向链表的应用场景

一、前言

链表是数据结构中十分重要的内容,我们知道链表的结构是非常多样的

当所有情况组合起来就有8种链表结构:

1.单向或者双向:
在这里插入图片描述

2.带头或者不带头:

在这里插入图片描述

3.循环或者非循环:

在这里插入图片描述

4.最常用的两种结构:

在这里插入图片描述

今天我们就来介绍优势更为明显的带头双向循环链表

在介绍带头双向循环链表之前呢,我们需要先来简单了解一下无头单向非循环链表,从图中我们也可以看出来,该结构较为简单。所以我们一般不会用其单独来存储数据。在实际应用中我们更多的是将其作为其他数据结构的子结构,如哈希桶、图的邻接表等等。在这里多提一句,这种结构在笔试面试中出现的概率是比较高的。

而我们今天要着重讲解的带头双向循环链表,它的结构相对来说是比较复杂的,一般用来单独存储数据。在实际使用链表数据结构,都是带头双向循环链表。虽然这个结构的结构较为复杂,但是在我们真正使用起来的时候,我们会发现它给我们带来了许多优势,反而实现起来更加简单了,这在后文讲解中会有所体现。

二、双向链表的实现

NOTE:老规矩,在这里我们先把所需要用的的库给到大家:

#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdio.h>
  • 1
  • 2
  • 3
  • 4
  • 5

①、定义节点

首先和单向链表一样,我们需要先定义一个节点,大致的实现都是相同的,但由于双向的结构,我们需要额外新增一个新的指针,因为我们需要一个指针指向前面一个指针指向后面,节点的定义具体如下:

typedef int LTDataType;
typedef struct ListNode
{
	struct ListNode* next;
	struct ListNode* prev;
	LTDataType data;
}LTNode;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

②、创建新节点(BuyLTNode)

LTNode* BuyListNode(LTDataType x)
{
	LTNode* node = (LTNode*)malloc(sizeof(LTNode));
	if (node == NULL)
	{
		perror("malloc fail!");
		exit(-1);
	}

	node->next = NULL;
	node->prev = NULL;
	node->data = x;
	return node;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

③、初始化链表(ListInit)

LTNode* ListInit()
{
	LTNode* guard = (LTNode*)malloc(sizeof(LTNode));
	if (guard == NULL)
	{
		perror("malloc fail!");
		exit(-1);
	}

	guard->next = guard;
	guard->prev = guard;

	return guard;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

④、双向链表销毁(ListDestory)

void ListDestory(LTNode* phead)
{
	assert(phead);

	LTNode* cur = phead->next;
	while (cur != phead)
	{
		LTNode* next = cur->next;
		free(cur);
		cur = next;
	}
	free(phead);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

⑤、双向链表打印(ListPrint)

void ListPrint(LTNode *phead)
{
	assert(phead);
	printf("phaed<=>");
	LTNode* cur = phead->next;
	while (cur != phead)
	{
		printf("%d<=>", cur->data);
		cur = cur->next;
	}
	printf("\n");
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

⑥、双向链表尾插(ListPushBack)

void ListPushBack(LTNode* phead, LTDataType x)
{
	assert(phead);

	LTNode* newnode = BuyListNode(x);
	LTNode* tail = phead->prev;

	tail->next = newnode;
	newnode->prev = tail;
	newnode->next = phead;
	phead->prev = newnode;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

⑦、双向链表头插(ListPushFront)

void ListPushFront(LTNode* phead, LTDataType x)
{
	assert(phead);

	LTNode* newnode = BuyListNode(x);
	newnode->next = phead->next;
	phead->next->prev = newnode;

	phead->next = newnode;
	newnode->prev = phead;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

⑧、双链表尾删(ListPopBack)

void ListPopBack(LTNode* phead)
{
	assert(phead);
	assert(!ListEmpty(phead));

	LTNode* tail = phead->prev;
	LTNode* prev = tail->prev;

	prev->next = phead;
	phead->prev = prev;

	free(tail);
	tail = NULL;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

⑨、双链表头删(ListPopFront)

void ListPopFront(LTNode* phead)
{
	assert(phead);
	assert(!ListEmpty(phead));

	LTNode* first = phead->prev;
	LTNode* second = first->next;

	phead->next = second;
	second->prev = phead;

	free(first);
	first = NULL;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

⑩、双向链表pos位置之前插入(ListInsert)

void ListInsert(LTNode* pos, LTDataType x)
{
	assert(pos);

	LTNode* prev = pos->prev;
	LTNode* newnode = BuyListNode(x);

	prev->next = newnode;
	newnode->prev = prev;
	newnode->next = pos;
	pos->prev = newnode;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

⑪、双向链表删除pos位置(ListEarse)

void ListErase(LTNode* pos)
{
	assert(pos);

	LTNode* prev = pos->prev;
	LTNode* next = pos->next;

	prev->next = next;
	next->prev = prev;
	free(pos);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

⑫、双向链表查找(ListFind)

LTNode* ListFind(LTNode* phead, LTDataType x)
{
	assert(phead);

	size_t n = 0;
	LTNode* cur = phead->next;
	while (cur != phead)
	{
		if (cur->data == x)
		{
			return cur;
		}
		cur = cur->next;
	}

	return NULL;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

三、总结

带头双向循环链表是一种较为复杂的就够,但由于其具有的循环特性,会使我们通过代码实现它时反而更为简单,双向链表这个结构的应用场景我们会在下一篇博客里通过oj题目为大家介绍,感谢大家的支持!

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Gausst松鼠会/article/detail/598275
推荐阅读
相关标签
  

闽ICP备14008679号