Leetcode 86. 分隔链表

284 阅读3分钟

原题链接: 86. 分隔链表 - 力扣(Leetcode)

tag: 双指针, 链表.

一. 题目

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

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

二. 题解

本题我们通过将原链表的节点 尾插 到分隔链表的左半部分或右半部分来分隔链表.

由于尾插第一个节点时分隔链表的左半部分或右半部分中没有节点, 即 ltail == nullptrrtail == nullptr, 无法通过 ltail->next = curr 或者 rtail->next = curr 来尾插节点, 需要分类讨论.

为了避免这种额外的讨论, 我们引入虚拟头节点(dummy)的概念.

设置虚拟头节点, 即在链表的头节点前新插入一个节点. 如此, 链表的头节点便有了前驱节点(即dummy), 尾插时就不需要进行额外的讨论.

分隔链表前.

image.png

设置两个虚拟头节点 lheadrhead.

ListNode* lhead = new ListNode(), * rhead = new ListNode();

image.png

定义两个尾指针 ltailrtail 分别指向虚拟头节点 lheadrhead.

ListNode* ltail = lhead, * rtail = rhead;

image.png

定义 curr 指针指向原链表头节点 head.

ListNode* curr = head;

image.png

curr 不为空时.

curr != nullptr

image.png

curr->val < x

image.png

curr 所指节点尾插到 ltail 所指节点后.

image.png

更新 ltail 指针.

ltail = ltail->next;

image.png

更新 curr 指针.

curr = curr->next;

image.png

重复这一步骤.

image.png

重复这一步骤.

image.png

重复这一步骤.

image.png

重复这一步骤.

image.png

重复这一步骤.

image.png

curr == nullptr, while 循环终止.

image.png

将分隔链表的左半部分的尾节点的 next 指针指向右半部分的头节点.

ltail->next = rhead->next;

image.png

将分隔链表的右半部分的尾节点置空.

rtail->next = nullptr;

image.png

head = lhead->next;

image.png

delete lhead, rhead;

image.png

分隔链表后.

image.png

三. 复杂度分析

时间复杂度: O(N), N 是原链表的长度, 我们对原链表进行了一次遍历.

空间复杂度: O(1).

四. 代码

/**
 * 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* lhead = new ListNode(), * rhead = new ListNode();    // 设置两个虚拟头节点 lhead 和 rhead
        ListNode* ltail = lhead, * rtail = rhead;    // 定义两个尾指针 ltail 和 rtail
        ListNode* curr = head;    // 定义 curr 指针指向原链表头节点
        
        while (curr) {
            if (curr->val < x) {    // 若小于基准值 x
                ltail->next = curr;    // 尾插到 ltail 所指节点后
                ltail = ltail->next;    // 更新 ltail 指针
            } else {    // 若大于等于基准值 x
                rtail->next = curr;    // 尾插到 rtail 所指节点后
                rtail = rtail->next;    // 更新 rtail 指针
            }
            curr = curr->next;    // 更新 curr 指针
        }

        ltail->next = rhead->next;    // 将分隔链表的左半部分的尾节点的 next 指针指向右半部分的头节点
        rtail->next = nullptr;    // 将分隔链表的尾节点置空

        head = lhead->next;    // 返回分隔后链表的头节点
        delete lhead, rhead;
        return head;
    }
};