一日一练: 分隔链表

105 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第11天,点击查看活动详情

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

原地操作

第一种思路是: 找到值小于x的节点,移到链表前部分,具体步骤:

  1. 首先判断链表是不是以小于x的节点开头的
    • 如果不是,以哨兵节点pre节点,
    • 如果是的话,找到第一个大于x节点的前一个节点pre节点
  2. 然后从pre节点开始,依次判断下一个节点的valx的关系
    • 如果大于等于x,移动指针到下一个位置

    • 如果小于x,移动节点到pre的下一个节点

以leetcode示例为例,主要思路可以用下图表示:

image.png

function partition(head: ListNode | null, x: number): ListNode | null {     
    const dummy = new ListNode(-1, head)
    let cur = head
    let pre = dummy
    // 找到值大于等于x节点的前一个节点
    while(cur) {
        if (cur.val < x) {
            pre = cur
            cur = cur.next
        } else {
            break
        }        
    }
    // 延用上面的cur
    // 遇到比x小的值,就以移动到pre节点后面
    while(cur && cur.next) {
        if (cur.next.val < x) {
            // 移动节点,具体对应的逻辑,可以参照上图理解
            let next = cur.next
            cur.next = cur.next.next
            next.next = pre.next
            pre.next = next
            pre = next
        } else {
             cur = cur.next
        }       
    }
    return dummy.next
};
  • 时间复杂度: O(n)
  • 空间复杂度: O(1)

两条链表

还有一种思路也很自然,准备两个链表,一个来存储小于x的值,另一个存储大于等于x的值,然后用前者拼接后者后者

function partition(head: ListNode | null, x: number): ListNode | null {     
    let lessList = new ListNode(-1)
    let greaterList = new ListNode(-1)
    // 保存头节点用来拼接
    const lessHeaderList = lessList
    const greaterHeader = greaterList
    let cur = head
    while(cur) {
        // 收集:小值 连接到小链表中;大值 连接到大链表中
        // 
        if (cur.val < x) {
            lessList = lessList.next = cur
        } else {
            greaterList = greaterList.next = cur
        }        
        cur = cur.next
    }
    // 将大的部分的尾结点 置null 防止与之前的链表形成cycle
    greaterList.next = null
    // 小值链表 拼接 大值链表
    lessList.next = greaterHeader.next
    return lessHeaderList.next
};
  • 时间复杂度: O(n)
  • 空间复杂度: O(1)