LeetCode 题目 86:分隔链表

作者介绍:10年大厂数据\经营分析经验,现任大厂数据部门负责人。
会一些的技术:数据分析、算法、SQL、大数据相关、python
欢迎加入社区:码上找工作
作者专栏每日更新:
LeetCode解锁1000题: 打怪升级之旅
python数据分析可视化:企业实战案例
python源码解读
程序员必备的数学知识与应用
备注说明:方便大家阅读,统一使用python,带必要注释,公众号 数据分析螺丝钉 一起打怪升级

题目描述

给你一个链表和一个特定值 x,对链表进行分隔,使得所有小于 x 的节点都在大于或等于 x 的节点之前。你应当保留两个分区中每个节点的初始相对位置。

输入格式
  • head:链表的头节点。
  • x:分隔值。
输出格式
  • 返回重新排列后的链表的头节点。

示例

示例 1
输入: head = 1->4->3->2->5->2, x = 3
输出: 1->2->2->4->3->5

方法一:使用两个指针

解题步骤
  1. 创建两个链表:一个用于存放小于 x 的节点,另一个用于存放大于等于 x 的节点。
  2. 合并链表:遍历原链表,根据节点值将节点分配到两个链表中,然后将这两个链表合并。
完整的规范代码
class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next

def partition(head, x):
    """
    使用两个指针分隔链表
    :param head: ListNode, 链表的头节点
    :param x: int, 分隔值
    :return: ListNode, 分隔后的链表头节点
    """
    less_head = ListNode(0)  # 小于x的链表虚拟头节点
    greater_head = ListNode(0)  # 大于等于x的链表虚拟头节点
    less = less_head
    greater = greater_head

    while head:
        if head.val < x:
            less.next = head
            less = less.next
        else:
            greater.next = head
            greater = greater.next
        head = head.next

    # 合并两个链表
    greater.next = None  # 防止构成环
    less.next = greater_head.next
    return less_head.next

# 示例调用
# 假设创建链表函数和打印函数已定义
算法分析
  • 时间复杂度:(O(n)),其中 (n) 是链表的长度,需要遍历一次链表。
  • 空间复杂度:(O(1)),使用了常数个额外指针。

方法二:原地重排

解题步骤
  1. 使用四个指针less_start, less_end, greater_start, greater_end 分别标记两个部分的开始和结束位置。
  2. 节点交换:遍历链表,根据节点的值决定其应该放置的位置,并相应地移动指针。
完整的规范代码
def partition(head, x):
    """
    原地重排链表,保留节点的相对位置
    :param head: ListNode, 链表的头节点
    :param x: int, 分隔值
    :return: ListNode, 分隔后的链表头节点
    """
    if not head or not head.next:
        return head

    dummy = ListNode(0)
    dummy.next = head
    prev = dummy
    curr = head

    # 寻找第一个大于等于x的节点
    while curr and curr.val < x:
        prev = curr
        curr = curr.next

    # 如果已经是排好序或没有需要移动的节点
    if not curr or not curr.next:
        return dummy.next

    # 遍历剩余节点
    node_to_move = curr.next
    node_to_move_prev = curr
    while node_to_move:
        if node_to_move.val < x:
            # 移动节点到前半部分
            node_to_move_prev.next = node_to_move.next
            node_to_move.next = curr
            prev.next = node_to_move

            # 更新指针
            prev = node_to_move
            node_to_move = node_to_move_prev.next
        else:
            node_to_move_prev = node_to_move
            node_to_move = node_to_move.next

    return dummy.next

# 示例调用
# 假设创建链表函数和打印函数已定义
算法分析
  • 时间复杂度:(O(n)),必须遍历整个链表。
  • 空间复杂度:(O(1)),原地修改,没有使用额外空间。

方法三:收集节点后重建

解题步骤
  1. 收集节点:遍历链表,将小于 x 的节点放入一个列表,大于等于 x 的节点放入另一个列表。
  2. 重建链表:首先链接小于 x 的节点,然后链接大于等于 x 的节点,最后返回新链表的头节点。
