【数据结构和算法】删除链表的中间节点

其他系列文章导航

Java基础合集
数据结构与算法合集

设计模式合集

多线程合集

分布式合集

ES合集


文章目录

其他系列文章导航

文章目录

前言

一、题目描述

二、题解

三、代码

四、复杂度分析


前言

这是力扣的1657题,难度为中等,解题方案有很多种,本文讲解我认为最奇妙的一种。

慢慢开始链表的模块了,这道题是一道非常好的队列的例题,很有代表性。


一、题目描述

给你一个链表的头节点 head删除 链表的 中间节点 ,并返回修改后的链表的头节点 head

长度为 n 链表的中间节点是从头数起第 ⌊n / 2⌋ 个节点(下标从 0 开始),其中 ⌊x⌋ 表示小于或等于 x 的最大整数。

  • 对于 n = 12345 的情况,中间节点的下标分别是 01122

示例 1:

复制代码
输入:head = [1,3,4,7,1,2,6]
输出:[1,3,4,1,2,6]
解释:
上图表示给出的链表。节点的下标分别标注在每个节点的下方。
由于 n = 7 ,值为 7 的节点 3 是中间节点,用红色标注。
返回结果为移除节点后的新链表。 

示例 2:

复制代码
输入:head = [1,2,3,4]
输出:[1,2,4]
解释:
上图表示给出的链表。
对于 n = 4 ,值为 3 的节点 2 是中间节点,用红色标注。

示例 3:

复制代码
输入:head = [2,1]
输出:[2]
解释:
上图表示给出的链表。
对于 n = 2 ,值为 1 的节点 1 是中间节点,用红色标注。
值为 2 的节点 0 是移除节点 1 后剩下的唯一一个节点。

提示:

  • 链表中节点的数目在范围 [1, 105]
  • 1 <= Node.val <= 105

二、题解

2.1 方法一:快慢指针法

这个算法的目的是从链表中删除中间的节点,而保持链表的其余部分不变。给定链表的头结点 head,该方法返回删除中间节点后的链表。

思路与算法:

  1. 基本情况 : 如果链表只有一个节点或者没有节点,直接返回 null
  2. 双指针法 : 使用两个指针,一个快速指针 fast 和一个慢指针 slow。开始时,fastslow 都指向链表的头部。
  3. 移动指针 : 当 fast 指针移动到倒数第二个节点时(即当前节点是中间节点的前一个节点),停止移动 fast 指针。同时,移动 slow 指针,使其指向下一个节点。
  4. 删除节点 : 将 slow.next 指向 slow.next.next,从而删除中间节点。
  5. 返回结果 : 返回原始的头节点 head

2.2 链表算法的解题思路

链表算法的一般思路解法包括以下几个方面:

  1. 理解问题:首先,你需要理解问题的具体要求。例如,是否需要找到链表的长度,是否需要插入或删除节点,是否需要反转链表等。
  2. 选择合适的算法:根据问题的具体要求,选择合适的算法。例如,如果需要找到链表的长度,可以使用快慢指针法;如果需要插入或删除节点,可以使用双指针法;如果需要反转链表,可以使用迭代或递归方法。
  3. 定义节点和链表结构:在开始编写代码之前,你需要定义节点和链表的结构。在大多数编程语言中,你可以使用类或结构体来定义节点,使用指针或引用类型来定义链表。
  4. 实现算法:根据选择的算法,使用编程语言实现代码。在实现代码时,需要注意指针的操作,确保指针的正确指向。例如,在插入节点时,需要更新新节点和它后面节点的指针;在删除节点时,需要更新被删除节点前一个节点的指针,使其指向被删除节点的下一个节点。
  5. 测试和验证:运行代码,测试算法的正确性和效率。如果发现问题,需要对代码进行调试和修改。你可以使用一些测试用例来验证算法的正确性,例如测试空链表、只有一个节点的链表、有两个节点的链表等。
  6. 优化和改进:根据实际情况,可以对算法进行优化和改进,以提高算法的效率和适用范围。例如,可以使用哈希表来存储每个节点的值和对应的节点指针,以便快速查找节点;可以使用迭代方法来遍历链表,避免使用递归方法导致的栈溢出问题。

三、代码

3.1 方法一:快慢指针法

Java版本:

java 复制代码
class Solution {
    public ListNode deleteMiddle(ListNode head) {
        if (head.next == null) {
            return null;
        }
        ListNode fast = head;
        ListNode slow = new ListNode(-1, head);
        while (fast != null && fast.next != null) {
            fast = fast.next.next;
            slow = slow.next;
        }
        slow.next = slow.next.next;
        return head;
    }
}

Pyhton版本:

python 复制代码
class Solution:
    def deleteMiddle(self, head: ListNode) -> ListNode:
        if head.next is None:
            return None
        fast = head
        slow = ListNode(-1, head)
        while fast is not None and fast.next is not None:
            fast = fast.next.next
            slow = slow.next
        slow.next = slow.next.next
        return head

C++版本:

cpp 复制代码
class Solution {
public:
    ListNode* deleteMiddle(ListNode* head) {
        if (head->next == nullptr) {
            return nullptr;
        }
        ListNode* fast = head;
        ListNode* slow = new ListNode(-1, head);
        while (fast != nullptr && fast->next != nullptr) {
            fast = fast->next->next;
            slow = slow->next;
        }
        slow->next = slow->next->next;
        return head;
    }
};

Go版本:

Go 复制代码
type ListNode struct {
    Val  int
    Next *ListNode
}

func deleteMiddle(head *ListNode) *ListNode {
    if head.Next == nil {
        return nil
    }
    fast := head
    slow := &ListNode{Val: -1, Next: head}
    for fast != nil && fast.Next != nil {
        fast = fast.Next.Next
        slow = slow.Next
    }
    slow.Next = slow.Next.Next
    return head
}

四、复杂度分析

4.1 方法一:快慢指针法

  • 时间复杂度: O(n)。
  • 空间复杂度: O(1)。

相关推荐
吾日三省吾码11 分钟前
JVM 性能调优
java
LNTON羚通39 分钟前
摄像机视频分析软件下载LiteAIServer视频智能分析平台玩手机打电话检测算法技术的实现
算法·目标检测·音视频·监控·视频监控
弗拉唐1 小时前
springBoot,mp,ssm整合案例
java·spring boot·mybatis
oi772 小时前
使用itextpdf进行pdf模版填充中文文本时部分字不显示问题
java·服务器
少说多做3432 小时前
Android 不同情况下使用 runOnUiThread
android·java
知兀2 小时前
Java的方法、基本和引用数据类型
java·笔记·黑马程序员
哭泣的眼泪4082 小时前
解析粗糙度仪在工业制造及材料科学和建筑工程领域的重要性
python·算法·django·virtualenv·pygame
蓝黑20202 小时前
IntelliJ IDEA常用快捷键
java·ide·intellij-idea
Ysjt | 深2 小时前
C++多线程编程入门教程(优质版)
java·开发语言·jvm·c++