返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >C语言超详细讲解数据结构中双向带头循环链表
  • 389
分享到

C语言超详细讲解数据结构中双向带头循环链表

2024-04-02 19:04:59 389人浏览 泡泡鱼
摘要

目录一、概念二、必备工作2.1、创建双向链表结构2.2、初始化链表2.3、动态申请节点2.4、打印链表2.5、销毁链表三、主要功能3.1、在pos节点前插入数据尾插头插3.2、删除p

一、概念

前文我们已经学习了单向链表,并通过oj题目深入了解了带头节点的链表以及带环链表,来画张图总体回顾下:

在我们学习的链表中,其实总共有8种,都是单双向和带不带头以及带不带环的任意组合

今儿要学习的是双向 - 带头 - 循环链表,听名字就觉着结构很复杂,要比曾经学的单向 - 不带头 - 不循环 链表的结构复杂的多 ,确实也是。先来画张图整体感受下:

解释:

  • 双向:就要确保每个数据存两个指针next和prev。next指向下一个节点,prev指向上一个节点
  • 带头:带一个哨兵位的头节点在数据的最前头。
  • 循环:尾节点的next指向哨兵位头节点,而哨兵位的上一个节点prev指向尾节点,构成循环。

正文开始:

二、必备工作

2.1、创建双向链表结构

因为是双向链表,所以在结构体里头必然有两个指针,一个next指向下一个节点,一个prev指向上一个节点。

List.h 文件:

//创建双向链表结构
typedef int LTDataType;   //方便后续更改数据类型,本文以int整型为主
typedef struct Listnode
{
	LTDataType data; //存储数据
	struct ListNode* next; //指向下一个
	struct ListNode* prev; //指向上一个
}LTNode; //方便后续使用,不需要重复些struct

2.2、初始化链表

思路:

在初始化的时候要传地址,因为形参的改变不会影响实参,pphead的改变不会影响pList,要传pList的地址,用**pphead来接收,此时就要assert断言了,因为二级指针地址不可能位空。因为是双向循环链表,所以要将创建好的哨兵位节点的next和prev均指向自己。

List.h 文件:(1)

//初始化链表(二级指针版)
void ListInit(LTNode* pphead);

List.c 文件:(1)

//初始化链表(二级指针版)
void ListInit(LTNode** pphead)
{
	//传二级指针,那么当然要断言
	assert(pphead);
	*pphead = BuyLTNode(0);//因为是带哨兵位的头节点,所以一开始就要给一个节点
	//为了循环,要让哨兵位的next和prev均指向自己
	(*pphead)->next = *pphead; //注意优先级,*pphead要加括号
	(*pphead)->prev = *pphead;
}

注意:

上一种方法我们传的是二级指针,那么可以传一级指针吗,其实也是可以的,只需写个函数返回指针即可

List.h 文件:(2)

//初始化链表(一级指针版本)
LTNode* ListInit();

List.c 文件:(2)

//初始化链表(一级指针版)
LTNode* ListInit()
{
	LTNode* phead = BuyLTNode(0);
	phead->next = phead;
	phead->prev = phead;
	return phead;
}

2.3、动态申请节点

List.c 文件:

//创建新节点
LTNode* BuyLTNode(LTDataType x)
{
	LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));
	if (newnode == NULL)
	{
		printf("malloc fail\n");
		exit(-1);
	}
	newnode->data = x;
	newnode->next = NULL;
	newnode->prev = NULL;
	return newnode; //返回新创建的节点
}

2.4、打印链表

思路:

既然是打印,首先要搞明白一点,哨兵位不用来存放有效数据,那么就不需要打印,定义一个cur指针来迭代,那么应该从phead的next开始打印,当cur走完一遍,重又回到phead的时候停止

List.h 文件:

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

List.c 文件:

//打印链表
void ListPrint(LTNode* phead)
{
	assert(phead);//断言
	LTNode* cur = phead->next;
	while (cur != phead)
	{
		printf("%d ", cur->data);
		cur = cur->next;
	}
	printf("\n");
}

2.5、销毁链表

思路:

