当前位置: 首页 > news >正文

LeetCode 算 法 实 战 - - - 移 除 链 表 元 素、反 转 链 表

LeetCode 算 法 实 战 - - - 移 除 链 表 元 素、反 转 链 表

  • 第 一 题 - - - 移 除 链 表 元 素
    • 方 法 一 - - - 原 地 删 除
    • 方 法 二 - - - 双 指 针
    • 方 法 三 - - - 尾 插
  • 第 二 题 - - - 反 转 链 表
    • 方 法 一 - - - 迭 代
    • 方 法 二 - - - 采 用 头 插 创 建 新 链 表
  • 总 结

💻作 者 简 介:曾 与 你 一 样 迷 茫,现 以 经 验 助 你 入 门 数据 结 构。
💡个 人 主 页:@笑口常开xpr 的 个 人 主 页
📚系 列 专 栏:硬 核 数 据 结 构 与 算 法
✨代 码 趣 语:恰 当 的 数 据 视 图 实 际 上 就 决 定 了 程 序 的 结 构。
💪代 码 千 行,始 于 坚 持,每 日 敲 码,进 阶 编 程 之 路。
📦gitee 链 接:gitee

在这里插入图片描述

         在 数 据 结 构 的 世 界 里,每 一 种 设 计 都 可 能 孕 育 出 惊 人 的 效 率 变 革。你 是 否 深 思 过,一 组 精 心 组 织 的 数 据 究 竟 能 创 造 怎 样 的 奇 迹?每 一 次 挖 掘 底 层 原 理,都 是 与 计 算 机 智 慧 的 巅 峰 对 话;每 一 次 剖 析 存 储 模 式,都 在 破 解 数 据 世 界 的 终 极 密 码。准 备 好 迎 接 这 场 盛 宴 了 吗?让 我 们 一 同 探 寻 双 指 针 的 无 尽 奥 秘,见 证 它 如 何 重 塑 数 字 时 代 的 运 行 法 则!


第 一 题 - - - 移 除 链 表 元 素

移 除 链 表 元 素


描 述:给 你 一 个 链 表 的 头 节 点 head 和 一 个 整 数 val,请 你 删 除 链 表 中 所 有 满 足 Node.val == val 的 节 点,并 返 回 新 的 头 节 点。

示 例 1:
输 入:head = [1,2,6,3,4,5,6], val = 6
输 出:[1,2,3,4,5]

示 例 2:
输 入:head = [], val = 1
输 出:[ ]

示 例 3:
输 入:head = [7,7,7,7], val = 7
输 出:[ ]

提 示:
列 表 中 的 节 点 数 目 在 范 围 [0, 104] 内
1 <= Node.val <= 50
0 <= val <= 50


方 法 一 - - - 原 地 删 除


思 路 分 析
         要 删 除 链 表 中 值 为 val 的 节 点,需 要 考 虑 两 种 情 况:一 是 头 节 点 的 值 恰 好 为 val;二 是 中 间 或 尾 部 节 点 的 值 为 val。对 于 头 节 点,直 接 删 除 即 可,不 过 要 注 意 更 新 头 节 点;对 于 中 间 或 尾 部 节 点,需 要 借 助 前 驱 节 点 来 删 除 当 前 节 点。


解 题 步 骤
删 除 头 部 的 目 标 节 点
(1)利 用 循 环 判 断 头 节 点 是 否 存 在,并 且 其 值 是 否 等 于 val。
(2)若 满 足 条 件,就 临 时 保 存 当 前 头 节 点,将 头 节 点 更 新 为 下 一 个 节 点, 然 后 释 放 临 时 保 存 的 头 节 点。

删 除 中 间 和 尾 部 的 目 标 节 点
(1)初 始 化 temp 指 针,让 它 指 向 新 的 头 节 点(可 能 是 原 头 节 点,也 可 能 是 更 新 后 的 头 节 点)。
(2)遍 历 链 表,确 保 当 前 节 点 temp 和 它 的 下 一 个 节 点 都 不 为 空。
(3)若 下 一 个 节 点 的 值 等 于 val,则 临 时 保 存 该 节 点,将 当 前 节 点 的 next 指 针 指 向 下 下 个 节 点,最 后 释 放 临 时 保 存 的 节 点。
(4)若 下 一 个 节 点 的 值 不 等 于 val,则 将 temp 指 针 后 移 一 位。

返 回 新 的 头 节 点
         遍 历 结 束 后,返 回 更 新 后 的 头 节 点 head。


温 馨 提 示:读 者 们 ,先 自 己 写 代 码,这 是 提 升 编 程 能 力 的 好 机 会。若 未 达 要 求 ,别 气 馁 ,参 考 下 文 解 释 会 有 新 收 获。

