面试经典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)

相关推荐
C++ 老炮儿的技术栈1 分钟前
什么是通信规约
开发语言·数据结构·c++·windows·算法·安全·链表
学历真的很重要19 分钟前
LangChain V1.0 Context Engineering(上下文工程)详细指南
人工智能·后端·学习·语言模型·面试·职场和发展·langchain
有一个好名字19 分钟前
力扣-从字符串中移除星号
java·算法·leetcode
萧瑟其中~23 分钟前
二分算法模版——基础二分查找,左边界查找与右边界查找(Leetcode的二分查找、在排序数组中查找元素的第一个位置和最后一个位置)
数据结构·算法·leetcode
AlenTech39 分钟前
208. 实现 Trie (前缀树) - 力扣(LeetCode)
leetcode
iAkuya40 分钟前
(leetcode)力扣100 36二叉树的中序遍历(迭代递归)
算法·leetcode·职场和发展
wangwangmoon_light1 小时前
1.1 LeetCode总结(线性表)_枚举技巧
算法·leetcode·哈希算法
有一个好名字1 小时前
力扣-小行星碰撞
算法·leetcode·职场和发展
栈与堆2 小时前
LeetCode-1-两数之和
java·数据结构·后端·python·算法·leetcode·rust