既然是销毁链表了,那么自然是要把链表的所有元素包括哨兵位都给销毁掉,但毕竟刚开始传phead的时候是不能为空的,所以要断言,在把所有有效数据销毁后最后再销毁哨兵位即可。

法一:遍历

定义一个指针cur,从phead的next第一个有效数据开始free,保存下一个,再free,依次遍历下去

法二:附用ListErase函数

此法也可以,不过每次Erase完,都会把前后两个节点再链接起来,虽说最后都会销毁,但duck不必多此一举,所有直接采用法一比较好

List.h 文件:

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

List.c 文件:

//销毁链表
void ListDestory(LTNode* phead)
{
	assert(phead);
	LTNode* cur = phead->next;
	//销毁从第一个节点到尾部的数据
	while (cur != phead)
	{
		LTNode* next = cur->next;
		//ListErase(cur);
		free(cur);
		cur = next;
	}
	//置空哨兵位节点phead
	free(phead);
	phead = NULL;
}

Test.c 文件:

void TestList7()
{
	LTNode* pList = ListInit();
	for (int i = 1; i <= 7; i++)
	{
		ListPushBack(pList, i); //尾插7个数字
	}
	ListPrint(pList);//打印
	//销毁链表
	ListDestory(pList);
	pList = NULL;
}

三、主要功能

3.1、在pos节点前插入数据

思路:

假设我们已经进行了尾插4个数字,现在想在数字3的前面插入30,那么首先就要查找有无数字3,若有,则插入。注意:这里需要用到后文才讲到的查找函数,这里直接引用了,详解看后文即可,问题不大!

首先,将30放到新创建的节点newnode里头,为了实现双向,要先把3的前一个数据2的next指向新节点newnode,把newnode的prev指向2,newnode的next指向3,3的prev指向newnode。

 List.h 文件:

//在pos前插入数据
void ListInsert(LTNode* pos, LTDataType x);

List.c 文件:

//在pos前插入数据
void ListInsert(LTNode* pos, LTDataType x)
{
	assert(pos);
	//创建插入数据的新节点
	LTNode* newnode = BuyLTNode(x);
	//链接左侧
	pos->prev->next = newnode;
	newnode->prev = pos->prev;
	//链接右侧
	newnode->next = pos;
	pos->prev = newnode;
}

Test.c 文件:

void TestList3()
{
	LTNode* pList = ListInit();
	for (int i = 1; i <= 7; i++)
	{
		ListPushBack(pList, i); //尾插7个数据
	}
	ListPrint(pList);//打印尾插的7个
	//寻找数字
	LTNode* pos = ListFind(pList, 3);
	if (pos)
	{
		ListInsert(pos, 30); //找到数字3就插入
	}
	ListPrint(pList);//打印
}

效果如下:

尾插

思路:

首先,因为此链表是带哨兵位的头节点,所以头节点必然不为空,刚开始就要assert断言。其次,单链表尾插需要找尾,双向链表虽然也需要,不过非常简单,不需要再遍历链表了,因为哨兵位头节点的phead的上一个节点指向的就是尾,这就充分体现了双向循环的好处,找到了尾节点就需要再创建一个节点存储插入数据,方便尾插。

List.h 文件:

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

List.c 文件:1.0

//尾插1.0
void ListPushBack(LTNode* phead, LTDataType x)
{
	assert(phead); //断言,防止头节点为空
	LTNode* tail = phead->prev; //找到尾节点,便于后续插入数据
	LTNode* newnode = BuyLTNode(x);//创建新节点
	//将此新插入的尾节点与上一个节点链接起来
	tail->next = newnode;
	newnode->prev = tail;
	//将尾节点与哨兵位phead链接起来构成循环
	newnode->next = phead;
	phead->prev = newnode;
}

Test.c 文件:

void TestList1()
{
	//初始化(法一)
	
	//初始化(法二)
	LTNode* pList = ListInit();
	for (int i = 1; i <= 7; i++)
	{
		ListPushBack(pList, i); //尾插7个数据
	}
	ListPrint(pList);//打印尾插的7个
}

效果如下:

注意:

在上文中,我们学习了在pos前插入数据,那么设想一下,当pos就等于phead的时候,它(phead)的前不就是链表的尾部吗,那么理所应当,尾插也可以这样完成:

