数据结构:双向链表

文章目录

单链表的链式存储结构的结点中只有一个指示直接后继的指针域,由此,从某个结点出发只能顺指针向后寻查其他结点。若要寻查结点的直接前驱,则必须从表头指针出发。换句话说。在单链表中,查找直接后继结点的执行时间为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. 频繁进行插入或删除操作
相关推荐
y_m_h36 分钟前
leetcode912.排序数组的题解
数据结构·算法
1 9 J37 分钟前
数据结构 C/C++(实验三:队列)
c语言·数据结构·c++·算法
921正在学习编程41 分钟前
数据结构之二叉树前序,中序,后序习题分析(递归图)
c语言·数据结构·算法·二叉树
毕竟秋山澪1 小时前
岛屿数量 广搜版BFS C#
数据结构·算法·宽度优先
乔冠宇2 小时前
环状 DNA 序列的最小表示法
java·数据结构·算法
vir022 小时前
找出目标值在数组中的开始和结束位置(二分查找)
数据结构·c++·算法·leetcode
薄荷冰ovo2 小时前
C++中的提供的四种类型转换方式;
数据结构·c++
CodeHackerBhx4 小时前
归并排序和随机化快速排序
数据结构·算法·排序算法
福楠5 小时前
[LeetCode] 1137. 第N个泰波那契数
数据结构·c++·算法·leetcode
妈妈说名字太长显傻6 小时前
【数据结构】二叉树——判断是否为完全二叉树
数据结构