数据结构:线性表之-循环双向链表(万字详解)

这篇具有很好参考价值的文章主要介绍了数据结构:线性表之-循环双向链表(万字详解)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

基本概念

1,什么是双向链表

2,与单向链表的区别

双向链表详解

功能展示:

1. 定义链表

2,创建双向链表

3,初始化链表

4,尾插

5,头插

6,尾删

判断链表是否被删空

尾删代码

7,头删

8,pos位置之前插入

优化后的头插

优化后的尾插

9,删除pos位置的节点

优化后的尾删

优化后的头删

10,求链表长度

11,查找链元素

12,销毁链表

成品展示

List.h

List.c

test.c


本文将以写代码思路进行讲述,故中间会出现代码的优化以便梳理思路,渐入佳境

本文分成三个文件:

List.h//函数的声明
List.c//函数的创建
test.c //用于测试文件

基本概念

1,什么是双向链表

双向链表(Doubly Linked List)是一种常见的链表数据结构。它与普通链表的区别在于,每个节点都有两个指针,一个指向前一个节点,一个指向后一个节点,因此可以从任意一个节点开始,双向遍历整个链表。

双向链表的节点通常由三部分组成:数据部分(存储节点的值)、前驱指针(指向前一个节点的指针)和后继指针(指向后一个节点的指针)。相比于单向链表,双向链表可以更方便地进行正反向遍历和节点的插入、删除操作。

双向链表的优点是在某些特定场景下可以更高效地操作链表,缺点是相比于单向链表,需要额外的空间来存储每个节点的前驱指针。

继续遍历一下双向链表的特点和操作:

  1. 特点:

    • 双向链表每个节点有两个指针,可以向前或向后遍历。
    • 可以从任意节点开始向前或向后遍历整个链表。
    • 插入和删除节点的操作相对容易,只需要调整节点的前驱和后继指针即可。
    • 对于双向链表,可以更轻松地实现双向循环链表,即首尾节点相连。
  2. 常见操作:

    • 遍历链表:可以从头节点开始,按照后继指针依次访问每个节点。或者从尾节点开始,按照前驱指针依次访问每个节点。
    • 插入节点:在给定位置之前或之后插入一个新节点,只需要调整前后节点的指针即可。
    • 删除节点:删除给定位置上的节点,同样通过调整前后节点的指针来实现。
    • 查找节点:可以通过遍历链表来查找特定值或者特定位置上的节点。

注意:在使用双向链表时需要注意指针的正确性和更新,以避免出现指针丢失和内存泄漏等问题。

2,与单向链表的区别

双向链表与单向链表的主要区别在于节点中指针的个数和指向方式:

1. 指针个数:双向链表每个节点有两个指针,分别指向前一个节点和后一个节点;而单向链表每个节点只有一个指针,指向下一个节点。
2. 遍历方式:双向链表可以从任意节点开始向前或向后遍历整个链表,而单向链表只能从头节点开始顺序遍历。
3. 插入和删除操作:双向链表的插入和删除操作相对方便,因为可以直接调整前驱和后继节点的指针;而单向链表在进行插入和删除时,需要更多的操作来调整节点之间的链接关系。
4. 空间占用:双向链表相较于单向链表需要额外的空间来存储每个节点的前驱指针。这在一些特定场景下可能会带来一定的开销。
5. 双向性:双向链表可以在节点内部通过前驱指针和后继指针来实现双向的遍历和操作。这使得在某些场景下,双向链表更加方便和高效,例如需要反向遍历链表或者需要在给定节点处进行插入和删除操作。
6. 双向循环性:双向链表可以通过连接首尾节点的前驱和后继指针来形成一个双向循环链表。这使得链表在某些场景下更具灵活性,例如需要循环遍历链表或者实现循环队列等。
7. 内存占用:相对于单向链表,每个节点额外存储一个前驱指针和一个后继指针,使得双向链表在存储上需要更多的内存空间。这是一个需要考虑的因素,尤其是在链表节点数量较大或内存有限的情况下。
8. 双向链表的前向遍历与后向遍历:由于双向链表具有前驱和后继指针,可以方便地进行正向和反向的遍历操作。而单向链表只能从头节点开始进行顺序遍历,无法直接进行反向遍历。
9.  删除操作的效率:相对于单向链表,在双向链表中删除给定节点的效率更高。由于双向链表可以直接通过前驱和后继指针找到目标节点的前后节点,所以删除操作只需要修改前后节点的指针即可,而单向链表需要遍历找到目标节点的前一个节点,才能进行删除操作。
10.  双向链表支持双向迭代器:由于双向链表具有双向性,可以轻松地实现双向迭代器。这使得在某些情况下,可以更加方便地进行双向迭代操作,如双向搜索算法或需要反向遍历的数据结构。
总之,双向链表相较于单向链表具有更强的遍历和操作能力,但在存储上需要额外的空间开销。根据具体需求和场景的不同,选择适合的链表类型是根据实际情况进行权衡和取舍。

