数据结构——双链表

简介: 数据结构——双链表

我宁愿靠自己的力量,打开我的前途,而不愿求有力者垂青


文章目录


双线向链表各接口函数名或变量名


双向链表接口实现源码


快速索引【头文件及函数声明】


双向链表接口实现


双向链表的构造分析


双向链表的定义及初始化


双向链表的插入和删除


往期回顾:


数据结构——单链表


数据结构——顺序表


 大家好,我是纪宁。


 这篇文章向大家介绍一种相对单链表性能更优的链表——双向链表,它能更高效的实现数据的插入、删除和查找等。


 文章前半段是双向链表对应名称和源码,文章后半段是对双向链表实现的具体解释。


双线向链表各接口函数名或变量名

LTDataType 双向链表数据类型重命名

ListNode 双向链表结构体

LTNode 双向链表的重命名

BuyLTNode 创建一个结点

LTInit 初始化结点

LTPrint 打印双向链表

LTPushBack 尾插

LTPopBack 尾删

LTPushFront 头插

LTPopFront 头删

LTSize 计算双向链表元素个数

LTFind 找链表元素

LTInsert 在pos之前插入结点

LTErase 删除pos位置的结点

双向链表接口实现源码

快速索引【头文件及函数声明】

#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* BuyLTNode(LTDataType x); //创建一个新节点

LTNode* LTInit(); //哨兵位的头结点

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

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

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

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

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

LTNode* LTFind(LTNode* phead, LTDataType x);//寻找结点

void LTInsert(LTNode*phead,LTNode* pos, LTDataType x); //在pos之前插入结点


双向链表接口实现

LTNode* BuyLTNode(LTDataType x)

{

LTNode* nownode =(LTNode*)malloc(sizeof(LTNode));

if (nownode == NULL)

{

 perror("malloc fail");

}

nownode->Data = x;

nownode->next = NULL;

nownode->prev = NULL;

return nownode;

}

LTNode* LTInit()

{

LTNode* phead = BuyLTNode(0);

phead->next = phead;

phead->prev = phead;

return phead;

}

void LTPushBack(LTNode* phead, LTDataType x)

{

assert(phead);

LTNode* tail = phead->prev;

LTNode* nownode = BuyLTNode(x);

nownode->next = phead;

nownode->prev = tail;

tail->next = nownode;

phead->prev = nownode;

}

void LTPrint(LTNode* phead)

{

assert(phead);

printf("phead<=>");

LTNode* cur = phead;

while (cur->next!=phead)

{

 printf("%d<=>", cur->next->Data);

 cur = cur->next;

}

printf("\n");

}

void LTPopBack(LTNode* phead)

{

assert(phead);

assert(phead->next != phead);//只有哨兵位的时候不能删

LTNode* tail = phead->prev;

LTNode* tailPrev = tail->prev;

tailPrev->next = tail->next;

phead->prev = tailPrev;

free(tail);

}

void LTPushFront(LTNode* phead, LTDataType x)

{

assert(phead);

LTNode* first = phead->next;

LTNode* nownode = BuyLTNode(x);

nownode->next = first;

nownode->prev = phead;

phead->next = nownode;

first->prev = nownode;

}

void LTPopFront(LTNode* phead)

{

assert(phead);

assert(phead->next != phead);//只有哨兵位的时候不能删除

LTNode* first = phead->next;

LTNode* second = first->next;

phead->next = second;

second->prev = phead;

free(first);

}

LTNode* LTFind(LTNode* phead, LTDataType x)

{

assert(phead);

LTNode* cur = phead;

while (cur->next != phead)

{

 if (cur->next->Data == x)

  return  cur->next;

 cur = cur->next;

}

return NULL;

}

void LTInsert(LTNode* phead, LTNode* pos, LTDataType x)

{

assert(phead);

assert(pos);

LTNode* cur = phead;

while (cur->next != pos)

{

 cur = cur->next;

}

LTNode*nownode = BuyLTNode(x);

nownode->next = pos;

nownode->prev = cur;

cur->next = nownode;

pos->prev = nownode;

}

void LTErase(LTNode* phead, LTNode* pos)

{

assert(pos&&phead);

assert(pos->next != pos);

assert(phead->next != phead);

LTNode* cur = phead;

LTNode* posNext = pos->next;

while (cur->next != pos)

{

 cur = cur->next;

}

cur->next = posNext;

posNext->prev = cur;

free(pos);

}


