【数据结构初阶】四、线性表里的链表(带头+双向+循环 链表 -- C语言实现)

这篇具有很好参考价值的文章主要介绍了【数据结构初阶】四、线性表里的链表(带头+双向+循环 链表 -- C语言实现)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

=========================================================================

相关代码gitee自取

C语言学习日记: 加油努力 (gitee.com)

 =========================================================================

接上期

【数据结构初阶】三、 线性表里的链表(无头+单向+非循环链表 -- C语言实现)-CSDN博客

 =========================================================================

                     

 引言 

通过上期单链表(无头+单向+非循环链表)介绍使用

我们可以知道顺序表和链表的区别

              

              

顺序表和链表的一些区别:

              

  • 单链表无头+单向+非循环链表只有一个后继指针next指向下一个结点
    双向链表不仅有后继指针next还有一个前驱指针prev指向上一个结点
                         
  • 上篇单链表只能顺着往后遍历不能倒着往回走
    会造成一些操作很困难回文逆置等操作),
    双向链表顺着往后遍历也能倒着往回遍历

更多区别图示:

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

【数据结构初阶】四、线性表里的链表(带头+双向+循环 链表 -- C语言实现),CCC全是C,链表,数据结构,算法

               

                

回顾上期中提到的带头双向循环链表

           

带头双向循环链表

简介:

结构最复杂一般用在单独存储数据

实际中使用的链表数据结构,都是带头双向循环链表

另外这个结构虽然结构复杂

但是使用代码实现以后会发现结构会带来很多优势实现反而简单

图示:

【数据结构初阶】四、线性表里的链表(带头+双向+循环 链表 -- C语言实现),CCC全是C,链表,数据结构,算法

         

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

             

1 . 双向链表的实现 (带头+双向+循环 链表)

(详细解释在图片的注释中,代码分文件放最后)                   

实现具体功能前的准备工作

  • 包含之后会用到的头函数
                               
  • 创建双向链表数据类型 -- 链表结点中数据域里存储的数据的类型
                 
  • 创建双向链表结点结构体(类型) -- 结点中应有 数据域  指针域
图示

【数据结构初阶】四、线性表里的链表(带头+双向+循环 链表 -- C语言实现),CCC全是C,链表,数据结构,算法

            

            

---------------------------------------------------------------------------------------------

            

BuyLTNode函数 -- 创建双向循环链表结点

  • 为创建结点开辟动态空间,并检查是否开辟成功
                          
  • 开辟成功后初始化结点数据域指针域
                   
  • 最后返回开辟的空间地址
图示

【数据结构初阶】四、线性表里的链表(带头+双向+循环 链表 -- C语言实现),CCC全是C,链表,数据结构,算法

            

            

---------------------------------------------------------------------------------------------

            

LTInit函数 -- 带头双向循环链表初始化函数

  • 初始化时先使用BuyLTNode函数创建哨兵位
                
  • 因为要实现循环
    所以让哨兵位后继指针next前驱指针prev都指向自己
                    
  • 初始化后返回链表哨兵位
图示

【数据结构初阶】四、线性表里的链表(带头+双向+循环 链表 -- C语言实现),CCC全是C,链表,数据结构,算法

            

            

---------------------------------------------------------------------------------------------

            

LTPrint函数 -- 打印双向链表各结点数据域数据

  • assert断言头指针(哨兵位地址)不为空
                
  • 创建结点指针cur进行遍历
                    
  • 使用while循环进行遍历打印

图示

【数据结构初阶】四、线性表里的链表(带头+双向+循环 链表 -- C语言实现),CCC全是C,链表,数据结构,算法

测试 -- LTPrint函数

【数据结构初阶】四、线性表里的链表(带头+双向+循环 链表 -- C语言实现),CCC全是C,链表,数据结构,算法

            

            

---------------------------------------------------------------------------------------------

            

LTPushBack函数 -- 向链表尾部插入一个结点(尾插)

  • assert断言头指针(哨兵位地址)不为空
                
  • 通过哨兵位配合前驱指针prev获得尾结点地址
                    
  • 调用BuyLTNode函数为尾插操作创建尾插结点newnode

                     

  • 尾插结点原尾部结点连接
                     

  • 尾插结点哨兵位进行连接