概述:

  1. 无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结
    构的子结构,如哈希桶、图的邻接表等等。另外这种结构在笔试面试中出现很多。
  2. 带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都
    是带头双向循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带
    来很多优势,实现反而简单了,后面我们代码实现了就知道了。

单向链表(无头)详细讲解:# 数据结构:线性表之-单向链表(无头)

双向链表详解

功能展示:

typedef int LTDataType;
typedef struct ListNode
{
	struct ListNode* next;
	struct ListNode* prev;
	LTDataType data;
}LTNode;

LTNode* BuyListNode(LTDataType x);

//void ListInit(LTNode** pphead);
LTNode* ListInit();

//打印
void ListPrint(LTNode* phead);

//尾插
void ListPushBack(LTNode* phead, LTDataType x);

//头插
void ListPushFront(LTNode* phead, LTDataType x);

//尾删
void ListPopBack(LTNode* phead);

//头删
void ListPopFront(LTNode* phead);

//判断链表是否被删空
bool ListEmptyLTNode(LTNode* phead);

//pos位置之前插入
void ListInsert(LTNode* pos, LTDataType x);

//删除pos位置的节点
void ListErase(LTNode* pos);

//求链表长度
int ListSize(LTNode* phead);

//销毁链表
void ListDestory(LTNode* phead);

1. 定义链表

typedef struct ListNode
{
	struct ListNode* next;//下一节点
	struct ListNode* prev;//上一个节点
	LTDataType data;
}LTNode;

2,创建双向链表

LTNode* BuyListNode(LTDataType x)
{
	//计算连自定义链表大小
	LTNode* node = (LTNode*)malloc(sizeof(LTNode));
	if (node == NULL)
	{
		//判断是否创建成功
		perror("malloc fail");
		exit(-1);
	}
	//对前节点进行赋值
	node->data = x;
	node->next = NULL;
	node->prev = NULL;
	return node;
}

3,初始化链表

初始化链表使用哨兵位(头节点)前后指针存储的位置为phead本身。数据结构:线性表之-循环双向链表(万字详解),数据结构,数据结构,链表,c语言

LTNode* ListInit()
{
	LTNode* phead = BuyListNode(-1);
	phead->next = phead;
	phead->prev = phead;
}

当然用无返回值的函数用二级指针进行初始化,及后续的增删查改
这样会更麻烦,本文全程采用一级指针:

void ListInit(LTNode** pphead)
{
	*pphead = BuyListNode(-1);
	(*pphead)->next = *pphead;
	(*pphead)->prev = *pphead;
}

改变结构体要用结构体指针。
要改变结构体指针要用结构体指针的指针。

4,尾插

先让我们回顾下双向链表的结构:

数据结构:线性表之-循环双向链表(万字详解),数据结构,数据结构,链表,c语言
与单链表的不同是:单链表要尾插时,要从头一次向后访问找到为节点再进行插入
,而双向链表要尾插在head前插入节点即为尾节点。数据结构:线性表之-循环双向链表(万字详解),数据结构,数据结构,链表,c语言

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

	LTNode* newnode = BuyListNode(x);
	LTNode* tail = phead->prev;
	//前一个节点指向新节点
	tail->next = newnode;
	//新节点的prev指向上一个节点
	newnode->prev = tail;
	//新节点存放的下一节点为头节点
	newnode->next = phead;
	//头节点的prev存放新节点,方便尾插
	phead->prev = newnode;
}

5,头插

头部插入为head前一个节点数据结构:线性表之-循环双向链表(万字详解),数据结构,数据结构,链表,c语言