下 面 展 示代 码 示 例

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     struct ListNode *next;* };*/
struct ListNode* removeElements(struct ListNode* head, int val) 
{while (head != NULL && head->val == val) {struct ListNode* tmp = head;head = head->next;free(tmp);}struct ListNode* temp = head;while (temp != NULL && temp->next != NULL){if (temp->next->val == val){struct ListNode* cur = temp->next;temp->next = cur->next;free(cur);} else {temp = temp->next;}}return head;
}

复 杂 度 分 析
时 间 复 杂 度:O (n),其 中 n 为 链 表 长 度。需 遍 历 每 个 节 点 一 次。
空 间 复 杂 度:O (1),仅 需 常 数 级 额 外 空 间 存 储 指 针 变 量。

以 链 表 元 素 为 1,2,6,3,4,5,6 val 为 6 进 行 动 画 演 示。

在这里插入图片描述


方 法 二 - - - 双 指 针

思 路 分 析
         从 单 链 表 中 删 除 所 有 值 等 于 给 定 值 val 的 节 点。主 要 思 路 是 通 过 双 指 针 prev 和 cur 遍 历 链 表,当 遇 到 目 标 节 点 时,调 整 指 针 跳 过 该 节 点 并 释 放 内 存。

温 馨 提 示:读 者 们 ,先 自 己 写 代 码,这 是 提 升 编 程 能 力 的 好 机 会。若 未 达 要 求 ,别 气 馁 ,参 考 下 文 解 释 会 有 新 收 获。

下 面 展 示代 码 示 例

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     struct ListNode *next;* };*/
struct ListNode* removeElements(struct ListNode* head, int val) 
{struct ListNode* prev = NULL;struct ListNode* cur = head;while (cur != NULL) {if (cur->val != val) {prev = cur;cur = cur->next;} else {if (cur == head && cur->val == val) {head = cur->next;free(cur);cur = head;} else {prev->next = cur->next;free(cur);cur = prev->next;}}}return head;
}

代 码 分 析
1、初 始 化 双 指 针
         prev:初 始 化 为 NULL,用 于 记 录 当 前 节 点 的 前 一 个 节 点。
         cur:初 始 化 为 头 节 点 head,用 于 遍 历 链 表。
2、遍 历 链 表
         使 用 while (cur != NULL) 循 环 遍 历 链 表,直 到 所 有 节 点 处 理 完 毕。
3、处 理 当 前 节 点
(1)若 当 前 节 点 值 不 等 于 val:
         将 prev 更 新 为 cur。
         cur 后 移 一 位(cur = cur->next)。
(2)若 当 前 节 点 值 等 于 val:
         若 当 前 节 点 是 头 节 点 cur == head:
              将 头 指 针 head 后 移 一 位 head = cur->next。
              释 放 当 前 节 点 内 存 free(cur)。
              更 新 cur 为 新 的 头 节 点 cur = head。
         若 当 前 节 点 不 是 头 节 点:
              通 过 prev->next = cur->next 跳 过 当 前 节 点。
              释 放 当 前 节 点 内 存 free(cur)。
              更 新 cur 为 prev->next。
4、返 回 头 指 针:
         遍 历 结 束 后,所 有 值 为 val 的 节 点 均 被 删 除,返 回 新 的 头 指 针 head。


复 杂 度 分 析
时 间 复 杂 度:O (n),其 中 n 为 链 表 长 度。需 遍 历 每 个 节 点 一 次。
空 间 复 杂 度:O (1),仅 需 常 数 级 额 外 空 间 存 储 指 针 变 量。


方 法 三 - - - 尾 插

思 路 分 析
         通 过 遍 历 原 链 表,将 不 等 于 val 的 节 点 依 次 尾 插 到 新 链 表 中,最 后 返 回 新 链 表 的 头 指 针。


温 馨 提 示:读 者 们 ,先 自 己 写 代 码,这 是 提 升 编 程 能 力 的 好 机 会。若 未 达 要 求 ,别 气 馁 ,参 考 下 文 解 释 会 有 新 收 获。

