数据结构——6.跳舞链和静态链表

第一部分:基础概念回顾

在学习跳舞链之前,必须先掌握双向链表和循环链表的概念。

1. 循环链表 (Circular Linked List)

  • 定义:它是一种特殊的单链表,其最后一个结点的 next 指针不再指向 NULL,而是指向链表的第一个结点(或头结点/哨兵结点),形成一个环。
  • 特点:从链表中的任意一个结点出发,都可以遍历到整个链表的所有结点。
  • 判断空表:对于带头结点 head 的循环链表,当 head->next == head 时,链表为空。
  • 应用:约瑟夫问题 (Josephus Problem) 是其经典应用场景。

2. 双向链表 (Doubly Linked List)

  • 定义:每个结点除了有数据域 data 和指向后继结点的 next(或 right)指针外,还有一个指向前驱结点的 prev(或 left)指针。
  • 特点:可以双向遍历,从一个结点可以很方便地找到它的前驱和后继,使得插入和删除操作更加灵活。
  • 结构:通常有一个头指针 head 指向第一个结点,尾结点的 right 指针为 NULL;头结点的 left 指针也为 NULL

第二部分:核心知识点一:跳舞链 (Dancing Links)

“跳舞链”这个名字听起来很酷,但其本质并不复杂。PPT中提到的“跳舞链”实际上是 带哨兵结点的双向循环链表

1. 结构定义

  • 它是一个双向的、循环的链表。
  • 它包含一个特殊的“哨兵”结点(通常称为 head0 号结点),这个结点不存储实际数据,只用于辅助操作,使代码更简洁。
  • 表头:第一个有数据的元素是 head->right
  • 表尾:最后一个有数据的元素是 head->left
  • 空表示:当 head->right == headhead->left == head 时,链表为空。

这种结构的最大优势在于 插入和删除操作极其高效且代码统一。因为链表是循环的,且有哨兵结点,所以不需要为“在表头/表尾插入/删除”或“在空表插入”等情况写额外的判断逻辑。所有位置的插入和删除操作都可以用同样的代码完成。

2. 核心操作:删除与插入

这是跳舞链的精髓,笔试和机试都极有可能考到。

删除结点 x
想象一下 x 结点和它的左邻居 L、右邻居 R。要删除 x,就是让 LR “手拉手”,直接跳过 x

  • x 的左邻居 Lright 指针,原本指向 x,现在应该指向 x 的右邻居 R
    • 代码:x->left->right = x->right;
  • x 的右邻居 Rleft 指针,原本指向 x,现在应该指向 x 的左邻居 L
    • 代码:x->right->left = x->left;

恢复(插入)结点 x
删除是“跳过”,恢复就是让 LR 松开手,重新和 x 连接。

  • x 的左邻居 Lright 指针,重新指向 x
    • 代码:x->left->right = x;
  • x 的右邻居 Rleft 指针,重新指向 x
    • 代码:x->right->left = x;

这两个操作的时间复杂度都是 O(1)O(1)O(1),非常高效。


第三部分:核心知识点二:静态链表 (Static Linked List)

1. 为什么需要静态链表?

  • 语言限制:在一些早期的编程语言(如 FORTRAN, BASIC)中,没有指针数据类型,无法使用 newmalloc 动态分配内存。
  • 性能考量:在现代C++等语言中,如果需要频繁地创建和销毁结点,malloc/freenew/delete 的系统调用开销可能较大。静态链表预先分配一大块连续内存,所有操作都在这块内存中进行,速度更快。

2. 实现原理

其核心思想是 用数组来模拟链表。我们不使用内存地址指针,而是使用数组下标(Index) 来“指向”下一个元素。

  • 一个大数组(或几个平行数组)作为内存池。
  • 每个结点包含两个关键部分:data(数据域)和 next(游标/指针域)。next 存储的是下一个结点在数组中的下标。
  • 通常用数组的 0 号元素作为哨兵结点。

3. 两种实现方式

方式一:结构体数组 (Struct Array)

// C++
#include <iostream>
#define MAXN 1000

struct Node {
   
   
    int data;
    int next;
};

Node link[MAXN];
int tot = 0; // 当前已分配的结点计数器

// 初始化哨兵结点
void init() {
   
   
    link[0].next = 0; // 0的next指向0,表示链表为空
}

// 头插法
void insert_head(int val) {
   
   
    tot++;
    link[tot].data = val;
    link[tot].next = link[0].next;
    link[0].next = tot;
}

// 遍历
void print_list() {
   
   
    for 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱看烟花的码农

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值