图示

【数据结构初阶】四、线性表里的链表(带头+双向+循环 链表 -- C语言实现),CCC全是C,链表,数据结构,算法

【数据结构初阶】四、线性表里的链表(带头+双向+循环 链表 -- C语言实现),CCC全是C,链表,数据结构,算法

测试 -- LTPushBack函数

【数据结构初阶】四、线性表里的链表(带头+双向+循环 链表 -- C语言实现),CCC全是C,链表,数据结构,算法

            

            

---------------------------------------------------------------------------------------------

            

LTPopBack函数 -- 删除链表尾部结点(尾删)

  • assert断言 头指针(哨兵位地址)不为空双向链表不为空链表
                
  • 通过哨兵位的前驱指针prev获得尾结点tail
    通过尾结点tail获得倒数第二个结点tailPrev
                    
  • 释放删除尾结点tail

                     

  • 倒数第二个结点tailPrev成为新的尾结点
    为保持循环,把tailPrev哨兵位连接起来

图示

【数据结构初阶】四、线性表里的链表(带头+双向+循环 链表 -- C语言实现),CCC全是C,链表,数据结构,算法

测试 -- LTPopBack函数

【数据结构初阶】四、线性表里的链表(带头+双向+循环 链表 -- C语言实现),CCC全是C,链表,数据结构,算法

            

            

---------------------------------------------------------------------------------------------

            

LTPushFront函数 -- 向链表头部插入一个结点(头插)

  • assert断言头指针(哨兵位地址)不为空
                
  • 调用BuyLTNode函数为头插操作创建头插结点newnode
                    
  • 创建一个first指针保存原本第一个结点地址

                     

  • 哨兵位后继指针next指向头插结点newnode
    头插结点newnode前驱指针prev指向哨兵位
                     

  • 头插结点newnode后继指针next指向原本头结点first
    原本头结点first前驱指针prev指向头插结点newnode

图示

【数据结构初阶】四、线性表里的链表(带头+双向+循环 链表 -- C语言实现),CCC全是C,链表,数据结构,算法

测试 -- LTPushFront函数

【数据结构初阶】四、线性表里的链表(带头+双向+循环 链表 -- C语言实现),CCC全是C,链表,数据结构,算法

            

            

---------------------------------------------------------------------------------------------

            

LTPopFront函数 -- 删除链表头部结点(头删)

  • assert断言 头指针(哨兵位地址)不为空双向链表不为空链表
                
  • 通过哨兵位后继结点next获得头结点地址
    再通过first结点获得第二个结点
                    
  • 释放头结点first

                     

  • 哨兵位后继结点next指向第二个结点second
    第二个结点的前驱指针prev指向哨兵位

图示

【数据结构初阶】四、线性表里的链表(带头+双向+循环 链表 -- C语言实现),CCC全是C,链表,数据结构,算法

测试 -- LTPopFront函数

【数据结构初阶】四、线性表里的链表(带头+双向+循环 链表 -- C语言实现),CCC全是C,链表,数据结构,算法

            

            

---------------------------------------------------------------------------------------------

            

LTSize函数 -- 求链表有效结点个数(求链表长度)

  • assert断言头指针(哨兵位地址)不为空
                
  • 创建变量size存放链表长度
    创建结点指针cur进行遍历
                    
  • 使用while循环遍历链表计算链表长度

                     

  • 最后返回链表长度size

图示

【数据结构初阶】四、线性表里的链表(带头+双向+循环 链表 -- C语言实现),CCC全是C,链表,数据结构,算法

测试 -- LTSize函数

【数据结构初阶】四、线性表里的链表(带头+双向+循环 链表 -- C语言实现),CCC全是C,链表,数据结构,算法

            

            

---------------------------------------------------------------------------------------------

            

LTFind函数 -- 在双向链表中查找数据域数据为x的结点地址

  • assert断言头指针(哨兵位地址)不为空
                
  • 创建遍历指针cur保存第一个结点地址
                    
  • 使用while循环进行遍历查找

                     

  • 未找到则返回空指针

图示

