正常数组上的快排很常考,可以参考 https://blog.youkuaiyun.com/Bob__yuan/article/details/98782859 以及求数组第K大元素时使用的快排:https://blog.youkuaiyun.com/Bob__yuan/article/details/100145469。
在链表上进行排序一般用归并,可以参考 https://blog.youkuaiyun.com/Bob__yuan/article/details/100621973。
本篇文章记录在单链表上的快排。这个在日常编程中,一般情况不会用,但是网上有面经说考到了,所以记录一下。
由于链表是不能随机存取的,只能顺序遍历,所以不能按照下标的方式进行遍历,但是思想和正常数组上的快排是一模一样的。核心思想就是用两个指针 slow 和 fast,每次保证 slow 一定指向 <= pivot 的节点,slow 的下一个节点值一定 > pivot 或者 slow == fast。
算法步骤如下:
1、每轮迭代设 slow 指向第一个节点(第一个节点为 pivot),fast 指向 slow 的下一个节点;
2、当 fast 没有到达链表末尾时,若 fast 指向的值 >= pivot,更新 fast 指向 fast 的下一个元素;若 < pivot,则将更新 slow 指向 slow 的下一个元素,并将 slow 与 fast 指向的值交换,更新 fast 指向 fast 的下一个元素。也就是 fast 每次一定是往前走一个位置的。
3、循环执行步骤2直至 fast 到达链表尾部;交换链表头元素与 slow 指向元素值。
4、以 slow 为分割点(后半部分链表的 head)将链表分为两部分,两个序列递归调用上述步骤排序完成。
#include <iostream>
using namespace std;
struct ListNode {
int val;
ListNode *next;
ListNode(int x) : val(x), next(nullptr) {}
};
int rnd = 1; // round用于输出每一轮快排结果
ListNode* h;
ListNode* partition(ListNode* begin, ListNode* end) {
if (!begin) return nullptr;
ListNode* slow = begin, *fast = begin->next;
const int pivot = begin->val;
while (fast != end) {
if (fast->val < pivot) {
slow = slow->next;
swap(slow->val, fast->val);
}
fast = fast->next;
}
swap(begin->val, slow->val);
return slow;
}
void quicksort(ListNode* begin, ListNode* end) {
if (begin != end) {
ListNode* mid = partition(begin, end);
/// 输出每一轮快排结果
ListNode *ite = h;
printf("第 %d 轮快排结果为:", rnd++);
while (ite) {
cout << ite->val << ' ';
ite = ite->next;
}
cout << endl;
quicksort(begin, mid);
quicksort(mid->next, end);
}
}
int main() {
// 6 2 1 3 5 4
ListNode *head = new ListNode(6);
h = head;
head->next = new ListNode(2);
head = head->next;
head->next = new ListNode(1);
head = head->next;
head->next = new ListNode(3);
head = head->next;
head->next = new ListNode(5);
head = head->next;
head->next = new ListNode(4);
quicksort(h, nullptr); // 左闭右开区间!!
while (h) {
cout << h->val << " ";
h = h->next;
}
}
6 2 1 3 5 4
结果如下:
第 1 轮快排结果为:4 2 1 3 5 6
第 2 轮快排结果为:3 2 1 4 5 6
第 3 轮快排结果为:1 2 3 4 5 6
第 4 轮快排结果为:1 2 3 4 5 6
第 5 轮快排结果为:1 2 3 4 5 6
第 6 轮快排结果为:1 2 3 4 5 6
1 2 3 4 5 6
上边代码是有调试测试的,实际核心代码就是:
ListNode* partition(ListNode* begin, ListNode* end) {
if (!begin) return nullptr;
ListNode* slow = begin, *fast = begin->next;
const int pivot = begin->val;
while (fast != end) {
if (fast->val < pivot) {
slow = slow->next;
swap(slow->val, fast->val);
}
fast = fast->next;
}
swap(begin->val, slow->val);
return slow;
}
void quicksort(ListNode* begin, ListNode* end) {
if (begin != end) {
ListNode* mid = partition(begin, end);
quicksort(begin, mid);
quicksort(mid->next, end);
}
}