面试经典150题[066]:分隔链表(LeetCode 86)

分隔链表(LeetCode 86)

题目链接:分隔链表(LeetCode 86)

难度:中等

1. 题目描述

给你一个链表的头节点 head 和一个特定值 x ,请你对链表进行分隔,使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。

你应当 保留 两个分区中每个节点的初始相对位置。

示例:

复制代码
输入: head = [1,4,3,2,5,2], x = 3
输出: [1,2,2,4,3,5]

输入: head = [2,1], x = 2
输出: [1,2]

2. 问题分析

2.1 规律

  • 这是一个链表的划分问题,需要将节点根据其值与 x 的关系分成两部分。
  • 关键要求是保持每个分区内部节点的 原始相对顺序。这意味着我们不能简单地通过交换节点值来解决,而需要移动节点。

2.2 双链表(双指针)思路

最直观且高效的方法是创建两个新的链表,一个用来存放所有小于 x 的节点,另一个用来存放所有大于或等于 x 的节点。

  • 创建哑节点(Dummy Nodes)

    • less_head:作为 "小于 x" 链表的头节点。
    • greater_head:作为 "大于等于 x" 链表的头节点。
    • 使用哑节点可以简化边界条件的处理,避免对头节点的特殊判断。
  • 创建遍历指针

    • less_ptr:指向 "小于 x" 链表的尾部。
    • greater_ptr:指向 "大于等于 x" 链表的尾部。
  • 遍历原链表

    • 用一个指针 current 遍历原链表 head
    • 对于每个节点 current
      • 如果 current.val < x,则将其链接到 less_ptr 的后面,并移动 less_ptr
      • 如果 current.val >= x,则将其链接到 greater_ptr 的后面,并移动 greater_ptr
  • 拼接链表

    • 遍历结束后,我们得到了两个独立的链表。
    • 将 "小于" 链表的尾部 (less_ptr) 指向 "大于等于" 链表的头部 (greater_head.next)。
    • 重要 :将 "大于等于" 链表的尾部 (greater_ptr) 的 next 指针设为 None,以防止产生环。
    • 返回 less_head.next作为新链表的头节点。

3. 代码实现

Python

python 复制代码
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next

class Solution:
    def partition(self, head: ListNode, x: int) -> ListNode:
        # 创建两个哑节点,分别作为两个新链表的头
        less_head = ListNode(-1)
        greater_head = ListNode(-1)
        
        # 创建两个指针,用于构建新链表
        less_ptr = less_head
        greater_ptr = greater_head
        
        current = head
        while current:
            if current.val < x:
                # 将节点加入到 'less' 链表
                less_ptr.next = current
                less_ptr = less_ptr.next
            else:
                # 将节点加入到 'greater' 链表
                greater_ptr.next = current
                greater_ptr = greater_ptr.next
            
            # 移动到下一个节点
            current = current.next
        
        # 将 'greater' 链表的末尾指向 None,断开与原链表的连接
        greater_ptr.next = None
        
        # 将 'less' 链表的末尾连接到 'greater' 链表的头部
        less_ptr.next = greater_head.next
        
        return less_head.next

C++

cpp 复制代码
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* partition(ListNode* head, int x) {
        // 创建哑节点
        ListNode* less_head = new ListNode(-1);
        ListNode* greater_head = new ListNode(-1);
        
        // 创建构建指针
        ListNode* less_ptr = less_head;
        ListNode* greater_ptr = greater_head;
        
        ListNode* current = head;
        while (current != nullptr) {
            if (current->val < x) {
                less_ptr->next = current;
                less_ptr = less_ptr->next;
            } else {
                greater_ptr->next = current;
                greater_ptr = greater_ptr->next;
            }
            current = current->next;
        }
        
        // 断开 'greater' 链表的尾部
        greater_ptr->next = nullptr;
        
        // 拼接两个链表
        less_ptr->next = greater_head->next;
        
        // 释放哑节点内存(可选,但好习惯)
        ListNode* new_head = less_head->next;
        delete less_head;
        delete greater_head;
        
        return new_head;
    }
};

4. 复杂度分析

  • 时间复杂度:O(n),其中 n 是链表的节点数。我们只需要遍历一次原链表。
  • 空间复杂度 :O(1),我们只使用了常数个额外指针(less_head, greater_head, less_ptr, greater_ptr),并没有创建新的节点,只是重新组织了原链表的节点。

5. 总结

  • 链表划分 问题,特别是要求保持相对顺序时,双链表(或双指针+哑节点) 方法是一个非常经典且有效的解决方案。
  • 使用哑节点可以极大地简化代码逻辑,避免对头节点的空指针判断和特殊处理。
  • 最后一定要记得处理好两个链表的拼接以及新链表尾部的 next 指针(设置为 None/nullptr)。

复习

面试经典150题[006]:旋转数组(LeetCode 189)

面试经典150题[036]:旋转图像(LeetCode 48)

面试经典150题[051]:用最少数量的箭引爆气球(LeetCode 452)

相关推荐
小橙编码日志几秒前
MongoDB深入与实战:基于SQL的对照解析
后端·面试
练习时长一年2 小时前
LeetCode热题100(搜索插入位置)
数据结构·算法·leetcode
(●—●)橘子……3 小时前
记力扣557.反转字符串中的单词 练习理解
算法·leetcode·职场和发展
PineappleCoder3 小时前
WebP/AVIF 有多香?比 JPEG 小 30%,<picture>标签完美解决兼容性
前端·面试·性能优化
sprintzer4 小时前
12.06-12.15力扣分治法刷题
算法·leetcode
月明长歌4 小时前
【码道初阶】【牛客BM30】二叉搜索树与双向链表:java中以引用代指针操作的艺术与陷阱
java·数据结构·算法·leetcode·二叉树·笔试·字节跳动
刃神太酷啦4 小时前
Linux 进程核心原理精讲:从体系结构到实战操作(含 fork / 状态 / 优先级)----《Hello Linux!》(6)
java·linux·运维·c语言·c++·算法·leetcode
玩具猴_wjh4 小时前
面试问题相关回答
面试·职场和发展
小李小李快乐不已4 小时前
数组&&矩阵理论基础
数据结构·c++·线性代数·算法·leetcode·矩阵
SiYuanFeng4 小时前
新手leetcode快速刷题指南
算法·leetcode·职场和发展