List.c 文件:2.0

//尾插2.0
void ListPushBack(LTNode* phead, LTDataType x)
{
	assert(phead); 
	ListInsert(phead, x);
}

头插

思路:

前面我们已经学习了在pos前插入数据,那么头插的实现就尤为简单了,当pos为原第一个数据phead->next时,此时就是在其之前插入数据,那么实现的不久是头插吗,如下:

List.h 文件:

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

List.c 文件:

//头插
void ListPushFront(LTNode* phead, LTDataType x)
{
	assert(phead);
	ListInsert(phead->next, x);
}

Test.c 文件:

void TestList4()
{
	LTNode* pList = ListInit();
	for (int i = 1; i <= 7; i++)
	{
		ListPushBack(pList, i); //尾插7个数字
	}
	ListPrint(pList);//打印
	for (int i = -2; i <= 0; i++)
	{
		ListPushFront(pList, i); //头插3个数字
	}
	ListPrint(pList);//打印
}

效果如下:

3.2、删除pos处节点数据

思路:

删除pos处数据其实也很简单,有点类似于把pos处直接忽略的思想,或者说是绕过去。首先,需要找到pos的上一个节点prev和下一个节点next,将prev和next互相链接即可,直接跳过了pos,这样就实现了删除pos处节点的数据,记得把pos处给free释放掉。这里我们以pos为2示例:

 List.h 文件:

//删除pos处数据
void ListErase(LTNode* pos);

List.c 文件:

//删除pos处数据
void ListErase(LTNode* pos)
{
	assert(pos);
	//定义两个指针保存pos两边的节点
	LTNode* prev = pos->prev;
	LTNode* next = pos->next;
	//将prev和next链接起来
	prev->next = next;
	next->prev = prev;
	//free释放
	free(pos);
	pos = NULL;
}

Test.c 文件:

void TestList5()
{
	LTNode* pList = ListInit();
	for (int i = 1; i <= 7; i++)
	{
		ListPushBack(pList, i); //尾插7个数据
	}
	ListPrint(pList);//打印尾插的7个
	//寻找数字
	LTNode* pos = ListFind(pList, 3);
	if (pos)
	{
		ListErase(pos); //删除pos处数据
		pos = NULL; //形参的改变不会影响实参,最好在这置空pos
	}
	ListPrint(pList);//打印
}

效果如下:

尾删

思路:

双向循环链表的特点将再次得以体现,根据其特性,我们知道phead的prev指向尾节点,用tail指针保存,再定义一个指针tailPrev指向tail的prev,现仅需将tailPrev的next指向哨兵位节点phead,再把哨兵位phead的prev重新置成tailPrev即可,但是别忘记把删掉的尾节点给释放掉,得free(tail)。记得要断言链表不能为空,因为不能删除哨兵位节点。

List.H 文件:

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

List.c 文件:1.0

//尾删
void ListPopBack(LTNode* phead)
{
	assert(phead);//本身就有哨兵位,不能为空,要断言
	assert(phead->next != phead); //防止链表为空,导致删除哨兵位节点
	LTNode* tail = phead->prev;
	LTNode* tailPrev = tail->prev;
	//释放尾节点
	free(tail);
	tail = NULL;
	//将链表循环起来
	tailPrev->next = phead;
	phead->prev = tailPrev;
}

Test.c 文件:

void TestList2()
{
	LTNode* pList = ListInit();
	for (int i = 1; i <= 7; i++)
	{
		ListPushBack(pList, i); //尾插7个数据
	}
	ListPrint(pList);//打印尾插的7个
	//尾删两次
	ListPopBack(pList);
	ListPopBack(pList);
	ListPrint(pList);//再次打印
}

效果如下:

 注意:

前文我们已经学了删除pos处节点的数据,那么当pos为phead->prev时,删除的是不是就是尾节点,所以,尾删理所应当可以这样写:

List.c 文件:2.0

//尾删
void ListPopBack(LTNode* phead)
{
	assert(phead);//本身就有哨兵位,不能为空,要断言
	assert(phead->next != phead); //防止链表为空,导致删除哨兵位节点
	ListErase(phead->prev);
}

