数 据 结 构 进 阶:哨 兵 位 的 头 结 点 如 何 简 化 链 表 操 作
- [哨 兵 位 的 头 结 点](#哨 兵 位 的 头 结 点)
- [第 一 题 - - - 合 并 两 个 有 序 链 表](#第 一 题 - - - 合 并 两 个 有 序 链 表)
-
- [方 法 一 - - - 不 带 哨 兵 位 的 头 节 点](#方 法 一 - - - 不 带 哨 兵 位 的 头 节 点)
- [方 法 二 - - - 带 有 哨 兵 位 的 头 节 点](#方 法 二 - - - 带 有 哨 兵 位 的 头 节 点)
- [第 二 题 - - - 链 表 分 割](#第 二 题 - - - 链 表 分 割)
- [总 结](#总 结)
💻作 者 简 介:曾 与 你 一 样 迷 茫,现 以 经 验 助 你 入 门 数据 结 构。
💡个 人 主 页:@笑口常开xpr 的 个 人 主 页
📚系 列 专 栏:硬 核 数 据 结 构 与 算 法
✨代 码 趣 语:哨 兵 位 是 数 据 结 构 中 的 一 个 特 殊 存 在,它 如 同 一 个 忠 诚 的 卫 士,守 护 着 数 据 结 构 的 边 界 与 秩 序。
💪代 码 千 行,始 于 坚 持,每 日 敲 码,进 阶 编 程 之 路。
📦gitee 链 接:gitee

在 数 据 结 构 的 世 界 里,每 一 种 设 计 都 可 能 孕 育 出 惊 人 的 效 率 变 革。你 是 否 深 思 过,一 组 精 心 组 织 的 数 据 究 竟 能 创 造 怎 样 的 奇 迹?每 一 次 挖 掘 底 层 原 理,都 是 与 计 算 机 智 慧 的 巅 峰 对 话;每 一 次 剖 析 存 储 模 式,都 在 破 解 数 据 世 界 的 终 极 密 码。准 备 好 迎 接 这 场 盛 宴 了 吗?让 我 们 一 同 探 寻 链 表 中 哨 兵 位 的 头 结 点 的 无 尽 奥 秘,见 证 它 如 何 重 塑 数 字 时 代 的 运 行 法 则!
哨 兵 位 的 头 结 点
定 义
在 普 通 链 表 中,头 指 针 直 接 指 向 第 一 个 结 点。而 在 带 有 哨 兵 位 头 结 点 的 链 表 中,我 们 在 链 表 的 最 前 面 额 外 添 加 一 个 节 点,这 个 节 点 不 存 储 实 际 的 数 据,仅 仅 作 为 一 个 标 记,也 就 是 所 谓 的 "哨 兵",即 这 个 节 点 起 到 站 岗 作 用,用 来 简 化 链 表 操 作。如 果 一 个 链 表 有 哨 兵 节 点 的 话,那 么 线 性 表 的 第 一 个 元 素 应 该 是 链 表 的 第 二 个 节 点。这 个 哨 兵 结 点 的 指 针 域 指 向 链 表 的 第 一 个 实 际 节 点。
优 势
1、统 一 空 链 表 和 非 空 链 表 的 处 理
在 普 通 链 表 中,空 链 表 和 非 空 链 表 的 插 入、删 除 操 作 可能 需 要 特 殊 处 理,因 为 头 指 针 在 空 链 表 时 为 NULL,而 非 空 时 指 向 第 一 个 节 点。而 使 用 哨 兵 位 头 结 点 后,无 论 链 表 是 否 为 空,头 结 点 始 终 存 在,操 作 更 加 统 一。
2、简 化 边 界 条 件 处 理
在 删 除 操 作 中,如 果 要 删 除 第 一 个 节 点,普 通 链 表 需 要 修 改 头 指 针,而 使 用 哨 兵 位 头 结 点 后,删 除 第 一 个 节 点 的 操 作 和 删 除 其 他 节 点 的 操 作 完 全 一 致,无 需 特 殊 处 理。
3、提 高 代 码 健 壮 性
哨 兵 位 头 结 点 减 少 了 对 空 指 针 的 判 断,降 低 了 代 码 出 错 的 可 能 性,使 代 码 更 加 健 壮。
使 用 场 景
哨 兵 位 头 结 点 适 用 于 需 要 频 繁 插 入 和 删 除 链 表 头 部 节 点 的 情 况 下。
注 意 事 项
(1)、哨 兵 位 头 结 点 不 存 储 实 际 数 据,它 的 存 在 仅 仅 是 为 了 简 化 操 作。
(2)、在 释 放 链 表 时,需 要 记 得 释 放 哨 兵 位 头 结 点 的 内 存。
(3)、在 遍 历 链 表 时,要 从 哨 兵 位 头 结 点 的 下 一 个 节 点 开 始, 而 不 是 从 哨 兵 位 头 结 点 本 身 开 始。
第 一 题 - - - 合 并 两 个 有 序 链 表
描 述:将 两 个 升 序 链 表 合 并 为 一 个 新 的 升 序 链 表 并 返 回。新 链 表 是 通 过 拼 接 给 定 的 两 个 链 表 的 所 有 节 点 组 成 的。
示例 1:
输 入:l1 = [1,2,4], l2 = [1,3,4]
输 出:[1,1,2,3,4,4]

示 例 2:
输 入:l1 = [ ], l2 = [ ]
输 出:[ ]
示 例 3:
输 入:l1 = [ ], l2 = [ 0 ]
输 出:[0]
提 示:
两 个 链 表 的 节 点 数 目 范 围 是 [0, 50]
-100 <= Node.val <= 100
l1 和 l2 均 按 非 递 减 顺 序 排 列
方 法 一 - - - 不 带 哨 兵 位 的 头 节 点
思 路 分 析
针 对 这 道 题,可 以 利 用 链 表 已 经 有 序 的 特 性,通 过 比 较 两 个 链 表 当 前 节 点 的 值,将 较 小 值 的 节 点 依 次 连 接 起 来,形 成 一 个 新 的 有 序 链 表。
解 题 步 骤
1、处 理 边 界 情 况
若 任 一 链 表 为 空,直 接 返 回 另 一 链 表。
2、初 始 化 指 针
使 用 双 指 针 cur1 和 cur2 分 别 遍 历 两 个 链 表。
通 过 head 和 tail 指 针 维 护 新 链 表 的 头 节 点 和 尾 节 点。
3、比 较 并 合 并 节 点
循 环 条 件 :同 时 遍 历 两 个 链 表,直 至 其 中 一 个 链 表 遍 历 完 毕。
节 点 选 择:比 较 cur1 和 cur2 的 值,选 择 较 小 值 的 节 点 加 入 新 链 表。
4、尾 插 操 作
若 新 链 表 为 空(head为 NULL),初 始 化 head 和 tail。
否 则 将 选 中 节 点 连 接 到 tail 之 后,并 更 新 tail。
5、处 理 剩 余 节 点
将 未 遍 历 完 的 链 表 的 剩 余 部 分 直 接 连 接 到 新 链 表 尾 部。
温 馨 提 示:读 者 们 ,先 自 己 写 代 码,这 是 提 升 编 程 能 力 的 好 机 会。若 未 达 要 求 ,别 气 馁 ,参 考 下 文 解 释 会 有 新 收 获。
下 面 展 示
代 码 示 例
。
javascript
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2)
{
struct ListNode* cur1 = list1;
struct ListNode* cur2 = list2;
struct ListNode* head = NULL;
struct ListNode* tail = NULL;
if(cur1==NULL)
{
return cur2;
}
if(cur2==NULL)
{
return cur1;
}
while(cur1&&cur2)
{
if(cur1->val<cur2->val)
{
if(head==NULL)
{
head=tail=cur1;
}
else
{
tail->next=cur1;
tail=tail->next;
}
cur1=cur1->next;
}
else
{
if(head==NULL)
{
head=tail=cur2;
}
else
{
tail->next=cur2;
tail=tail->next;
}
cur2=cur2->next;
}
}
if(cur1!=NULL)
{
tail->next=cur1;
}
if(cur2!=NULL)
{
tail->next=cur2;
}
return head;
}
复 杂 度 分 析
时 间 复 杂 度 :O (m+n),其 中 m 和 n 分 别 为 两 个 链 表 的长 度。
空 间 复 杂 度: O (1),仅 需 常 数 级 额 外 空 间。
方 法 二 - - - 带 有 哨 兵 位 的 头 节 点
思 路 分 析
可 以 引 入 哨 兵 位 的 头 结 点 优 化 合 并 两 个 有 序 链 表 的 过 程。哨 兵 位 是 链 表 中 常 用 的 技 巧,能 够 简 化 边 界 条 件 处 理,使 代 码 更 加 简 洁 和 健 壮。
解 题 步 骤
1、哨 兵 位 引 入
创 建 一 个 虚 拟 节 点(哨 兵 位)作 为 合 并 后 链 表 的 起 始 点,不 存 储 实 际 数 据。
使 用 tail 指 针 动 态 维 护 链 表 的 尾 部,初 始 时 指 向 哨 兵 位。
2、比 较 并 合 并 节 点
同 时 遍 历 两 个 链 表,比 较 当 前 节 点 的 值,将 较 小 值 的 节 点 连 接 到 tail 之 后。
每 次 连 接 后 更 新 tail 指 针,使 其 始 终 指 向 链 表 的 最 后 一 个 节 点。
3、处 理 剩 余 节 点
任 一 链 表 遍 历 完 后,将 另 一 链 表 的 剩 余 部 分 直 接 连 接 到 tail 之 后。
4、释 放 哨 兵 位
合 并 完 成 后,哨 兵 位 的 下 一 个 节 点 即 为 实 际 链 表 的 头 节 点。
释 放 哨 兵 位 占 用 的 内 存,避 免 内 存 泄 漏。
温 馨 提 示:读 者 们 ,先 自 己 写 代 码,这 是 提 升 编 程 能 力 的 好 机 会。若 未 达 要 求 ,别 气 馁 ,参 考 下 文 解 释 会 有 新 收 获。
下 面 展 示代 码 示 例
。
javascript
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2)
{
struct ListNode* cur1 = list1;
struct ListNode* cur2 = list2;
struct ListNode* guard = NULL;
struct ListNode* tail = NULL;
// 带哨兵位的头结点
guard = tail = (struct ListNode*)malloc(sizeof(struct ListNode));
tail->next = NULL;
while (cur1 && cur2)
{
if (cur1->val < cur2->val)
{
tail->next = cur1;
tail = tail->next;
cur1 = cur1->next;
}
else
{
tail->next = cur2;
tail = tail->next;
cur2 = cur2->next;
}
}
if (cur1)
{
tail->next = cur1;
} else
{
tail->next = cur2;
}
struct ListNode* head = guard->next;
free(guard);
return head;
}
复 杂 度 分 析
时 间 复 杂 度 :O (m+n),其 中 m 和 n 分 别 为 两 个 链 表 的长 度。
空 间 复 杂 度: O (1),仅 需 常 数 级 额 外 空 间。
第 二 题 - - - 链 表 分 割
描 述
现 有 一 链 表 的 头 指 针 ListNode* pHead,给 一 定 值 x,编 写 一 段 代 码 将 所 有 小 于 x 的 结 点 排 在 其 余 结 点 之 前,且 不 能 改 变 原 来 的 数 据 顺 序,返 回 重 新 排 列 后 的 链 表 的 头 指 针。
思 路 分 析
要 将 链 表 按 值 x 分 割,可 以 使 用 两 个 辅 助 链 表 分 别 存 储 小 于 x 的 节 点 和 大 于 等 于 x 的 节 点,然 后 将 这 两 个 链 表 连 接 起 来形 成 最 终 结 果。这 种 方 法 的 核 心 在 于 利 用 两 个 哨 兵 位 头 结 点 来 简 化 链 表 操 作,避 免 处 理 空 链 表 的 边 界 情 况。
解 题 步 骤
1、创 建 两 个 辅 助 链 表
一 个 链 表 用 于 存 储 所 有 小 于 x 的 节 点(称 为 " 较 小 值 链 表")。
另 一 个 链 表 用 于 存 储 所 有 大 于 等 于 x 的 节 点(称 为 " 较 大 值 链 表")。
2、遍 历 原 链 表
将 每 个 节 点 根 据 其 值 的 大 小 添 加 到 对 应 的 辅 助 链 表 尾 部。
3、连 接 两 个 辅 助 链 表
将 较 小 值 链 表 的 尾 部 连 接 到 较 大 值 链 表 的 头 部。
将 较 大 值 链 表 的 尾 部 置 为 NULL,确 保 链 表 正 确 结 束。
4、释 放 哨 兵 位 头 结 点
释 放 两 个 辅 助 链 表 的 哨 兵 位 头 结 点,避 免 内 存 泄 漏。
温 馨 提 示:读 者 们 ,先 自 己 写 代 码,这 是 提 升 编 程 能 力 的 好 机 会。若 未 达 要 求 ,别 气 馁 ,参 考 下 文 解 释 会 有 新 收 获。
下 面 展 示代 码 示 例
。
javascript
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) : val(x), next(NULL) {}
};*/
class Partition
{
public:
ListNode* partition(ListNode* pHead, int x)
{
struct ListNode *lesshead, *lesstail, *greaterhead, *greatertail;
lesshead = lesstail = (struct ListNode*)malloc(sizeof(struct ListNode));
greaterhead = greatertail = (struct ListNode*)malloc(sizeof(struct ListNode));
lesshead->next = lesstail->next = NULL;
struct ListNode* cur = pHead;
while (cur)
{
if(cur->val < x)
{
lesstail->next = cur;
lesstail=lesstail->next;
}
else
{
greatertail->next = cur;
greatertail=greatertail->next;
}
cur = cur->next;
}
lesstail->next=greaterhead->next;
pHead=lesshead->next;
greatertail->next=NULL;//阻止链表带环,形成循环链表
free(lesshead);
free(greaterhead);
return pHead;
}
};
代 码 分 析
1、初 始 化 辅 助 链 表
创 建 两 个 哨 兵 位 头 结 点 lesshead 和 greaterhead,分 别 作 为 较 小 值 链 表 和 较 大 值 链 表 的 头 部。
使 用 lesstail 和 greatertail 指 针 分 别 指 向 两 个 链 表 的 尾 部,初 始 时 都 指 向 各 自 的 哨 兵 位 头 结 点。
2、遍 历 原 链 表 并 分 区
使 用 指 针 cur 遍 历 原 链 表 的 每 个 节 点。
若 当 前 节 点 的 值 小 于 x,将 其 添 加 到 较 小 值 链 表 的 尾 部 ,并 更 新 lesstail 指 针。
若 当 前 节 点 的 值 大 于 等 于 x,将 其 添 加 到 较 大 值 链 表 的 尾 部,并 更 新 greatertail 指 针。
3、连 接 两 个 链 表
将 较 小 值 链 表 的 尾 部(lesstail->next)连 接 到 较 大 值 链 表 的 头 部(greaterhead->next)。
将 较 大 值 链 表 的 尾 部(greatertail->next)置 为 NULL,防 止 形 成 环。
4、获 取 结 果 并 释 放 内 存
最 终 链 表 的 头 结 点 为 较 小 值 链 表 哨 兵 位 的 下 一 个 节 点(lesshead->next)。
释 放 两 个 哨 兵 位 头 结 点 的 内 存。
复 杂 度 分 析
时 间 复 杂 度 :O (n),其 中 n 是 链 表 的 长 度。
空 间 复 杂 度:O (1),只 需 要 常 数 级 的 额 外 空 间。

总 结
至 此,关 于 链 表 的 哨 兵 位 的 头 结 点 探 索 暂 告 一 段 落,但 你 的 编 程 征 程 才 刚 刚 启 航。编 写 代 码 是 与 计 算 机 逻 辑 深 度 对 话,过 程 中 虽 会 在 结 构 设 计、算 法 实 现 的 困 境 里 挣 扎,但 这 些 磨 砺 加 深 了 对 代 码 逻 辑 和 数 据 组 织 的 理 解。愿 你 合 上 电 脑 后,灵 感 不 断,在 数 据 结 构 的 世 界 里 持 续 深 耕,书 写 属 于 自 己 的 编 程 传 奇,下 一 次 开 启,定 有 全 新 的 精 彩 等 待。小 编 期 待 重 逢,盼 下 次 阅 读 时 见 证 你 们 更 大 的 进 步,共 赴 代 码 之 约!