【数据结构初阶】四、线性表里的链表(带头+双向+循环 链表 -- C语言实现),CCC全是C,链表,数据结构,算法

测试 -- LTFind函数

【数据结构初阶】四、线性表里的链表(带头+双向+循环 链表 -- C语言实现),CCC全是C,链表,数据结构,算法

            

            

---------------------------------------------------------------------------------------------

            

LTInsert函数 -- 在pos结点之前插入数据域数据为x的结点

  • assert断言头指针(哨兵位地址)不为空
                
  • 通过pos结点获得前一个结点posPrev地址
                    
  • 使用BuyLTNode函数为插入结点开辟空间

                     

  • posPrev结点的后继指针next指向newnode
    newnode前驱指针prev指向posPrev
                     

  • newnode后继指针next指向pos
    pos结点前驱指针prev指向newnode

图示

【数据结构初阶】四、线性表里的链表(带头+双向+循环 链表 -- C语言实现),CCC全是C,链表,数据结构,算法

测试 -- LTInsert函数

【数据结构初阶】四、线性表里的链表(带头+双向+循环 链表 -- C语言实现),CCC全是C,链表,数据结构,算法

            

            

---------------------------------------------------------------------------------------------

            

LTErase函数 -- 删除pos结点

  • assert断言删除位置结点pos不为空
                
  • 保存删除结点pos的前一个结点posPrev地址
    保存删除结点pos的后一个结点posNext地址
                    
  • 释放掉pos结点

                     

  • 将pos前结点posPrev的后继指针指向posNext
    将pos后结点posNext的前驱指针指向posPrev

图示

【数据结构初阶】四、线性表里的链表(带头+双向+循环 链表 -- C语言实现),CCC全是C,链表,数据结构,算法

测试 -- LTErase函数

【数据结构初阶】四、线性表里的链表(带头+双向+循环 链表 -- C语言实现),CCC全是C,链表,数据结构,算法

            

            

---------------------------------------------------------------------------------------------

            

LTDestroy函数 -- 销毁链表

  • assert断言头指针(哨兵位地址)不为空
                
  • 创建遍历指针cur保存第一个结点地址
                    
  • 使用while循环遍历释放有效结点

                     

  • 最后释放哨兵位

图示

【数据结构初阶】四、线性表里的链表(带头+双向+循环 链表 -- C语言实现),CCC全是C,链表,数据结构,算法

         

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

             

2 . 对应代码

List.h -- 双向链表头文件

#pragma once

//双向链表头文件:

//包含之后需要用到的头文件:
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>

//定义双向链表数据域数据类型:
typedef int LTDataType;

//创建双向链表结点类型:
typedef struct ListNode
{
	//数据域:
	LTDataType data;

	//双向链表指针域:
	 //后继指针--指向后一个结点:
	struct ListNode* next;
	 //前驱指针--指向前一个结点:
	struct ListNode* prev;

}LTNode; //类型简称LTNode



//函数声明:

//创建链表结点--创建双向循环链表结点
//接收要插入创建结点数据域的数据
//返回创建结点的地址
LTNode* BuyLTNode(LTDataType x);

//双向链表初始化--带头双向循环链表初始化函数
//返回初始化结点的地址
LTNode* LTInit();

//打印链表--打印双向链表各结点数据域数据
//接收链表头指针(phead)
LTNode* LTPrint(LTNode* phead);

//双向链表尾插函数--向链表尾部插入一个结点(尾插):
//接收链表头指针(phead)、要尾插进链表的值(x)
void LTPushBack(LTNode* phead, LTDataType x);

//双向链表尾删函数--删除链表尾部结点(尾删)
//接收链表头指针(phead)
void LTPopBack(LTNode* phead);

//双向链表头插函数--向链表头部插入一个结点(头插):
//接收链表头指针(phead)、要头插进链表的值(x)
void LTPushFront(LTNode* phead, LTDataType x);

//双向链表头删函数--删除链表头部结点(头删)
//接收链表头指针(phead)
void LTPopFront(LTNode* phead);

//求链表结点个数函数--求链表有效结点个数(求链表长度)
//接收链表头指针(phead)
int LTSize(LTNode* phead);