头删

思路:

有了上文之鉴,我们可以直接利用前面写的删除pos处数据的函数来完成,当pos为phead->prev时,pos的位置就是尾,此时删除的就是尾。当然还得注意一点,需要额外assert断言防止删除的数据为哨兵位的节点。

List.h 文件:

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

List.c 文件:

//头删
void ListPopFront(LTNode* phead)
{
	assert(phead);
	assert(phead->next != phead); //防止删除哨兵位节点
	ListErase(phead->next);
}

Test.c 文件:

void TestList6()
{
	LTNode* pList = ListInit();
	for (int i = 1; i <= 7; i++)
	{
		ListPushBack(pList, i); //尾插7个数字
	}
	ListPrint(pList);//打印
	//头插3个数字
	ListPushFront(pList, 0);
	ListPushFront(pList, -1);
	ListPushFront(pList, -2);
	ListPrint(pList);//打印
	//尾删3个数字
	ListPopBack(pList);
	ListPopBack(pList);
	ListPopBack(pList);
	ListPrint(pList);//打印
	//头删3个数字
	ListPopFront(pList);
	ListPopFront(pList);
	ListPopFront(pList);
	ListPrint(pList);//打印
}

效果如下:

3.3、查找数据

思路:

查找数据其实也比较简单,首先,定义一个指针cur指向哨兵位phead的next,依次遍历cur看cur->data是否为查找的数据x,如果是,则返回cur,否则继续(cur=cur->next),若找不到则返回NULL。

List.h 文件:

//链表查找
LTNode* ListFind(LTNode* phead, LTDataType x);

List.c 文件:

//链表查找
LTNode* ListFind(LTNode* phead, LTDataType x)
{
	assert(phead);
	LTNode* cur = phead->next;
	while (cur != phead)
	{
		if (cur->data == x)
		{
			return cur; //找到就返回cur
		}
		cur = cur->next;
	}
	return NULL; //找不到就返回空
}

四、总代码

List.h 文件

#pragma once
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
//创建双向链表结构
typedef int LTDataType;   //方便后续更改数据类型,本文以int整型为主
typedef struct ListNode
{
	LTDataType data; //存储数据
	struct ListNode* next; //指向下一个
	struct ListNode* prev; //指向上一个
}LTNode; //方便后续使用,不需要重复些struct
 
//初始化链表(二级指针版本)

//初始化链表(一级指针版本)
LTNode* ListInit();
 
//打印链表
void ListPrint(LTNode* phead);
//链表查找
LTNode* ListFind(LTNode* phead, LTDataType x);
//销毁链表
void ListDestory(LTNode* phead);
 
//尾插
void ListPushBack(LTNode* phead, LTDataType x);
//尾删
void ListPopBack(LTNode* phead);
//头插
void ListPushFront(LTNode* phead, LTDataType x);
//头删
void ListPopFront(LTNode* phead);
 
//在pos前插入数据
void ListInsert(LTNode* pos, LTDataType x);
//删除pos处数据
void ListErase(LTNode* pos);

List.c 文件

#define _CRT_SECURE_NO_WARNINGS 1
#include"List.h"
//创建新节点
LTNode* BuyLTNode(LTDataType x)
{
	LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));
	if (newnode == NULL)
	{
		printf("malloc fail\n");
		exit(-1);
	}
	newnode->data = x;
	newnode->next = NULL;
	newnode->prev = NULL;
	return newnode; //返回新创建的节点
}
//初始化链表(二级指针版)

//初始化链表(一级指针版)
LTNode* ListInit()
{
	LTNode* phead = BuyLTNode(0);
	phead->next = phead;
	phead->prev = phead;
	return phead;
}
 
