86. Partition List(分隔链表)两种解法(C++ & 注释)

1. 题目描述

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

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

示例:

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

题目链接:中文题目英文题目

2. 迭代(Iteration)

2.1 解题思路

迭代的思路非常简单:将小于x的节点串联在一个链表之中(less),把大于等于x的节点串联在另一个链表之中(greater),最后合并两个链表即可。比如题目这个例子:

head = 1->4->3->2->5->2, x = 3

less: lessHead -> 1 -> 2 -> 2;
greater: geaterHead -> 4 -> 3 -> 5

之后合并两个链表:lessHead -> 1 -> 2 -> 2 -> 4 -> 3 -> 5,这里我们也使用了假头指针的技巧,避免判断头指针的情况。只是需要注意5节点的next需指向空,否则链表可能会形成循环链表;此部分对应代码:2.2.1 不使用额外空间(推荐)

当然也可以两个queue来分别存储less和greater的节点,之后再合并链表,思路和上面是一模一样的。此部分对应代码:2.2.2 使用额外空间

2.2 实例代码

2.2.1 不使用额外空间(推荐)

class Solution {
public:
    ListNode* partition(ListNode* head, int x) {
        ListNode less, greater;
        ListNode* lessHead = &less, * greaterHead = &greater;

        while (head) {
            // 添加到before和after写法一
            /*if (head->val < x) { lessHead->next = head; lessHead = lessHead->next; }
            else { greaterHead->next = head; greaterHead = greaterHead->next; }*/

            // 添加到before和after写法二
            ListNode*& temp = head->val < x ? lessHead : greaterHead;
            temp->next = head;
            temp = temp->next;

            head = head->next;
        }

        lessHead->next = greater.next; // 合并两个链表
        greaterHead->next = nullptr;
        return less.next;
    }
};

2.2.2 使用额外空间

class Solution {
public:
    ListNode* partition(ListNode* head, int x) {
        queue<ListNode*> less;
        queue<ListNode*> greater;

        while (head) {
            if (head->val < x) less.push(head);
            else greater.push(head);
            head = head->next;
        }

		// less和greater进行合并
        while (greater.size()) { less.push(greater.front()); greater.pop(); }
        
        // 生成分隔后的链表
        if (less.size()) { head = less.front(); less.pop(); }
        ListNode* cur = head;
        while (less.size()) { cur->next = less.front(); less.pop(); cur = cur->next; }
        if (cur) cur->next = nullptr; // corner case: [], 0

        return head;
    }
};

3. 递归(Recursion)

3.1 解题思路

递归的方法稍微要难一点,不如迭代那么清晰。递归的关键是从后往前找到:head->val > head->next->val && head->next->val < x,这里表示head是大于等于x的,而head->next是小于x的,比如:5 -> 2, x = 3。所以存在这样一个节点,我们需要向后检查,把所有的小于x的值移动到前面,大于等于x的值移动到后面。递归过程如下图所示:
在这里插入图片描述

3.2 实例代码

class Solution {
    void dfs(ListNode* head, int x) {
        if (!head || !head->next) return;

        dfs(head->next, x); // 找到最后一个节点
        if (head->val >= x) {
            // 将小于x的值向前移动,大于等于x的值向后移动
            while (head->next) {
                if (head->val > head->next->val && head->next->val < x) {
                    int temp = head->val; head->val = head->next->val; head->next->val = temp; // 交换两个节点的值
                    head = head->next;
                }
                else break;
            }
        }
    }

public:
    ListNode* partition(ListNode* head, int x) {
        dfs(head, x);
        return head;
    }
};

4. 参考资料

  1. 86. Partition List

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值