//双向链表查找函数--在双向链表中查找数据域数据为x的结点地址
//接收链表头指针(phead)、要在链表中查找的值(x)
LTNode* LTFind(LTNode* phead, LTDataType x);

//双向链表插入函数--在pos结点之前插入数据域数据为x的结点
//接收插入位置(pos)、要插入链表的值(x)
void LTInsert(LTNode* pos, LTDataType x);

//双向链表删除函数--删除pos结点
//接收要删除结点地址(pos)
void LTErase(LTNode* pos);

//双向链表销毁函数--销毁链表
//接收要销毁链表头系欸但(phead)
void LTDestroy(LTNode* phead);

            

            

---------------------------------------------------------------------------------------------

                文章来源地址https://www.toymoban.com/news/detail-716643.html

List.c -- 双向链表函数实现文件

#define _CRT_SECURE_NO_WARNINGS 1

//双向链表函数实现文件:

//包含双向链表头文件:
#include "List.h"

//函数实现:

//创建链表结点--创建双向循环链表结点
//接收要插入创建结点数据域的数据
LTNode* BuyLTNode(LTDataType x)
{
	//为创建结点开辟动态空间:
	LTNode* node = (LTNode*)malloc(sizeof(LTNode));
	//检查是否开辟失败:
	if (node == NULL)
		//返回NULL,开辟失败
	{
		//打印错误信息:
		perror("malloc fail");
		//终止程序:
		exit(-1);
	}

	//把x放入结点数据域:
	node->data = x;
	//设置双向链表指针域:
	node->next = NULL;
	node->prev = NULL;
	
	//开辟成功则返回开辟空间地址
	return node;
}



//链表初始化--带头双向循环链表初始化函数
//接收链表头指针(phead)
LTNode* LTInit()
{
	//初始化时先使用BuyLTNode函数创建哨兵位:
	LTNode* phead = BuyLTNode(-1); //返回哨兵位指针

	//因为要实现循环,
	//所以让哨兵位后继指针next和前驱指针prev都指向自己:
	phead->next = phead;
	phead->prev = phead;
	//这样即使链表为空,它也是有头有尾的,即哨兵位phead

	//初始化后返回链表哨兵位:
	return phead;
}



//打印链表--打印双向链表各结点数据域数据
//接收链表头指针(phead)
LTNode* LTPrint(LTNode* phead)
{
	//assert断言头指针(哨兵位地址)不为空:
	assert(phead != NULL);

	//创建结点指针cur进行遍历:
	//指针cur应该从哨兵位(头指针)下一个结点开始
	LTNode* cur = phead->next;
	
	printf("phead <==> ");
	//因为是循环链表,不是以NULL空指针结尾
	//所以应该是当指针cur遍历回到哨兵位就终止遍历:
	while (cur != phead)
	//如果只用哨兵位,链表为空,
	//phead->next还是phead,不会进行打印
	{
		//打印数据域内容:
		printf("%d <=> ", cur->data);

		//打印完当前结点数据域数据后cur移向下个结点:
		cur = cur->next;
	}

	//打印完一个链表后换行:
	printf("\n");
}



//向链表尾部插入一个结点(尾插):
//接收结点头指针(phead)、要尾插进链表的值(x)
void LTPushBack(LTNode* phead, LTDataType x)
{
	//assert断言头指针(哨兵位地址)不为空:
	assert(phead != NULL);

	//通过哨兵位找到尾结点:
	//因为是循环链表:
	//所以哨兵位(带头链表)的前一个结点就是尾结点
	LTNode* tail = phead->prev; 
	//调用前驱指针获得尾结点

	//调用BuyLTNode函数为尾插创建尾插结点newnode:
	LTNode* newnode = BuyLTNode(x);

	//尾插结点前驱指针prev指向原本的尾结点:
	newnode->prev = tail;
	//原本尾结点后继指针next指向尾插结点:
	tail->next = newnode;

	//尾插结点后继指针next指向头结点:
	newnode->next = phead;
	//头结点前驱指针指向尾插结点:
	phead->prev = newnode;

	//带头+双向+循环 链表:
	//对比单链表,因为有哨兵位不用考虑链表为空的情况,
	//且不需要二级指针,通过操纵哨兵位这个结构体,
	//替换用二级指针操作头指针的操作


	/*
	//第二种方法:复用LTInsert函数
	//在哨兵位前插入一个值x就是尾插了:
	LTInsert(phead, x);
	*/
}