//打印链表
void ListPrint(LTNode* phead)
{
	assert(phead);//断言
	LTNode* cur = phead->next;
	while (cur != phead)
	{
		printf("%d ", cur->data);
		cur = cur->next;
	}
	printf("\n");
}
//链表查找
LTNode* ListFind(LTNode* phead, LTDataType x)
{
	assert(phead);
	LTNode* cur = phead->next;
	while (cur != phead)
	{
		if (cur->data == x)
		{
			return cur; //找到就返回cur
		}
		cur = cur->next;
	}
	return NULL; //找不到就返回空
}
//销毁链表
void ListDestory(LTNode* phead)
{
	assert(phead);
	LTNode* cur = phead->next;
	//销毁从第一个节点到尾部的数据
	while (cur != phead)
	{
		LTNode* next = cur->next;
		//ListErase(cur);
		free(cur);
		cur = next;
	}
	//置空哨兵位节点phead
	free(phead);
	phead = NULL;
}
 
//尾插
void ListPushBack(LTNode* phead, LTDataType x)
{
	assert(phead); //断言,防止头节点为空
	
	//法二:
	ListInsert(phead, x);
}
//尾删
void ListPopBack(LTNode* phead)
{
	assert(phead);//本身就有哨兵位,不能为空,要断言
	assert(phead->next != phead); //防止链表为空,导致删除哨兵位节点
	
	//法二:
	ListErase(phead->prev);
}
 
//头插
void ListPushFront(LTNode* phead, LTDataType x)
{
	assert(phead);
	ListInsert(phead->next, x);
}
//头删
void ListPopFront(LTNode* phead)
{
	assert(phead);
	assert(phead->next != phead); //防止删除哨兵位节点
	ListErase(phead->next);
}
 
//在pos前插入数据
void ListInsert(LTNode* pos, LTDataType x)
{
	assert(pos);
	//创建插入数据的新节点
	LTNode* newnode = BuyLTNode(x);
	//链接左侧
	pos->prev->next = newnode;
	newnode->prev = pos->prev;
	//链接右侧
	newnode->next = pos;
	pos->prev = newnode;
}
//删除pos处数据
void ListErase(LTNode* pos)
{
	assert(pos);
	//定义两个指针保存pos两边的节点
	LTNode* prev = pos->prev;
	LTNode* next = pos->next;
	//将prev和next链接起来
	prev->next = next;
	next->prev = prev;
	//free释放
	free(pos);
	pos = NULL;
}

Test.c 文件

#define _CRT_SECURE_NO_WARNINGS 1
#include"List.h"
void TestList1()
{
	//初始化(法一)
	
	//初始化(法二)
	LTNode* pList = ListInit();
	for (int i = 1; i <= 7; i++)
	{
		ListPushBack(pList, i); //尾插7个数据
	}
	ListPrint(pList);//打印尾插的7个
}
 
void TestList2()
{
	LTNode* pList = ListInit();
	for (int i = 1; i <= 7; i++)
	{
		ListPushBack(pList, i); //尾插7个数据
	}
	ListPrint(pList);//打印尾插的7个
	//尾删两次
	ListPopBack(pList);
	ListPopBack(pList);
	ListPrint(pList);//再次打印
}
 
void TestList3()
{
	LTNode* pList = ListInit();
	for (int i = 1; i <= 7; i++)
	{
		ListPushBack(pList, i); //尾插7个数据
	}
	ListPrint(pList);//打印尾插的7个
	//寻找数字
	LTNode* pos = ListFind(pList, 3);
	if (pos)
	{
		ListInsert(pos, 30); //找到数字3就插入
	}
	ListPrint(pList);//打印
}
 
void TestList4()
{
	LTNode* pList = ListInit();
	for (int i = 1; i <= 7; i++)
	{
		ListPushBack(pList, i); //尾插7个数字
	}
	ListPrint(pList);//打印
	for (int i = -2; i <= 0; i++)
	{
		ListPushFront(pList, i); //头插3个数字
	}
	ListPrint(pList);//打印
}
 
void TestList5()
{
	LTNode* pList = ListInit();
	for (int i = 1; i <= 7; i++)
	{
		ListPushBack(pList, i); //尾插7个数据
	}
	ListPrint(pList);//打印尾插的7个
	//寻找数字
	LTNode* pos = ListFind(pList, 3);
	if (pos)
	{
		ListErase(pos); //删除pos处数据
		pos = NULL; //形参的改变不会影响实参,最好在这置空pos
	}
	ListPrint(pList);//打印
}
 