void ListPushFront(LTNode* phead, LTDataType x)
{
	assert(phead);
	
	LTNode* newnode = BuyListNode(x);
	LTNode* next = phead->next;
//尾部标号对应图中步骤
	next->prev = newnode;  //1
	newnode->next = next;  //2
	newnode->prev = phead; //3
	phead->next = newnode; //4
//另一种写法
	//phead->next = newnode;
	//newnode->prev = phead;
	//newnode->next = next;
	//next->prev = newnode;
}

6,尾删

尾删即为删除head的前一个节点
切记不可先将head指向尾删节点NULL,否则有可能造成野指针。
还有一种情况,若链表已经被删空,则会出现错误,则应该对删空时进行提醒并终止程序:

判断链表是否被删空

bool ListEmptyLTNode(LTNode* phead)
{
	assert(phead);
	/*
		链表返回只剩头节点(链表已经被删空)为真
		否则为假
	*/
	return phead->next == phead;
}

尾删代码

void ListPopBack(LTNode* phead)
{
	assert(phead);
	//判断链表是否被删空:为真即为假,为假即为真
	assert(!ListEmptyLTNode(phead));//assert(phead->next != phead);

	//head的前一个即为尾巴
	LTNode* tail = phead->prev;//head前一个节点
	LTNode* tailPrev = tail->prev;//tail的前一个节点

	free(tail);//删除节点

	//重新链接新尾节点
	tailPrev->next = phead;
	phead->prev = tailPrev;
}

7,头删

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

	LTNode* next = phead->next;
	LTNode* nnext = next->next;

	free(next);//删除节点

	phead->next = nnext;
	nnext->prev = phead;
}

8,pos位置之前插入

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

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

	//prev newnode pos
	prev->next = newnode;
	newnode->prev = prev;
	newnode->next = pos;
	pos->prev = newnode;
}

此时既然已经做到了可以任意位置插入,
那就可以对头插尾插进行复用,简化代码

优化后的头插

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

	ListInsert(phead->next, x);
}

优化后的尾插

void ListPopBack(LTNode* phead)
{
	assert(phead);
	//判断链表是否被删空:为真即为假,为假即为真
	assert(!ListEmptyLTNode(phead));//assert(phead->next != phead);
	
	ListErase(phead->prev);
}

9,删除pos位置的节点

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

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

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

	free(pos);
}

此时既然已经做到了可以任意位置插入,
那就可以对头插尾插进行复用,简化代码

优化后的尾删

void ListPopBack(LTNode* phead)
{
	assert(phead);
	//判断链表是否被删空:为真即为假,为假即为真
	assert(!ListEmptyLTNode(phead));//assert(phead->next != phead);
	
	ListErase(phead->prev);
}

优化后的头删

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

	assert(!ListEmptyLTNode(phead));

	ListErase(phead->next);
}

10,求链表长度

int ListSize(LTNode* phead)
{
	assert(phead);

	//跳过哨兵位
	LTNode* cur = phead->next;
	//求长度
	int size = 0;
	while (cur!=phead)
	{
		++size;
		cur = cur->next;
	}
	//返回链表长度
	return size;
/*
	不要用头节点存储来计算长度,int符合但其他类型
	例如char,double 都会出现错误
*/
}

test.c中的调用方法:

	printf("存储的元素个数为:%d\n", ListSize(plist));
	//plist为主函数中创建的结构体

11,查找链元素

/*phead为原双链表,x表示被查找元素*/
int ListFind(LTNode* phead, LTDataType x)
{
	assert(phead);

	/*新建一个指针t,初始化为头指针 head*/
	LTNode* temp = phead->next;
	int i = 1;
	while (temp!=phead)
	{
		if (temp->data == x)
		{
			return i;
		}
		i++;
		temp = temp->next;
	}
	/*程序执行至此处,表示查找失败*/
	return 0;
}

test.c中的调用方法:

	int lf = ListFind(plist, 5);
	if (lf==1)
		printf("找到了%d\n",lf);
	else
		printf("没找到\n");

12,销毁链表

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

	LTNode* cur = phead->next;
	int size = 0;
	while (cur != phead)
	{
		LTNode* next = cur->next;
		ListErase(cur);
		cur = next;
	}
	free(phead);
	//只是将指向plist的指针置空,要置空plist要在Test.c中完成
	phead = NULL;
}

test.c中的使用方法:

	//销毁的为结构体,并不是指向结构体的指针,所以需要对指针置空
	ListDestory(plist);
	plist = NULL;
	if (plist == NULL)
		printf("销毁成功");