完整的规范代码
def partition(head, x):
    """
    收集节点后重建链表
    :param head: ListNode, 链表的头节点
    :param x: int, 分隔值
    :return: ListNode, 分隔后的链表头节点
    """
    less_nodes = []
    greater_nodes = []
    node = head
    while node:
        if node.val < x:
            less_nodes.append(node)
        else:
            greater_nodes.append(node)
        node = node.next

    # 重建链表
    dummy = ListNode(0)
    curr = dummy
    for node in less_nodes + greater_nodes:
        curr.next = node
        curr = curr.next
    curr.next = None

    return dummy.next

# 示例调用
# 假设创建链表函数和打印函数已定义
算法分析
  • 时间复杂度:(O(n)),遍历链表一次,并重新构建链表一次。
  • 空间复杂度:(O(n)),存储节点的列表可能与链表长度相同。

方法四:原地重排优化

解题步骤
  1. 直接在遍历中分离节点:利用两个虚拟头节点less_headgreater_head分别连接小于x和大于等于x的节点。
  2. 尾部处理:确保所有处理后的链表没有形成环。
完整的规范代码
def partition(head, x):
    """
    原地重排优化,避免额外空间使用
    :param head: ListNode, 链表的头节点
    :param x: int, 分隔值
    :return: ListNode, 分隔后的链表头节点
    """
    # 创建两个虚拟头节点
    less_head = ListNode(0)
    greater_head = ListNode(0)
    less = less_head
    greater = greater_head
    
    # 遍历原链表,根据x值分配节点到两个部分
    while head:
        if head.val < x:
            less.next = head
            less = less.next
        else:
            greater.next = head
            greater = greater.next
        head = head.next

    # 防止环的产生
    greater.next = None
    # 连接两部分
    less.next = greater_head.next
    
    return less_head.next

# 示例调用
# 假设创建链表函数和打印函数已定义
算法分析
  • 时间复杂度:(O(n)),遍历一次链表。
  • 空间复杂度:(O(1)),虽然使用了两个额外的头节点,但整体上仍为常数级额外空间。

方法五:递归法

解题步骤
  1. 递归遍历:递归处理链表节点,根据节点值与x的比较决定其归属。
  2. 合并:递归过程中自然合并符合条件的节点。
完整的规范代码
def partition(head, x):
    """
    递归方法重排链表
    :param head: ListNode, 链表的头节点
    :param x: int, 分隔值
    :return: ListNode, 分隔后的链表头节点
    """
    if not head or not head.next:
        return head

    # 将头节点后面的节点进行分隔
    next_partitioned = partition(head.next, x)
    
    if head.val < x:
        head.next = next_partitioned
        return head
    else:
        # 找到分隔后的链表中第一个小于x的节点前的节点
        last_less = None
        current = next_partitioned
        while current and current.val >= x:
            last_less = current
            current = current.next
        
        if last_less:
            head.next = last_less.next
            last_less.next = head
        else:
            head.next = next_partitioned
            return head
        
        return next_partitioned

# 示例调用
# 假设创建链表函数和打印函数已定义
算法分析
  • 时间复杂度:(O(n)),每个节点访问一次。
  • 空间复杂度:(O(n)),由于递归的深度可能达到链表的长度。

不同算法的优劣势对比

特征方法一:使用两个指针方法二:原地重排方法三:收集节点后重建方法四:原地重排优化方法五:递归法
时间复杂度(O(n))(O(n))(O(n))(O(n))(O(n))
空间复杂度(O(1))(O(1))(O(n))(O(1))(O(n))
优势简单明了,易于理解不需要额外空间,直观直接操作,易于实现极少额外空间,效率高自然合并,逻辑简单
劣势代码较长,需要处理多个节点连接较复杂,需要正确处理边界使用额外空间存储节点需要处理更多的边界条件可能导致栈溢出,效率低于迭代

应用示例

这些方法可以应用于数据流分离、系统优先级调度、社交网络中的数据分级处理等多个领域,特别是在需要根据特定规则组织数据时。

  • 23
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

数据分析螺丝钉

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值