下 面 展 示代 码 示 例

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     struct ListNode *next;* };*/
struct ListNode* removeElements(struct ListNode* head, int val) 
{struct ListNode* cur = head;struct ListNode* newhead = NULL;struct ListNode* tail = NULL;while (cur) {if (cur->val != val) {// 尾插if (tail == NULL) {newhead = tail = cur;} else {tail->next = cur;tail = tail->next;}cur = cur->next;} else{struct ListNode* next = cur->next;free(cur);cur = next;}}if(tail){tail->next=NULL;}return newhead;
}

代 码 分 析
1、初 始 化 变 量:
         cur:用 于 遍 历 原 链 表 的 指 针,初 始 化 为 原 链 表 头 head。
         newhead:新 链 表 的 头 指 针,初 始 化 为 NULL。
         tail:新 链 表 的 尾 指 针,初 始 化 为 NULL,用 于 高 效 尾 插 节 点。
2、遍 历 原 链 表:
         使 用 while (cur) 循 环 遍 历 原 链 表,直 到 所 有 节 点 处 理 完 毕。
3、处 理 当 前 节 点:
(1)若 当 前 节 点 值 不 等 于 val:
         若 新 链 表 为 空(tail == NULL):
              将 newhead 和 tail 都 指 向 当 前 节 点 cur。
         若 新 链 表 非 空:
              将 当 前 节 点 cur 连 接 到 新 链 表 尾 部(tail->next = cur)。
              更 新 tail 为 当 前 节 点(tail = tail->next)。
              移 动 cur 到 下 一 个 节 点。
(2)若 当 前 节 点 值 等 于 val:
         保 存 当 前 节 点 的 下 一 个 节 点 指 针 next。
         释 放 当 前 节 点 内 存(free(cur)),防 止 内 存 泄 漏。
         将 cur 更 新 为 next,继 续 处 理 后 续 节 点。
4、处 理 尾 节 点 的 next 指 针:
         遍 历 结 束 后,确 保 新 链 表 的 尾 节 点 tail 的 next 指 针 为 NULL,避 免 形 成 环。
5、返 回 新 链 表 头 指 针:
         返 回 newhead,即 新 链 表 的 头 节 点。若 原 链 表 所 有 节 点 均 被 删 除,newhead 为 NULL。


复 杂 度 分 析
时 间 复 杂 度:O (n),其 中 n 为 链 表 长 度。需 遍 历 每 个 节 点 一 次。
空 间 复 杂 度:O (1),仅 需 常 数 级 额 外 空 间 存 储 指 针 变 量。


第 二 题 - - - 反 转 链 表

反 转 链 表


描 述
给 你 单 链 表 的 头 节 点 head ,请 你 反 转 链 表,并 返 回 反 转 后 的 链 表。

示 例 1
输 入:head = [1,2,3,4,5]
输 出:[5,4,3,2,1]
示 例 2
输 入:head = [1,2]
输 出:[2,1]
示 例 3:
输 入:head = []
输 出:[ ]

提 示:
链 表 中 节 点 的 数 目 范 围 是 [0, 5000]
-5000 <= Node.val <= 5000


方 法 一 - - - 迭 代

思 路 分 析
         使 用 三 个 指 针 协 同 工 作,逐 步 将 每 个 节 点 的 next 指 针 指 向 前 驱 节 点,最 终 实 现 链 表 反 转。


温 馨 提 示:读 者 们 ,先 自 己 写 代 码,这 是 提 升 编 程 能 力 的 好 机 会。若 未 达 要 求 ,别 气 馁 ,参 考 下 文 解 释 会 有 新 收 获。

下 面 展 示代 码 示 例

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     struct ListNode *next;* };*/
struct ListNode* reverseList(struct ListNode* head) 
{struct ListNode* n1 = NULL;struct ListNode* n2 = head;struct ListNode* tmp = head;while(tmp!=NULL){tmp = n2->next;n2->next=n1;n1=n2;n2=tmp;}return n1;
}

代 码 分 析
1、初 始 化 三 个 指 针:
(1)n1:初 始 化 为 NULL,用 于 指 向 当 前 处 理 节 点 的 前 驱 节 点。
(2)n2:初 始 化 为 head,指 向 当 前 正 在 处 理 的 节 点。
(3)tmp:初 始 化 为 head,作 为 临 时 指 针,用 于 保 存 后 续 节 点。
2、遍 历 链 表:
         使 用 while (tmp != NULL) 循 环,确 保 遍 历 完 整 个 链 表。
3、反 转 操 作:
(1)保 存 后 继 节 点:tmp = n2->next,保 存 当 前 节 点 的 下 一 个 节 点。
(2)调 整 指 针 方 向:n2->next = n1,将 当 前 节 点 的 next 指 向 前 驱 节 点。
(3)指 针 后 移:
         n1 移 动 到 当 前 节 点(n1 = n2)。
         n2 移 动 到 之 前 保 存 的 后 继 节 点(n2 = tmp)。
4、返 回 新 头 节 点:
         循 环 结 束 后,n1 指 向 原 链 表 的 最 后 一 个 节 点,即 反 转 后 的 新 头 节 点,返 回 n1。


复 杂 度 分 析
时 间 复 杂 度:O (n),需 遍 历 每 个 节 点 一 次。
空 间 复 杂 度:O (1),仅 需 常 数 级 额 外 空 间 存 储 指 针 变 量。


方 法 二 - - - 采 用 头 插 创 建 新 链 表

思 路 分 析
         遍 历 原 链 表,为 每 个 节 点 创 建 副 本,创 建 新 的 链 表,然 后 将 副 本 节 点 依 次 插 入 到 新 链 表 的 头 部,最 终 形 成 原 链 表 的 逆 序。


温 馨 提 示:读 者 们 ,先 自 己 写 代 码,这 是 提 升 编 程 能 力 的 好 机 会。若 未 达 要 求 ,别 气 馁 ,参 考 下 文 解 释 会 有 新 收 获。

下 面 展 示代 码 示 例

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     struct ListNode *next;* };*/
struct ListNode* reverseList(struct ListNode* head) 
{struct ListNode* cur = head;struct ListNode* newHead = NULL; //新链表的头指针while (cur) {struct ListNode* newNode = (struct ListNode*)malloc(sizeof(struct ListNode));if (newNode == NULL) {perror("malloc failed");// 释放已分配的节点while (newHead) {struct ListNode* temp = newHead;newHead = newHead->next;free(temp);}return NULL;}newNode->val = cur->val;newNode->next = newHead; //头插法:新节点指向当前头newHead = newNode;       //更新头指针cur = cur->next;}return newHead; //返回新链表的头结点
}