成品展示

List.h

#pragma once

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

typedef int LTDataType;

//定义链表
typedef struct ListNode
{
	struct ListNode* next;
	struct ListNode* prev;
	LTDataType data;
}LTNode;

//创建双向链表
LTNode* BuyListNode(LTDataType x);

//链表初始化
//void ListInit(LTNode** pphead);
LTNode* ListInit();

//打印
void ListPrint(LTNode* phead);

//尾插
void ListPushBack(LTNode* phead, LTDataType x);

//头插
void ListPushFront(LTNode* phead, LTDataType x);

//尾删
void ListPopBack(LTNode* phead);

//头删
void ListPopFront(LTNode* phead);

//判断链表是否被删空
bool ListEmptyLTNode(LTNode* phead);

//pos位置之前插入
void ListInsert(LTNode* pos, LTDataType x);

//删除pos位置的节点
void ListErase(LTNode* pos);

//求链表长度
int ListSize(LTNode* phead);

//查找链元素
int ListFind(LTNode* phead, LTDataType x);

//销毁链表
void ListDestory(LTNode* phead);

List.c

#include "List.h"

//创建双向链表
LTNode* BuyListNode(LTDataType x)
{
	LTNode* node = (LTNode*)malloc(sizeof(LTNode));
	if (node == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}
	node->data = x;
	node->next = NULL;
	node->prev = NULL;
	return node;
} 

/*
	改变结构体要用结构体指针。
	要改变结构体指针要用结构体指针的指针。
*/
//初始化链表
//void ListInit(LTNode** pphead)
//{
//	*pphead = BuyListNode(-1);
//	(*pphead)->next = *pphead;
//	(*pphead)->prev = *pphead;
//}
LTNode* ListInit()
{
	LTNode* phead = BuyListNode(-1);
	phead->next = phead;
	phead->prev = phead;
}

//打印
void ListPrint(LTNode* phead)
{
	assert(phead);
	
	//创建指针进行依次访问
	LTNode* cur = phead->next;

	//当cur循环到头节点时代表已经循环一圈,停止循环
	while (cur!=phead)
	{
		printf("%d ", cur->data);
		cur = cur->next;
	}
	printf("\n");
}

//尾插
void ListPushBack(LTNode* phead, LTDataType x)
{
	assert(phead);
	
	//head前一个位置插入即为尾插
	ListInsert(phead, x);//pos前一个位置插入
}
//void ListPushBack(LTNode* phead, LTDataType x)
//{
//	assert(phead);
//
//	LTNode* newnode = BuyListNode(x);
//	LTNode* tail = phead->prev;
//	//前一个节点指向新节点
//	tail->next = newnode;
//	//新节点的prev指向上一个节点
//	newnode->prev = tail;
//	//新节点存放的下一节点为头节点
//	newnode->next = phead;
//	//头节点的prev存放新节点,方便尾插
//	phead->prev = newnode;
//}

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

	ListInsert(phead->next, x);
}
//void ListPushFront(LTNode* phead, LTDataType x)
//{
//	assert(phead);
//	
//	LTNode* newnode = BuyListNode(x);
//	LTNode* next = phead->next;
//
//	next->prev = newnode;  //1
//	newnode->next = next;  //2
//	newnode->prev = phead; //3
//	phead->next = newnode; //4
//
//	//phead->next = newnode;
//	//newnode->prev = phead;
//	//newnode->next = next;
//	//next->prev = newnode;
//
//}

//判断链表是否被删空
bool ListEmptyLTNode(LTNode* phead)
{
	assert(phead);

	/*
		链表返回只剩头节点(链表已经被删空)为真
		否则为假
	*/
	return phead->next == phead;
}

//尾删
void ListPopBack(LTNode* phead)
{
	assert(phead);
	//判断链表是否被删空:为真即为假,为假即为真
	assert(!ListEmptyLTNode(phead));//assert(phead->next != phead);
	
	ListErase(phead->prev);
}
//void ListPopBack(LTNode* phead)
//{
//	assert(phead);
//	//判断链表是否被删空:为真即为假,为假即为真
//	assert(!ListEmptyLTNode(phead));//assert(phead->next != phead);
//
//	//head的前一个即为尾巴
//	LTNode* tail = phead->prev;//head前一个节点
//	LTNode* tailPrev = tail->prev;//tail的前一个节点
//
//	free(tail);//删除节点
//
//	//重新链接新尾节点
//	tailPrev->next = phead;
//	phead->prev = tailPrev;
//}