void TestList6()
{
	LTNode* pList = ListInit();
	for (int i = 1; i <= 7; i++)
	{
		ListPushBack(pList, i); //尾插7个数字
	}
	ListPrint(pList);//打印
	//头插3个数字
	ListPushFront(pList, 0);
	ListPushFront(pList, -1);
	ListPushFront(pList, -2);
	ListPrint(pList);//打印
	//尾删3个数字
	ListPopBack(pList);
	ListPopBack(pList);
	ListPopBack(pList);
	ListPrint(pList);//打印
	//头删3个数字
	ListPopFront(pList);
	ListPopFront(pList);
	ListPopFront(pList);
	ListPrint(pList);//打印
	//销毁链表
	ListDestory(pList);
	pList = NULL;
}
 
void TestList7()
{
	LTNode* pList = ListInit();
	for (int i = 1; i <= 7; i++)
	{
		ListPushBack(pList, i); //尾插7个数字
	}
	ListPrint(pList);//打印
	//销毁链表
	ListDestory(pList);
	pList = NULL;
}
int main()
{
	//TestList1();
	//TestList2();
	//TestList3();
	//TestList4();
	//TestList5();
	//TestList6();
	TestList7();
	return 0;
}

五、拓展

对比顺序表和链表

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

优缺点对比:

 顺序表链表
优点

1、物理空间是连续的,方便用下标随机访问。

2、CPU高速缓存命中率会更高。(补充)

1、按需申请释放空间。

2、任意位置可以O(1)插入删除数据。

缺点

1、正因为物理空间连续,空间不够需要扩容,扩容本身又一定消耗,其次扩容机制还存在一定的空间浪费。

2、头部或者中部插入删除,挪动数据,效率低,O(N)。

1、不支持下标的随机访问。

2、有些算法不适合在它上面进行,如:二分查找、排序等。

到此这篇关于C语言超详细讲解数据结构中双向带头循环链表的文章就介绍到这了,更多相关C语言 双向带头循环链表内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: C语言超详细讲解数据结构中双向带头循环链表

