数据结构:双向链表

文章目录

单链表的链式存储结构的结点中只有一个指示直接后继的指针域,由此,从某个结点出发只能顺指针向后寻查其他结点。若要寻查结点的直接前驱,则必须从表头指针出发。换句话说。在单链表中,查找直接后继结点的执行时间为0(1),而查找直接前驱的执行时间为0(n)。为克服单链表这种单向性的缺点,可利用双向链表(Double Linked List)。
顾名思义,在双向链表的结点中有两个指针域,一个指向直接后继,另一个指向直接前驱。

1.双链表的结构定义

c 复制代码
typedef struct DuLNode {  
    ElemType data;  
    struct DuLNode *prior;  
    struct DuLNode *next;  
}DuLnode,*DuLinkList;

双向循环链表

和单链的循环表类似,双向链表也可以有循环表

  • 让头结点的前驱指针指向链表的最后一个结点
  • 让最后一个结点的后继指针指向头结点。

2.双链表结构的对称性

c 复制代码
p->prior->next = p = p->next->prior

在双向链表中有些操作(如:istLength、GetElem等),因仅涉及一个方向的指针,故它们的算法与线性链表的相同。但在插入、删除时,则需同时修改两个方向上的指针,两者的操作的时间复杂度均为O()。

2.双向链表的插入

  1. s->prior=p->prior;
  2. p->prior->next=s;
  3. s->next=p;
  4. p->prior=s;
c 复制代码
void insertBefore(Node** head, Node* p, int value) {
    Node* s = (Node*)malloc(sizeof(Node)); // 创建新节点
    if (!s) return; // 内存分配失败处理

    s->data = value; // 设置新节点的数据
    s->next = p;     // 设置新节点的后继为p
    s->prior = p->prior; // 设置新节点的前驱为p的前驱

    if (p->prior) { // 如果p不是头节点
        p->prior->next = s; // 更新p的前驱的后继指向新节点
    } else { // 如果p是头节点
        *head = s; // 更新头指针
    }

    p->prior = s; // 更新p的前驱指向新节点
}

3.双向链表的删除

  1. p->prior-next=p->next;
  2. p->next->prior=p->prior;
c 复制代码
void deleteNode(Node** head, Node* p) {
    if (p == NULL) return; // 检查p是否为NULL

    // 更新前驱节点的后继指针
    if (p->prior != NULL) {
        p->prior->next = p->next;
    } else {
        *head = p->next; // 如果p是头节点,更新头指针
    }

    // 更新后继节点的前驱指针
    if (p->next != NULL) {
        p->next->prior = p->prior;
    }

    // 释放被删除节点的内存
    free(p);
}

4.顺序表和链表的比较

链式存储结构的优点

  • 结点空间可以动态申请和释放;
  • 数据元素的逻辑次序靠结点的指针来指示,插入和删除时不需要移动数据元素。
    链式存储结构的缺点
  • 存储密度小,每个结点的指针域需额外占用存储空间。当每个结点的数据域所占字节不多时,指针域所占存储空间的比重显得很大。
  • 链式存储结构是非随机存取结构。对任一结点的操作都要从头指针依指针链查找到该结点,这增加了算法的杂度。
    存储结构
比较项目 顺序表 链表
预先分配,会导致空间闲置或溢出现象 ×
动态分配,不会出现存储空间闲置或溢出现象 ×
不用为表示结点间的逻辑关系而增加额外的存储开销,存储密度等于1 ×
需要借助指针来体现元素间的逻辑关系,存储密度小于1 ×
随机存取,按位置访问元素的时间复杂度为O(1) ×
顺序存取,按位置访问元素时间复杂度为O(n) ×
平均移动约表中一半元素,时间复杂度为O(n),时间复杂度为O(1) ×
不需移动元素,确定插入、删除位置后,时间复杂度为O(1) ×

适用情况

  • 顺序表:
    1. 表长变化不大,且能事先确定变化的范围
    2. 很少进行插入或删除操作,经常按元素位置序号访问数据元素
  • 链表:
    1. 长度变化较大
    2. 频繁进行插入或删除操作
相关推荐
Dong雨24 分钟前
六大排序算法:插入排序、希尔排序、选择排序、冒泡排序、堆排序、快速排序
数据结构·算法·排序算法
茶猫_1 小时前
力扣面试题 39 - 三步问题 C语言解法
c语言·数据结构·算法·leetcode·职场和发展
初学者丶一起加油1 小时前
C语言基础:指针(数组指针与指针数组)
linux·c语言·开发语言·数据结构·c++·算法·visual studio
半盏茶香3 小时前
C语言勘破之路-最终篇 —— 预处理(上)
c语言·开发语言·数据结构·c++·算法
2401_858286113 小时前
118.【C语言】数据结构之排序(堆排序和冒泡排序)
c语言·数据结构·算法
没事就去码3 小时前
RBTree(红黑树)
数据结构·c++
就爱学编程3 小时前
重生之我在异世界学编程之数据结构与算法:单链表篇
数据结构·算法·链表
CSCN新手听安9 小时前
list的常用操作
数据结构·list
梅茜Mercy10 小时前
数据结构:链表(经典算法例题)详解
数据结构·链表
青春男大11 小时前
java栈--数据结构
java·开发语言·数据结构·学习·eclipse