//双向链表尾删函数--删除链表尾部结点(尾删)
//接收链表头指针(phead)
void LTPopBack(LTNode* phead)
{
	//assert断言头指针(哨兵位地址)不为空:
	assert(phead != NULL);
	
	//assert断言双向链表不是空链表:
	assert(phead->next != phead);
	//如果哨兵位的下一个结点还是哨兵位说明是空链表

	//通过哨兵位的前驱指针prev获得尾结点tail:
	LTNode* tail = phead->prev;

	//再通过尾结点tail获得倒数第二个结点tailPrev:
	LTNode* tailPrev = tail->prev;

	//释放(删除)尾结点tail:
	free(tail);

	//这时就让倒数第二个结点tailPrev成为新的尾结点
	//为保持循环,把tailPrev和哨兵位连接起来:
	tailPrev->next = phead; //tailPrev后继指针指向哨兵位
	phead->prev = tailPrev; //哨兵位前驱指针指向tailPrev

	//带头+双向+循环 链表:
	//对比单链表,这里双向链表在尾删时因为有哨兵位的存在
	//即使链表只剩一个结点,也不用进行判断单独处理进行置空
	//这一个结点删掉后,还有哨兵位存在

	/*
	//第二种方法:复用LTErase函数
	//传尾结点地址给LTErase函数即可:
	LTErase(phead->prev);
	*/
}



//双向链表头插函数--向链表头部插入一个结点(头插):
//接收链表头指针(phead)、要头插进链表的值(x)
void LTPushFront(LTNode* phead, LTDataType x)
{
	//第二种方法:多定义一个指针
	
	//assert断言头指针(哨兵位地址)不为空:
	assert(phead != NULL);

	//调用BuyLTNode函数为头插创建头插结点newnode:
	LTNode* newnode = BuyLTNode(x);

	//创建一个first指针保存原本第一个结点地址:
	LTNode* first = phead->next;

	//哨兵位后继指针next指向头插结点newnode:
	phead->next = newnode;

	//头插结点newnode前驱指针prev指向哨兵位:
	newnode->prev = phead;

	//头插结点newnode后继指针next指向原本头结点first:
	newnode->next = first;

	//原本头结点first前驱指针prev指向头插结点newnode:
	first->prev = newnode;


	/*
	//assert断言头指针(哨兵位地址)不为空:
	assert(phead != NULL);
	
	//第一种方法:需要注意连接的顺序
	
	//调用BuyLTNode函数为头插创建头插结点newnode:
	LTNode* newnode = BuyLTNode(x);
	
	//先将头插结点的后继节点next连接上原本头结点:
	newnode->next = phead->next;
	//哨兵位的后继指针指向的就是头结点
	
	//再将原本头结点的前驱指针prev指向头插结点newnode:
	phead->next->prev = newnode;
	
	//哨兵位连接上头插节点newnode:
	phead->next = newnode;
	
	//头插结点newnode的前驱指针指向哨兵位:
	newnode->prev = phead;
	*/


	/*
	//第三种方法:复用LTInsert函数
	//在哨兵位后一个结点前插入一个值x就是头插了:
	LTInsert(phead->next, x);
	*/
}



//双向链表头删函数--删除链表头部结点(头删)
//接收链表头指针(phead)
void LTPopFront(LTNode* phead)
{
	//assert断言头指针(哨兵位地址)不为空:
	assert(phead != NULL);

	//assert断言双向链表不是空链表:
	assert(phead->next != phead);
	//如果哨兵位的下一个结点还是哨兵位说明是空链表

	//通过哨兵位后继结点next获得头结点地址:
	LTNode* first = phead->next;

	//再通过first结点获得第二个结点:
	LTNode* second = first->next;

	//释放头结点first:
	free(first);

	//让哨兵位后继结点next指向第二个结点second:
	phead->next = second;

	//第二个结点的前驱指针prev指向哨兵位:
	second->prev = phead;

	//带头+双向+循环 链表:
	//对比单链表,这里双向链表在头删时因为有哨兵位的存在
	//即使链表只剩一个结点,也不用进行判断单独处理进行置空
	//这一个结点删掉后,还有哨兵位存在
	
	/*
	//第二种方法:复用LTErase函数
	//传第一个结点地址给LTErase函数即可:
	LTErase(phead->next);
	*/
}



