数据结构(初阶)笔记归纳6:双向链表的实现

双向链表的实现

目录

双向链表的实现

一、双向链表的概念及结构

1.1.双向链表的概念

1.2.双向链表的结构

1.2.1.结构图

1.2.2.节点的组成

二、双向链表的实现

2.1.双向链表文件结构

2.2.头文件编写(List.h)

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.pos位置后插入数据

2.2.11.删除pos节点

2.2.12.双向链表的销毁

2.3.源文件编写(List.c)

2.3.1.头文件包含

2.3.2.节点申请函数

2.3.3.双向链表的初始化

2.3.4.双向链表的打印

2.3.5.双向链表的尾插

2.3.6.双向链表的头插

2.3.7.双向链表的尾删

2.3.8.双向链表的头删

2.3.9.双向链表的查找

2.3.10.pos位置后插入数据

2.3.11.删除pos节点

2.3.12.双向链表的销毁

2.4.测试文件编写(test.c)

2.4.1.头文件包含

2.4.2.测试文件01

三、总结


一、双向链表的概念及结构

1.1.双向链表的概念

带头双向循环链表

1.2.双向链表的结构

1.2.1.结构图
1.2.2.节点的组成

**数据域:**用于存储该节点的数据

**指针域:**用于存储上一个节点的地址(前驱指针)和下一个节点的地址(后置指针)

cpp 复制代码
struct ListNode
{
    int data;//节点数据
    struct ListNode* next;//指向下一个节点的指针变量
    struct ListNode* prev;//指向前一个节点的指针变量
};

二、双向链表的实现

2.1.双向链表文件结构

  • 头文件(List.h):双向链表的结构创建,双向链表的方法声明
  • 源文件(List.c):双向链表的方法实现
  • 测试文件(test.c):测试数据结构的方法

2.2.头文件编写(List.h)

2.2.1.头文件包含
cpp 复制代码
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
2.2.2.节点数据类型重定义
cpp 复制代码
typedef int LTDataType;
2.2.3.双向链表节点定义
cpp 复制代码
typedef struct ListNode
{
	LTDataType data;
	struct ListNode* next;
	struct ListNode* prev;
}LTNode;
2.2.4.双向链表的初始化

法1:

cpp 复制代码
void LTInit(LTNode** pphead);

法2:

cpp 复制代码
LTNode* LTInit();
2.2.5.双向链表的尾插
cpp 复制代码
void LTPushBack(LTNode* phead,LTDataType x);

**注:**哨兵位不会改变,所以传一级指针即可

2.2.6.双向链表的头插
cpp 复制代码
void LTPushFront(LTNode* phead,LTDataType x);
2.2.7.双向链表的尾删
cpp 复制代码
void LTPopBack(LTNode* phead);
2.2.8.双向链表的头删
cpp 复制代码
void LTPopFront(LTNode* phead);
2.2.9.双向链表的查找
cpp 复制代码
LTNode* LTFind(LTNode* phead, LTDataType x);
2.2.10.pos位置后插入数据
cpp 复制代码
void LTInsert(LTNode* pos, LTDataType x);
2.2.11.删除pos节点
cpp 复制代码
void LTErase(LTNode* pos);
2.2.12.双向链表的销毁
cpp 复制代码
void LTDesTroy(LTNode* phead);

2.3.源文件编写(List.c)

2.3.1.头文件包含
cpp 复制代码
#include "SList.h"
2.3.2.节点申请函数
cpp 复制代码
LTNode* LTBuyNode(LTDataType x)
{
	LTNode* node = (LTNode*)malloc(sizeof(LTNode));
	if (node == NULL)
	{
		perror("malloc fail!");
		exit(1);
	}

	node->data = x;
	node->prev = node->next = node;

	return node;
}

**注:**由于双向链表是循环链表,所以新节点的前驱指针和后置指针都先指向自己

2.3.3.双向链表的初始化

法1:

cpp 复制代码
void LTInit(LTNode** pphead)
{
	/*创建哨兵位*/
	*pphead = LTBuyNode(-1);
}

法2:

cpp 复制代码
LTNode* LTInit()
{
	LTNode* phead = LTBuyNode(-1);
	return phead;
}
2.3.4.双向链表的打印
cpp 复制代码
void LTPrint(LTNode* phead)
{
	LTNode* pcur = phead->next;
	while (pcur != phead)
	{
		printf("%d->", pcur->data);
		pcur = pcur->next;
	}
	printf("\n");
}

**解析:**从头节点的下一个节点开始打印,当节点再次走到头节点时停止打印