//头删
void ListPopFront(LTNode* phead)
{
	assert(phead);

	assert(!ListEmptyLTNode(phead));

	ListErase(phead->next);
}
//void ListPopFront(LTNode* phead)
//{
//	assert(phead);
//	assert(!ListEmptyLTNode(phead));
//
//	LTNode* next = phead->next;
//	LTNode* nnext = next->next;
//
//	free(next);//删除节点
//
//	phead->next = nnext;
//	nnext->prev = phead;
//}

//pos位置之前插入
void ListInsert(LTNode* pos, LTDataType x)
{
	assert(pos);

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

	//prev newnode pos
	prev->next = newnode;
	newnode->prev = prev;
	newnode->next = pos;
	pos->prev = newnode;
	//此时既然已经做到了可以任意位置插入,
	//那就可以对头插尾插进行复用,简化代码
}

//删除pos位置的节点
void ListErase(LTNode* pos)
{
	assert(pos);

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

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

	free(pos);
	//此时既然已经做到了可以任意位置插入,
	//那就可以对头删尾插进行复用,简化代码
}

//求链表长度
int ListSize(LTNode* phead)
{
	assert(phead);

	//跳过哨兵位
	LTNode* cur = phead->next;
	//求长度
	int size = 0;
	while (cur!=phead)
	{
		++size;
		cur = cur->next;
	}
	//返回链表长度
	return size;

/*
	不要用头节点存储来计算长度,int符合但其他类型
	例如char,double 都会出现错误
*/
}

/*phead为原双链表,x表示被查找元素*/
int ListFind(LTNode* phead, LTDataType x)
{
	assert(phead);

	/*新建一个指针t,初始化为头指针 head*/
	LTNode* temp = phead->next;
	int i = 1;
	while (temp!=phead)
	{
		if (temp->data == x)
		{
			return i;
		}
		i++;
		temp = temp->next;
	}
	/*程序执行至此处,表示查找失败*/
	return 0;
}

//销毁链表
void ListDestory(LTNode* phead)
{
	assert(phead);

	LTNode* cur = phead->next;
	int size = 0;
	while (cur != phead)
	{
		LTNode* next = cur->next;
		ListErase(cur);
		cur = next;
	}
	free(phead);
	//只是将指向plist的指针置空,要置空plist要在Test.c中完成
	phead = NULL;
}

test.c

test.c只是用于测试代码,菜单的写法本文将不进行讲解。
但test.c中含有对查找and销毁and存储链元素个数的使用方法进行了示例可以当参考。

#include "List.h"

void Test1()
{
	//LTNode* plist = NULL;
	//ListInit(&plist);
	
	LTNode* plist = ListInit();
	ListPushBack(plist, 0);
	ListPushBack(plist, 1);
	ListPushBack(plist, 2);
	ListPushBack(plist, 3);
	ListPushBack(plist, 4);
	ListPrint(plist);
	ListPushFront(plist, 10);
	ListPrint(plist);
}

void Test2()
{
	//LTNode* plist = NULL;
	//ListInit(&plist);

	LTNode* plist = ListInit();
	ListPushFront(plist, 0);
	ListPushFront(plist, 1);
	ListPushFront(plist, 2);
	ListPushFront(plist, 3);
	ListPushFront(plist, 4);
	ListPushBack(plist, 5);
	ListPrint(plist);
	ListPopBack(plist);
	ListPrint(plist);
	ListPopFront(plist);
	ListPrint(plist);

	int lf = ListFind(plist, 5);
	if (lf==1)
		printf("找到了%d\n",lf);
	else
		printf("没找到\n");

	printf("存储的元素个数为:%d\n", ListSize(plist));

	ListDestory(plist);
	plist = NULL;
	if (plist == NULL)
		printf("销毁成功");
}

int main()
{
	Test2();

	return 0;
}

本文到这就结束啦,本文为万字解读,创作不易,若喜欢请留下免费的赞吧!
感谢阅读😊😊😊文章来源地址https://www.toymoban.com/news/detail-708762.html