//求链表结点个数函数
//接收链表头指针(phead)
int LTSize(LTNode* phead)
{
	//assert断言头指针(哨兵位地址)不为空:
	assert(phead != NULL);

	int size = 0; //存放链表长度

	//创建结点指针cur进行遍历:
	//指针cur应该从哨兵位(头指针)下一个结点开始
	LTNode* cur = phead->next;

	//因为是循环链表,不是以NULL空指针结尾
	//所以应该是当指针cur遍历回到哨兵位就终止遍历:
	while (cur != phead)
		//如果只有哨兵位,链表为空,
		//phead->next还是phead,不会进行打印
	{
		++size; //遍历一遍长度+1

		//cur移向下个结点:
		cur = cur->next;
	}

	//返回链表长度:
	return size;
}



//双向链表查找函数--在双向链表中查找数据域数据为x的结点地址
//接收链表头指针(phead)、要在链表中查找的值(x)
LTNode* LTFind(LTNode* phead, LTDataType x)
{
	//assert断言头指针(哨兵位地址)不为空:
	assert(phead != NULL);

	//创建遍历指针cur,从第一个结点开始:
	LTNode* cur = phead->next;

	//使用while循环进行遍历:
	while (cur != phead)
		//如果只有哨兵位,链表为空,
		//phead->next还是phead,不会进行打印
	{
		if (cur->data == x)
			//找到要找的值了:
		{
			return cur; //返回该值结点
		}

		//调整指针:
		cur = cur->next;
	}

	//未找到则返回空指针:
	return NULL;
}



//双向链表插入函数--在pos结点之前插入数据域数据为x的结点
//接收插入位置(pos)、要插入链表的值(x)
void LTInsert(LTNode* pos, LTDataType x)
{
	//assert断言插入位置pos不为空:
	assert(pos != NULL);

	//通过pos结点获得前一个结点posPrev地址:
	LTNode* posPrev = pos->prev;

	//使用BuyLTNode函数为插入结点开辟空间:
	LTNode* newnode = BuyLTNode(x);

	//posPrev结点的后继指针next指向newnode:
	posPrev->next = newnode;

	//newnode前驱指针prev指向posPrev:
	newnode->prev = posPrev;

	//newnode后继指针next指向pos:
	newnode->next = pos;

	//pos结点前驱指针prev指向newnode:
	pos->prev = newnode;
}



//双向链表删除函数--删除pos结点
//接收要删除结点地址(pos)
void LTErase(LTNode* pos)
{
	//assert断言删除位置结点pos不为空:
	assert(pos != NULL);

	//保存要删除结点pos的前一个结点posPrev地址:
	LTNode* posPrev = pos->prev;

	//保存要删除结点pos的后一个结点posNext地址:
	LTNode* posNext = pos->next;

	//释放掉pos结点:
	free(pos);

	//将pos前结点posPrev的后继指针指向posNext:
	posPrev->next = posNext;

	//将pos后结点posNext的前驱指针指向posPrev:
	posNext->prev = posPrev;
}



//双向链表销毁函数--销毁链表
//接收要销毁链表头系欸但(phead)
void LTDestroy(LTNode* phead)
{
	//assert断言头指针(哨兵位地址)不为空:
	assert(phead != NULL);

	//创建遍历指针cur,从第一个结点开始:
	LTNode* cur = phead->next;

	//使用while循环进行遍历释放:
	while (cur != phead)
	{
		//释放前先存储下一个结点地址:
		LTNode* next = cur->next;

		//释放当前结点:
		free(cur);

		//调整指针:
		cur = next;
	}
	
	//删除完有效结点后,最后再释放哨兵位:
	free(phead);
}

            

            

---------------------------------------------------------------------------------------------

                

Test.c -- 双向链表测试文件

#define _CRT_SECURE_NO_WARNINGS 1