2.3.5.双向链表的尾插
cpp 复制代码
void LTPushBack(LTNode* phead, LTDataType x)
{
	assert(phead);
	LTNode* newnode = LTBuyNode(x);

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

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

解析:

  • 断言判断传参是否为NULL
  • 创建临时变量newnode接收新节点地址
  • 新节点的前驱指针指向上一个节点
  • 新节点的后置指针指向哨兵位
  • 上一个节点的后置指针指向新节点
  • 哨兵位的前驱指针指向新节点
2.3.6.双向链表的头插
cpp 复制代码
void LTPushFront(LTNode* phead, LTDataType x)
{
	assert(phead);
	LTNode* newnode = LTBuyNode(x);

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

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

解析:

  • 断言判断传参是否为NULL
  • 创建临时变量newnode接收新节点地址
  • 新节点的后置指针指向下一个节点
  • 新节点的前驱指针指向哨兵位
  • 下一个节点的前驱节点指向新节点
  • 哨兵位的后置指针指向新节点
2.3.7.双向链表的尾删
cpp 复制代码
void LTPopBack(LTNode* phead)
{
	assert(phead && phead->next != phead);

	LTNode* del = phead->prev;
	
	del->prev->next = phead;
	phead->prev = del->prev;

	free(del);
	del = NULL;
}

解析:

  • 断言判断传参是否为NULL,双向链表是否只有哨兵位
  • 创建临时变量del,存放要删除的尾节点地址
  • 尾节点前一个节点的后置指针指向哨兵位
  • 哨兵位的前驱指针指向尾节点的前一个节点
  • 释放尾节点,将del置为NULL
2.3.8.双向链表的头删
cpp 复制代码
void LTPopFront(LTNode* phead)
{
	assert(phead && phead->next != phead);

	LTNode* del = phead->next;

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

	free(del);
	del = NULL;
}

解析:

  • 断言判断传参是否为NULL,双向链表是否只有哨兵位
  • 创建临时变量del,存放要删除的哨兵位下一个节点地址
  • 哨兵位下下个节点的前驱指针指向哨兵位
  • 哨兵位的后置指针指向哨兵位下下个节点
  • 释放哨兵位下一个节点,将del置为NULL
2.3.9.双向链表的查找
cpp 复制代码
LTNode* LTFind(LTNode* phead, LTDataType x)
{
	LTNode* pcur = phead->next;
	while (pcur != phead)
	{
		if (pcur->data == x)
		{
			return pcur;
		}
		pcur = pcur->next;
	}
	return NULL;
}
2.3.10.pos位置后插入数据
cpp 复制代码
void LTInsert(LTNode* pos, LTDataType x)
{
	assert(pos);

	LTNode* newnode = LTBuyNode(x);

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

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

解析:

  • 断言判断传参是否为NULL
  • 创建临时变量newnode接收新节点地址
  • 新节点的后置指针指向pos位下一个节点
  • 新节点的前驱指针指向pos位的节点
  • pos位下一个节点的前驱指针指向新节点
  • pos位节点的后置指针指向新节点

2.3.11.删除pos节点
cpp 复制代码
void LTErase(LTNode* pos)
{
	assert(pos);

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

	free(pos);
	pos = NULL;
}

解析:

  • 断言判断传参是否为NULL
  • pos位下一个节点的前驱指针指向pos上一个节点
  • pos位上一个节点的后置指针指向pos下一个节点
  • 释放pos位节点,将pos置为NULL
2.3.12.双向链表的销毁
cpp 复制代码
void LTDesTroy(LTNode* phead)
{
	assert(phead);

	LTNode* pcur = phead->next;
	while (pcur != phead)
	{
		LTNode* next = pcur->next;
		free(pcur);
		pcur = next;
	}
	free(phead);
	phead = NULL;
}

解析:

  • 断言判断传参是否为NULL
  • 创建临时变量pcur存放哨兵位下一个节点
  • 循环释放每一个节点
  • 创建临时变量next存放pcur的下一个节点
  • 释放完pcur节点后pcur走到下一个节点
  • 最后释放哨兵位,将phead置为NULL

2.4.测试文件编写(test.c)

2.4.1.头文件包含
cpp 复制代码
#include "SList.h"
2.4.2.测试文件01
cpp 复制代码
void ListTest01()
{
    /*哨兵位初始化 法1*/
	LTNode* plist = NULL;
	LTInit(&plist);

     /*哨兵位初始化 法2*/
    LTNode* plist = LTInit();

    /*双向链表的尾插*/
    LTPushBack(plist,1);
    LTPushBack(plist,2);
    LTPushBack(plist,3);

    /*双向链表的头插*/
    LTPushFront(plist,1);
    LTPushFront(plist,2);
    LTPushFront(plist,3);

    /*双向链表的尾删*/
    LTPopBack(plist);

    /*双向链表的头删*/
    LTPopFront(plist);

    /*双向链表的查找*/
    LTNode* find = LTFind(plist,3);
    if(find == NULL)
    {
        printf("找不到!\n");
    }
    else
    {
        printf("找到了")
    }

    /*pos位后插入数据*/
    LTInsert(find,66);

    /*删除pos节点*/
    LTErase(find);

    /*双向链表的打印*/
    LTPrint(plist);

    /*双向链表的销毁*/
    LTDesTroy(plist);
    
    /*手动置空*/
    plist = NULL;
}

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

三、总结

本篇博客是对于数据结构中双向链表实现的整理归纳,后续还会更新链表OJ试题等内容,如果对你有帮助,欢迎点赞+收藏+关注,让我们一起共同进步🌟~

相关推荐
不会代码的小猴2 小时前
Linux环境编程第二天笔记
linux·笔记
我命由我123452 小时前
图像格式:RGB、BGR、RGBA、BGRA
图像处理·经验分享·笔记·学习·学习方法·photoshop·设计规范
菩提小狗2 小时前
小迪安全2022-2023|第176天:SRC挖掘-CNVD_EDU_通用事件_资产规则_审核评级_思路知识_笔记|web安全|渗透测试|
笔记·安全·web安全
xixi09242 小时前
selenium——浏览器基础操作(启动/访问/窗口控制)
开发语言·python
狐572 小时前
2026-01-20-LeetCode刷题笔记-3314-构造最小位运算数组I
笔记·算法·leetcode
0和1的舞者2 小时前
非力扣hot100-二叉树专题-刷题笔记(一)
笔记·后端·算法·leetcode·职场和发展·知识
FMRbpm2 小时前
树的练习7--------LCR 052.递增顺序搜索树
数据结构·c++·算法·leetcode·深度优先·新手入门
不吃洋葱.2 小时前
js主要内容
开发语言·javascript·ecmascript
Rhys..2 小时前
python + selenium 如何定位动态元素
开发语言·python·selenium