到了这里,关于数据结构:线性表之-循环双向链表(万字详解)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 数据结构(王道)——线性表之静态链表&顺序表和链表的比较

      如何定义一个静态链表     初始化静态链表:   静态链表的查找、插入、删除           创: 销:   增、删:   查:   顺序表、链表该如何选择?  

    2024年02月16日
    浏览(125)
  • 【数据结构】C语言实现双向链表(带头结点、循环)

    结点定义: 接口定义: 我们将申请结点的代码封装成函数,方便后续使用 由于是带头结点的双向链表,因此在使用链表前,我们需要对链表进行初始化。 遍历链表,值得说的是,带头结点的双向链表的循环结束条件是 cur != phead 尾插时,需要先找到尾结点,然后将新结点插

    2024年02月03日
    浏览(72)
  • 线性表之双向链表(详解)

    🍕博客主页:️自信不孤单 🍬文章专栏:数据结构与算法 🍚代码仓库:破浪晓梦 🍭欢迎关注:欢迎大家点赞收藏+关注 在前面我们已经学习了链表中的单链表,今天我们再来学习另一个常用的链表结构: 带头双向循环链表 。 首先创建两个文件来实现双向链表: List.h(节

    2024年02月06日
    浏览(34)
  • 【数据结构】线性表 ⑤ ( 双循环链表 | 双循环链表特点 | 双循环链表插入操作处理 | 代码示例 - 使用 Java 实现 双循环链表 )

    \\\" 双循环链表 \\\" 是 在 单循环链表 的基础上 , 在每个 节点 中 , 新增一个 指针 , 指向 该节点 的 前驱节点 ; 双向循环链表 每个 节点 都包含 数据 和 两个指针 , 一个指针指向前一个节点 , 一个指针指向后一个节点 ; 与 单循环链表相比 , 双循环链表 可以在两个方向上遍历整个链

    2024年02月13日
    浏览(42)
  • 【数据结构】线性表之栈、队列

    前面两篇文章讲述了关于线性表中的顺序表与链表,这篇文章继续讲述线性表中的 栈和队列。 这里讲述的两种线性表与前面的线性表不同,只允许在一端入数据,一段出数据,详细内容请看下面的文章。 顺序表与链表两篇文章的链接: 线性表之顺序表 线性表之链表 注意:

    2024年02月06日
    浏览(62)
  • 数据结构:线性表之-顺序表

    目录 1.线性表概念 1.1 什么是顺序列表 1.2 线性表 2.顺序表实现 将有以下功能: 详细过程 顺序表的动态存储 顺序表初始化 尾插 扩容 头插 更改后的尾插 尾删 头删 打印 释放内存 优化顺序表 (任意位置插入删除) 优化后的头插尾插 优化后的头删尾删 查找和删除 进行装饰(菜单

    2024年02月10日
    浏览(51)
  • 【数据结构】- 链表之单链表(下)

    未来藏在迷雾中 叫人看来胆怯 带你踏足其中 就会云开雾散 本章是关于数据结构中的链表之单链表(下) 提示:以下是本篇文章正文内容,下面案例可供参考 1.2.1 在pos位置插入(也就是pos位置之前) 流程图 多个节点 一个节点 1.2.2 在pos位置之后插入 流程图: 注意: 下面这种写

    2023年04月23日
    浏览(63)
  • [数据结构]链表之单链表(详解)

    在学习 链表 之前,我们已经学习了 顺序表 了 根据 顺序表 的特点,我们可以发现 顺序表 有优点,也有一些缺陷。 所以根据 顺序表 的缺点,链表就横空出世啦~ **概念:链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次

    2023年04月08日
    浏览(55)
  • 数据结构——线性表之顺序表

    目录 一.线性表 二.顺序表实现  2.1 概念及结构  2.2 动态顺序表 2.2.1 初始化与销毁函数 2.2.2 打印函数 2.2.3 尾插函数 2.2.4 尾删函数 2.2.5 扩容函数 2.2.6 头插函数 2.2.7 头删函数 2.2.8 任意位置插入函数 2.2.9 查找函数 2.2.10 任意位置删除函数  2.2.11 修改函数 三.完整代码 四.力扣

    2024年02月07日
    浏览(49)
  • 【数据结构】线性表之顺序表

    线性表是 n (n = 0) 个具有相同特性的数据元素的有限序列。线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列… 线性表在 逻辑上是线性结构 ,也就说是连续的一条直线。但是在 物理结构 上并 不一定 是连续的线性表在物理上存储时,通常以

    2024年02月04日
    浏览(56)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包