本文链接: https://lsjlt.com/news/145575.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • C语言超详细讲解数据结构中双向带头循环链表
    目录一、概念二、必备工作2.1、创建双向链表结构2.2、初始化链表2.3、动态申请节点2.4、打印链表2.5、销毁链表三、主要功能3.1、在pos节点前插入数据尾插头插3.2、删除p...
    99+
    2024-04-02
  • C语言超详细讲解双向带头循环链表
    目录一、双向带头循环链表的结构二、双向带头循环链表的函数接口1. 申请结点2. 初识化3. 打印4. 尾插尾删5. 头插头删6. 查找7. 中间插入和删除8. 判空及求链表长度9. ...
    99+
    2023-02-14
    C语言双向带头循环链表 C语言带头循环链表 C语言循环链表
  • C++带头双向循环链表超详细解析
    目录什么是带头双向循环链表带头双向循环链表常用接口实现上期我们讲完了无头单向非循环链表,这期我们接着来讲链表中结构最复杂的带头双向循环链表! 本期主要内容: 什么是带头双向循环链表...
    99+
    2024-04-02
  • C语言编程数据结构带头双向循环链表全面详解
    目录前言一、什么是带头循环双向链表二、链表初始化三、链表接口函数1.尾插2.头插3.头删4.尾删5.任意位置插入数据6.任意位置删除数据四、打印链表总结前言 上一篇数据结构专栏:C语...
    99+
    2024-04-02
  • C语言数据结构中双向带头循环链表怎么实现
    这篇文章主要讲解了“C语言数据结构中双向带头循环链表怎么实现”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“C语言数据结构中双向带头循环链表怎么实现”吧!一、概念来画张图总体回顾下:在我们学习...
    99+
    2023-06-30
  • C语言数据结构超详细讲解单向链表
    目录1.链表概况1.1 链表的概念及结构1.2 链表的分类2. 单向链表的实现2.1 SList.h(头文件的汇总,函数的声明)2.2 SList.c(函数的具体实现逻辑)2.2.1...
    99+
    2024-04-02
  • C语言实现带头双向循环链表
    目录前言1. 创建结构体2.malloc新节点3.创建哨兵位节点4.尾插5.打印6.尾删7.头插8.在指定位置pos的前面进行插入9. 删除指定位置pos节点10.销毁链表前言 在...
    99+
    2024-04-02
  • C语言超详细i讲解双向链表
    目录一、双向链表的概念二、双向链表的实现三、链表与顺序表的差别四、链表oj总结一、双向链表的概念 1、概念:概念:双向链表是每个结点除后继指针外还有⼀个前驱指针。双向链表也有带头结点...
    99+
    2024-04-02
  • C语言超详细介绍与实现线性表中的带头双向循环链表
    目录一、本章重点二、带头双向循环链表介绍2.1什么是带头双向循环链表?2.2最常用的两种链表结构三、带头双向循环链表常用接口实现 3.1结构体创建3.2带头双向循环链表的初始化 3....
    99+
    2024-04-02
  • C语言详解如何实现带头双向循环链表
    目录创建链表存储结构创建结点链表的初始化双向链表的打印双向链表尾插双向链表尾删双向链表头插双向链表头删双向链表查找双向链表pos前插入结点双向链表删除pos位置的结点双向链表的销毁顺...
    99+
    2024-04-02
  • C语言数据结构之双链表&循环链表&静态链表详解
    目录单链表 VS 双链表双链表双链表的初始化(带头结点)双链表的插入双链表的删除双链表的遍历循环单链表循环双链表循环双链表的初始化循环双链表的插入循环双链表的删除静态链表什么是静态链...
    99+
    2024-04-02
  • C++零基础精通数据结构之带头双向循环链表
    目录与单链表的区别代码的实现接口节点的构造初始化链表开辟节点销毁链表打印链表尾插链表尾删链表头插链表头删链表查找链表链表pos位置的删除总结与单链表的区别 单向/双向 单向:只有一个...
    99+
    2024-04-02
  • C语言如何实现带头双向循环链表
    这篇文章主要介绍了C语言如何实现带头双向循环链表,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。前言在实际生活中最常用的就是这两种链表。无头单向非循环链表。和带头双向循环链表。...
    99+
    2023-06-29
  • C语言怎么实现带头双向循环链表
    本篇内容主要讲解“C语言怎么实现带头双向循环链表”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“C语言怎么实现带头双向循环链表”吧!创建链表存储结构我们需要创建一个结构体来存储一个链表结点的相关信...
    99+
    2023-06-30
  • C语言带头双向循环链表怎么实现
    这篇“C语言带头双向循环链表怎么实现”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“C语言带头双向循环链表怎么实现”文章吧。带...
    99+
    2023-06-30
  • C语言实现带头双向循环链表的接口
    本文实例为大家分享了C语言实现带头双向循环链表的接口,供大家参考,具体内容如下 各函数功能如下 申请空间 ListNode* BuyListNode(LTDataType x) ...
    99+
    2024-04-02
  • C语言带头双向循环链表的示例代码
    目录前言结构分析链表的基本操作实现创建节点初始化链表链表销毁打印链表链表尾插链表尾删链表头插链表头删链表查找链表pos位置前面去插入删除pos位置链表判空代码复用总代码及头文件前言 ...
    99+
    2022-11-13
    C语言带头双向循环链表 C语言 双向循环链表 C语言 循环链表
  • C++ 数据结构超详细讲解单链表
    目录前言一、链表是什么链表的分类二、链表的实现总结(❁´◡`❁) 单链表 前言 上篇顺序表结尾了解了顺序表的诸多缺点,链表的特性很好的解决了这些问题,本期我们来认识单链表...
    99+
    2024-04-02
  • C语言手把手带你掌握带头双向循环链表
    目录前言带头双向循环链表的结构代码操作前言 关于链表这一块,写了多篇博客,学习了顺序表、单链表、及其一些练习题 顺序表:传送门:顺序表 单链表:传送门:单链表1  ...
    99+
    2024-04-02
  • C语言实现带头双向环形链表
    双向循环链表 上一次我们讲了单向无头非循环链表的实现,单向无头非循环链表的特点是:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构。而带头双向循环链表则恰恰与无...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作