代 码 分 析
1、初 始 化 变 量:
         cur:指 向 原 链 表 当 前 节 点,初 始 化 为 head。
         newHead:新 链 表 的 头 指 针,初 始 化 为 NULL。
2、遍 历 原 链 表:
         使 用 while (cur) 循 环,直 到 原 链 表 遍 历 完 毕。
3、创 建 新 节 点 并 头 插:
(1)分 配 内 存:为 当 前 节 点 创 建 副 本 newNode。
(2)赋 值:将 原 节 点 的 值 赋 给 新 节 点。
(3)头 插 操 作:
         将 新 节 点 的 next 指 向 当 前 新 链 表 的 头 结 点 newHead。
         更 新 newHead 为 新 节 点,使 其 成 为 新 链 表 的 头 结 点。
4、错 误 处 理:
若 内 存 分 配 失 败(newNode == NULL):
         释 放 已 创 建 的 新 链 表 节 点(避 免 内 存 泄 漏)。
         返 回 NULL 表 示 操 作 失 败。
5、返 回 结 果:
         遍 历 结 束 后,newHead 指 向 新 链 表 的 头 节 点,返 回 newHead。


关 键 技 巧 与 注 意 事 项
头 插 法 的 逆 序 特 性:
         每 次 将 新 节 点 插 入 到 链 表 头 部,导 致 新 链 表 节 点 顺 序 与 插 入 顺 序 相 反。
内 存 管 理:
         每 个 新 节 点 都 通 过 malloc 分 配 内 存,需 确 保 使 用 完 毕 后 调 用 free 释 放。
         当 内 存 分 配 失 败 时,需 释 放 已 分 配 的 所 有 新 节 点,避 免 内 存 泄 漏。
原 链 表 不 变:
         该 方 法 不 修 改 原 链 表 结 构,适 合 需 要 保 留 原 链 表 的 场 景。


复 杂 度 分 析
时 间 复 杂 度:O (n),遍 历 原 链 表 一 次。
空 间 复 杂 度:O (n),需 要 创 建 新 链 表。

在这里插入图片描述

总 结

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

相关文章:

  • 双目相机深度的误差分析(基线长度和相机焦距的选择)
  • Linux系统编程之共享内存
  • 【设计模式-4.5】行为型——迭代器模式
  • KWIC—Implicit Invocation
  • 【代码坏味道】变更阻碍者Change Preventers
  • Tomcat的整体架构及其设计精髓
  • MAC软件游戏打开提示已损坏
  • 通义灵码深度实战测评:从零构建智能家居控制中枢,体验AI编程新范式
  • azure web app创建分步指南系列之二
  • CSS专题之水平垂直居中
  • Redis最佳实践——安全与稳定性保障之高可用架构详解
  • Ubuntu22.04通过命令行安装qt5
  • ubuntu20.04.5-arm64版安装robotjs
  • 在Ubuntu20.04上安装ROS Noetic
  • ubuntu20.04.5--arm64版上使用node集成java
  • Ubuntu搭建DNS服务器
  • 【Ubuntu】摸鱼技巧之虚拟机环境复制
  • 配置远程无密登陆ubuntu服务器时无法连接问题排查
  • 第六十三节:深度学习-模型推理与后处理
  • 使用Java实现简单的计算机案例
  • 公司网站内容如何做/小说推文推广平台
  • 阿里云 多域名解析 到不同的网站/软文广告素材
  • 怎样看一个网站的浏览量/企业网络营销成功案例
  • 做网站多久学会/爱站网收录
  • 网站后台系统设置/百度知道一下首页
  • 淘宝客网站可以做分销吗/杭州疫情最新情况