//双向链表函数测试文件:

//包含双向链表头文件:
#include "List.h"

//测试函数--
//LTInit、LTPushBack、LTPrintf函数
void TestList1()
{
	//初始化一个双向链表:
	LTNode* plist = LTInit();

	//初始化后使用尾插函数插入数据:
	LTPushBack(plist, 1);
	LTPushBack(plist, 2);
	LTPushBack(plist, 3);
	LTPushBack(plist, 4);
	LTPushBack(plist, 5);
	  
	//打印当前双向链表:
	LTPrint(plist);
}


//测试函数--LTPopBack函数
void TestList2()
{
	//初始化一个双向链表:
	LTNode* plist = LTInit();
	//初始化后使用尾插函数插入数据:
	LTPushBack(plist, 1);
	LTPushBack(plist, 2);
	LTPushBack(plist, 3);
	//打印当前双向链表:
	LTPrint(plist);

	//进行尾删:
	LTPopBack(plist);
	//打印当前双向链表:
	LTPrint(plist);

	//进行尾删:
	LTPopBack(plist);
	//打印当前双向链表:
	LTPrint(plist);

	//进行尾删:
	LTPopBack(plist);
	//打印当前双向链表:
	LTPrint(plist);
}


//测试函数--LTPushFront函数
void TestList3()
{
	//初始化一个双向链表:
	LTNode* plist = LTInit();
	//初始化后使用尾插函数插入数据:
	LTPushBack(plist, 1);
	LTPushBack(plist, 2);
	LTPushBack(plist, 3);
	//打印当前双向链表:
	LTPrint(plist);

	//进行头插:
	LTPushFront(plist, 1000);
	//打印当前双向链表:
	LTPrint(plist);
}


//测试函数--LTPopFront函数
void TestList4()
{
	//初始化一个双向链表:
	LTNode* plist = LTInit();
	//初始化后使用尾插函数插入数据:
	LTPushBack(plist, 1);
	LTPushBack(plist, 2);
	LTPushBack(plist, 3);
	//打印当前双向链表:
	LTPrint(plist);

	//进行头删:
	LTPopFront(plist);
	//打印当前双向链表:
	LTPrint(plist);
}


//测试函数--LTSize函数
void TestList5()
{
	//初始化一个双向链表:
	LTNode* plist = LTInit();
	//初始化后使用尾插函数插入数据:
	LTPushBack(plist, 1);
	LTPushBack(plist, 2);
	LTPushBack(plist, 3);
	//打印当前双向链表:
	LTPrint(plist);

	//计算链表长度:
	int size = LTSize(plist);
	//打印当前双向链表:
	printf("链表长度为:%d", size);
}


//测试函数--LTFind函数
void TestList6()
{
	//初始化一个双向链表:
	LTNode* plist = LTInit();
	//初始化后使用尾插函数插入数据:
	LTPushBack(plist, 1);
	LTPushBack(plist, 2);
	LTPushBack(plist, 3);
	//打印当前双向链表:
	LTPrint(plist);

	//使用查找函数:
	LTNode* find = LTFind(plist, 2);
	//打印找到的地址
	printf("0x%xn", find);
}


//测试函数--LTInsert函数
void TestList7()
{
	//初始化一个双向链表:
	LTNode* plist = LTInit();
	//初始化后使用尾插函数插入数据:
	LTPushBack(plist, 1);
	LTPushBack(plist, 2);
	LTPushBack(plist, 3);
	//打印当前双向链表:
	LTPrint(plist);

	//使用插入函数:
	LTInsert(plist->next->next, 100);
	//打印当前双向链表:
	LTPrint(plist);
}


//测试函数--LTErase函数
void TestList8()
{
	//初始化一个双向链表:
	LTNode* plist = LTInit();
	//初始化后使用尾插函数插入数据:
	LTPushBack(plist, 1);
	LTPushBack(plist, 2);
	LTPushBack(plist, 3);
	//打印当前双向链表:
	LTPrint(plist);

	//使用删除函数:
	LTErase(plist->next->next);
	//打印当前双向链表:
	LTPrint(plist);
}

//主函数:
int main()
{
	//调用测试函数:
	//TestList1();
	//TestList2();
	//TestList3();
	//TestList4();
	//TestList5();
	//TestList6();
	//TestList7();
	TestList8();

	return 0;
}

到了这里,关于【数据结构初阶】四、线性表里的链表(带头+双向+循环 链表 -- C语言实现)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

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

相关文章

  • 【数据结构初阶】八、非线性表里的二叉树(二叉树的实现 -- C语言链式结构)

    ========================================================================= 相关代码gitee自取 : C语言学习日记: 加油努力 (gitee.com)  ========================================================================= 接上期 : 【数据结构初阶】七、非线性表里的二叉树(堆的实现 -- C语言顺序结构)-CSDN博客  ==========

    2024年02月08日
    浏览(51)
  • 数据结构--队列的链表实现

    初始化 判断队列是否为空 入队 初始化 判断队列是否为空 入队 队满 链式存储――一般不会队满,除非内存不足

    2024年02月11日
    浏览(47)
  • 【c++学习】数据结构中的链表

    链表与线性表相对,链表数据在内存中的存储空间是不连续的,链表每个节点包含数据域和指针域。 下述代码实现了链表及其接口 包括增、删、查、改以及其他一些简单的功能 运行结果: 于 2024-01-23 第一次整理编写 学习时整理,不当之处烦请指正 码字不易,留个赞再走吧

    2024年01月24日
    浏览(55)
  • 【数据结构】两两交换链表 && 复制带随机指针的链表

    给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。 使用一个栈S来存储相邻两个节点即可 给你一个长度为 n 的链表,每个节点包含一个额外增加的随机指针 random ,该指针可以

    2024年04月15日
    浏览(49)
  • 数据结构:队列的链表结构(含完整代码,可复制)

    1.输出队列 2.入队一个元素 3.出队一个元素 5.建立链表队列 6.完整代码

    2024年01月16日
    浏览(48)
  • 利用C++超详细解释数据结构中的链表

    链表(Linked List)是一种常见的数据结构,它可以动态地插入和删除元素,不需要像数组那样预先分配固定大小的内存。链表中的每个元素称为节点(Node),每个节点包含一个数据值和一个指向下一个节点的指针。本教学将涵盖以下知识点: 单向链表(Singly Linked List) 双向

    2024年02月04日
    浏览(31)
  • 【数据结构】[LeetCode138. 复制带随机指针的链表]

    给你一个长度为  n  的链表,每个节点包含一个额外增加的随机指针  random  ,该指针可以指向链表中的任何节点或空节点。 构造这个链表的  深拷贝 。 深拷贝应该正好由  n  个  全新  节点组成,其中每个新节点的值都设为其对应的原节点的值。新节点的  next  指针和

    2024年02月04日
    浏览(49)
  • 【数据结构OJ题】复制带随机指针的链表

    原题链接:https://leetcode.cn/problems/copy-list-with-random-pointer/description/ 目录 1. 题目描述 2. 思路分析 3. 代码实现 此题可以分三步进行: 1. 拷贝链表的每一个结点,拷贝的结点先链接到被拷贝结点的后面。 2. 复制随机指针的链接:拷贝结点的随机指针指向被拷贝结点随机指针的下

    2024年02月12日
    浏览(69)
  • 【LeetCode】数据结构题解(9)[复制带随机指针的链表]

    所属专栏:玩转数据结构题型❤️ 🚀 博主首页:初阳785❤️ 🚀 代码托管:chuyang785❤️ 🚀 感谢大家的支持,您的点赞和关注是对我最大的支持!!!❤️ 🚀 博主也会更加的努力,创作出更优质的博文!!❤️ 🚀 关注我,关注我,关注我,重要的事情说三遍!!!!!

    2024年02月11日
    浏览(58)
  • 【数据结构】LeetCode升级版的环形链表,复制带随机指针的链表

              1、题目说明           2、题目解析           1、题目说明           2、题目解析      1、题目说明 题目链接: 升级版的环形链表  给定一个链表的头节点 head ,返回链表开始入环的第一个节点。  如果链表无环,则返回NULL。 如果链表中有某个节点,可以通

    2024年01月16日
    浏览(62)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包