快速排序算法(三種分區方法要熟練!)


快排確實厲害!!!

總的思想是分治遞歸,取定一個值作為標簽,比該值小的去左邊,比該值大的去右邊。

單向掃描分區法:

 

去左邊的操作:只將sp++即可

去右邊的操作:具體是將sp指向的值與bigger指向的值交換

考慮邊界:當掃描指針sp與bigger相等時,再執行一次循環后,sp剛好在bigger的右邊一格。

 1 /*
 2  * 快速排序算法
 3  */
 4 void swp(int arr[], int n, int m)
 5 {
 6     int temp = arr[n];
 7     arr[n] = arr[m];
 8     arr[m] = temp;
 9 }
10 int Part(int arr[], int p, int r)
11 {
12     int pivot = arr[p]; // 定中間數
13     int sp = p + 1;   // 掃描指針
14     int bigger = r;   // 右側指針
15     while (sp <= bigger)
16     {
17         if (arr[sp] > pivot) {
18             swp(arr, sp, bigger);
19             bigger--;
20         }
21         else
22             sp++;
23     }
24     swp(arr, p, bigger);
25     return bigger;
26 }
27 void quickSort(int arr[], int p, int r)
28 {
29     int q = 0;
30     if (p < r) {
31         // 分區:小於的數移動到左邊,大於的數移動到右邊
32         q = Part(arr, p, r);
33         // 將排序問題分治
34         quickSort(arr, p, q - 1);
35         quickSort(arr, q + 1, r);
36     }
37 }
38 int main()
39 {
40     int ar[2] = {1, -1};
41     quickSort(ar, 0, 1);
42     // 打印
43     for (int i = 0; i <= 1; i++)
44         cout << ar[i] << endl;
45     return 0;
46 }

雙向掃描分區法:

與單向掃描分區類似,但left指針一直往右移,直到大於中間值時停止;right指針一直往左移,直到小於中間值時停止。然后left值與right值交換。之后left繼續一直左移,right一直右移。重復執行。

小心:left一直左移(或right一直右移)導致的數組越界問題。

 1 /*
 2  * 快速排序算法
 3  */
 4 void swp(int arr[], int n, int m)
 5 {
 6     int temp = arr[n];
 7     arr[n] = arr[m];
 8     arr[m] = temp;
 9 }
10 int Part(int arr[], int p, int r)
11 {
12     int pivot = arr[p]; // 定中間數
13     int left = p + 1;  // 左指針
14     int right = r;     // 右指針
15     // 雙向掃描核心
16     while (left <= right)
17     {
18         while (left <= right && arr[left] <= pivot) left++;
19         while (left <= right && arr[right] > pivot) right--;
20         if (left < right)
21             swp(arr, left, right);
22     }
23     swp(arr, p, right);
24     return right;
25 }
26 void quickSort(int arr[], int p, int r)
27 {
28     int q = 0;
29     if (p < r) {
30         // 分區:小於等於的數移動到左邊,大於的數移動到右邊
31         q = Part(arr, p, r);
32         // 將排序問題分治
33         quickSort(arr, p, q - 1);
34         quickSort(arr, q + 1, r);
35     }
36 }
37 int main()
38 {
39     int ar[8] = {2, 3, 3, 3, 45, 8, 4, 6};
40     quickSort(ar, 0, 7);
41     // 打印
42     for (int i = 0; i <= 7; i++)
43         cout << ar[i] << " ";
44     return 0;
45 }

三指針分區法:

三指針分區法對於相等元素較多的數組能提升一定的效率。

Next_Less_Pos指針始終指向數組的相等區第一個元素;Next_Scan_Pos指針始終指向要掃描區域的第一個元素;Next_Bigger_Pos指針的右區域所有元素總大於主元。

 1 /*
 2  * 快速排序算法
 3  */
 4 void swp(int arr[], int n, int m)
 5 {
 6     int temp = arr[n];
 7     arr[n] = arr[m];
 8     arr[m] = temp;
 9 }
10 void Part(int arr[], int p, int &e, int &bigger)
11 {
12     int pivot = arr[p]; // 定中間數
13     int s = e;
14     // 三指針分區核心
15     while (s <= bigger) {
16         if (arr[s] < pivot) { 
17             swp(arr, s, e);
18             s++; e++;
19         }
20         else if (arr[s] > pivot) {
21             swp(arr, s, bigger);
22             bigger--;
23         }
24         else s++; 
25     }
26     swp(arr, e - 1, p);
27 }
28 void quickSort(int arr[], int p, int r)
29 {
30     int left = p + 1, right = r;
31     if (p < r) {
32         // 分區:小於的數移動到左邊,大於的數移動到右邊,等於的數在中間
33         Part(arr, p, left, right);
34         // 將排序問題分治
35         if (left > p) quickSort(arr, p, left - 1);
36         if (right < r) quickSort(arr, right + 1, r);
37     }
38 }
39 int main()
40 {
41     int ar[20];
42     srand((unsigned)time(nullptr));
43     for (int i = 0; i <= 19; i++)
44         ar[i] = rand() % (10 - 1 + 1) + 1;
45     quickSort(ar, 0, 19);
46     // 打印
47     for (int i = 0; i <= 19; i++)
48         cout << ar[i] << " ";
49     return 0;
50 }

小心邊界:必須是相等區域的前一個小於元素,和相等區域的后一個大於元素(即 left - 1 和 right + 1),這時能避免無限循環。

但在避免無限循環的同時,又得保證邊界下標的合理性,故我們此時加 if 語句判斷。

 

補充 工程實踐中的一些優化:

  • 三點中值法(去首元素、尾元素和中間元素,取三元素的中間值作為主元)
  • 絕對中值法(花費 O(n) 的時間,尋找中間值作為主元)
  • 部分插入排序(在遞歸樹中,對於元素較少的部分直接用插入排序)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM