正常數組上的快排很常考,可以參考 https://blog.csdn.net/Bob__yuan/article/details/98782859 以及求數組第K大元素時使用的快排:https://blog.csdn.net/Bob__yuan/article/details/100145469。
在鏈表上進行排序一般用歸併,可以參考 https://blog.csdn.net/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);
}
}