原题链接: 86. 分隔链表 - 力扣(Leetcode)
tag: 双指针, 链表.
一. 题目
给你一个链表的头节点 head 和一个特定值 x , 请你对链表进行分隔, 使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前.
你应当 保留 两个分区中每个节点的初始相对位置.
二. 题解
本题我们通过将原链表的节点 尾插 到分隔链表的左半部分或右半部分来分隔链表.
由于尾插第一个节点时分隔链表的左半部分或右半部分中没有节点, 即 ltail == nullptr 或 rtail == nullptr, 无法通过 ltail->next = curr 或者 rtail->next = curr 来尾插节点, 需要分类讨论.
为了避免这种额外的讨论, 我们引入虚拟头节点(dummy)的概念.
设置虚拟头节点, 即在链表的头节点前新插入一个节点. 如此, 链表的头节点便有了前驱节点(即dummy), 尾插时就不需要进行额外的讨论.
分隔链表前.
设置两个虚拟头节点 lhead 和 rhead.
ListNode* lhead = new ListNode(), * rhead = new ListNode();
定义两个尾指针 ltail 和 rtail 分别指向虚拟头节点 lhead 和 rhead.
ListNode* ltail = lhead, * rtail = rhead;
定义 curr 指针指向原链表头节点 head.
ListNode* curr = head;
当 curr 不为空时.
curr != nullptr
curr->val < x
将 curr 所指节点尾插到 ltail 所指节点后.
更新 ltail 指针.
ltail = ltail->next;
更新 curr 指针.
curr = curr->next;
重复这一步骤.
重复这一步骤.
重复这一步骤.
重复这一步骤.
重复这一步骤.
curr == nullptr, while 循环终止.
将分隔链表的左半部分的尾节点的 next 指针指向右半部分的头节点.
ltail->next = rhead->next;
将分隔链表的右半部分的尾节点置空.
rtail->next = nullptr;
head = lhead->next;
delete lhead, rhead;
分隔链表后.
三. 复杂度分析
时间复杂度: 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;
}
};