双向链表的构造分析

 双向链表,相对于单链表不同的是双向链表的节点有两个指针域,一个指向后一个节点,另一个指向前一个节点,默认双向链表都是有带哨兵位的头节点,哨兵位的头节点中储存着第一个有效节点的地址(phead->next)和最后一个有效节点的地址(phead->prev)。


单双链表逻辑图对比


单双链表物理图对比




双向链表的定义及初始化



 双向链表中有一个数据域和两个指针域,一个指针指向下一个节点的地址,一个指针指向上一个节点的地址,将这个双链表的结构再重命名。




 双向链表在新开辟节点的时候,要先开辟一个节点大小的空间,将它的 next 和 NULL 指向空,然后将它的数据域值赋为 x。


双向链表的插入和删除

 双向链表的删除,首先要要明确的一点是不能删除哨兵位这个头节点,因为它是整个双向链表的结构支撑。删除的时候,要找到即将删除节点的上一个节点和下一个节点,将上一个节点的 next 指针指向下一个节点,将下一个节点的 prev 指针指向 上一个节点。最后,将删除的节点空间释放。


 双向链表的插入,在插入的时候,理论上在任何位置都是可以插入节点的。因为在初始化的时候,定义新创建节点的指针域都为空。所以在插入的时候,要改变插入节点的两个指针域,将它的next 指针指向下一个节点,将它的 prev 指针指向上一个节点。同样,将上一个节点的 next 指针指向这个新节点,将下一个指针的 prev 指针指向这个新节点。


相关文章
|
19天前
|
存储 C语言
【数据结构】手把手教你单链表(c语言)(附源码)
本文介绍了单链表的基本概念、结构定义及其实现方法。单链表是一种内存地址不连续但逻辑顺序连续的数据结构,每个节点包含数据域和指针域。文章详细讲解了单链表的常见操作,如头插、尾插、头删、尾删、查找、指定位置插入和删除等,并提供了完整的C语言代码示例。通过学习单链表,可以更好地理解数据结构的底层逻辑,提高编程能力。
45 4
|
20天前
|
算法 安全 搜索推荐
2024重生之回溯数据结构与算法系列学习之单双链表精题详解(9)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构王道第2.3章之IKUN和I原达人之数据结构与算法系列学习x单双链表精题详解、数据结构、C++、排序算法、java、动态规划你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!
|
20天前
|
存储 Web App开发 算法
2024重生之回溯数据结构与算法系列学习之单双链表【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构之单双链表按位、值查找;[前后]插入;删除指定节点;求表长、静态链表等代码及具体思路详解步骤;举例说明、注意点及常见报错问题所对应的解决方法
|
1月前
|
存储 Java
数据结构第三篇【链表的相关知识点一及在线OJ习题】
数据结构第三篇【链表的相关知识点一及在线OJ习题】
26 7
|
1月前
|
存储 安全 Java
【用Java学习数据结构系列】探索顺序表和链表的无尽秘密(附带练习唔)pro
【用Java学习数据结构系列】探索顺序表和链表的无尽秘密(附带练习唔)pro
23 3
|
1月前
|
算法 Java
数据结构与算法学习五:双链表的增、删、改、查
双链表的增、删、改、查操作及其Java实现,并通过实例演示了双向链表的优势和应用。
16 0
数据结构与算法学习五:双链表的增、删、改、查
|
19天前
|
C语言
【数据结构】双向带头循环链表(c语言)(附源码)
本文介绍了双向带头循环链表的概念和实现。双向带头循环链表具有三个关键点:双向、带头和循环。与单链表相比,它的头插、尾插、头删、尾删等操作的时间复杂度均为O(1),提高了运行效率。文章详细讲解了链表的结构定义、方法声明和实现,包括创建新节点、初始化、打印、判断是否为空、插入和删除节点等操作。最后提供了完整的代码示例。
38 0
【数据结构】——双向链表详细理解和实现
【数据结构】——双向链表详细理解和实现
|
1月前
|
存储 Java
【数据结构】链表
【数据结构】链表
18 1
|
1月前
|
存储 缓存
数据结构3——双向链表
数据结构3——双向链表
112 1