O(nlogn)快速排序-雙路排序+詳細註解
排序使用的相關工具類與介面
執行效率
/**
* 這時一個雙路快速排序
* 這是一個雙路快速排序
* 陣列長度[2000] 值範圍[1-2000] 消耗的時間為[1]毫秒
* 陣列長度[4000] 值範圍[1-4000] 消耗的時間為[1]毫秒
* 陣列長度[8000] 值範圍[1-8000] 消耗的時間為[1]毫秒
* 陣列長度[16000] 值範圍[1-16000] 消耗的時間為[1]毫秒
* 陣列長度[32000] 值範圍[1-32000] 消耗的時間為[4]毫秒
* 陣列長度[64000] 值範圍[1-64000] 消耗的時間為[4]毫秒
* 陣列長度[128000] 值範圍[1-128000] 消耗的時間為[7]毫秒
* 陣列長度[256000] 值範圍[1-256000] 消耗的時間為[16]毫秒
* 陣列長度[512000] 值範圍[1-512000] 消耗的時間為[34]毫秒
* 陣列長度[1024000] 值範圍[1-1024000] 消耗的時間為[72]毫秒
* 陣列長度[2048000] 值範圍[1-2048000] 消耗的時間為[147]毫秒
* 陣列長度[4096000] 值範圍[1-4096000] 消耗的時間為[306]毫秒
* 陣列長度[8192000] 值範圍[1-8192000] 消耗的時間為[644]毫秒
* 陣列長度[16384000] 值範圍[1-16384000] 消耗的時間為[1335]毫秒
* 陣列長度[32768000] 值範圍[1-32768000] 消耗的時間為[2754]毫秒
* 陣列長度[65536000] 值範圍[1-65536000] 消耗的時間為[5657]毫秒
* 陣列長度[131072000] 值範圍[1-131072000] 消耗的時間為[11612]毫秒
* 陣列長度[262144000] 值範圍[1-262144000] 消耗的時間為[24653]毫秒
*/
@Test
public void quickSort2WaysMethod() {
ArraysSort arraysSort = new QuickSort2WaysMethod();
SortHelper.arraySort(arraysSort, 2000, 20);
}
程式碼實現
private final static Random RANDOM=new Random();
@Override
public String getSortName() {
return "這是一個雙路快速排序";
}
@Override
public int[] arraySortMethod(int[] ints) {
optimizeSort(ints, 0, ints.length - 1);
return ints;
}
/**
* 雙路快速排序
* 優化後快速排序分配方法
*
* @param ints
* @param l
* @param r
*/
public void optimizeSort(int[] ints, int l, int r) {
//在出現左索引等於右索引的時候
//它們都是指向同一索引所以沒有相比的必要
//這裡直接返回
// if (l >= r) {
// return;
// }
//在資料量較小的時候使用插入排序
//這是因為經過了r-l>15的快速排序後 小一邊和大一邊的模糊排序後
//在r-l<=15 的時候之間的最大差已經很小了
//在最大差越小的情況下順序可能是越有序的
//說不定已經近乎有序的了 優化的快速排序對近乎有序的排序效率非常高
//優化後的插入排序對近乎有序的陣列進行效率相對較高
//插入排序相對歸併排序減少了陣列交換的過程
if (r - l <= 15) {
InsertSortMethod.insertSortMethod(ints, l, r);
return;
}
//個人理解快排核心
//每次遍歷排序對l-r之間的陣列進行區分
//好處1.每次遍歷可以定位一個p索引在ints陣列中確定的位置
//好處2.因為每次排序都對l-r之間的資料進行大小劃分做了一次模糊的排序
//保證下次排序l-r排序之間最大差越來越小
//所以下次遍歷都比上次排序更加有序效率相對更快
int p = optimizeQuickSort(ints, l, r);
//以p中心點-1和+1為界限繼續遞迴快速排序
optimizeSort(ints, l, p - 1);
optimizeSort(ints, p + 1, r);
}
/**
* 雙路快速排序
* partition
* 優化後的排序演算法邏輯
* 之前的排序演算法邏輯是從左到右排序演算法邏輯有個問題
* 在處理重複資料較多的情況下 可能會出現等於當時比較的元素較多的情況
* 等於的那個元素要麼在<=v方或者>=方 這樣就會造成其中一段分配的元素數量極度不平衡
* 為避免這種情況 讓相等於當時比較元素的元素 相對平均的分配到每一邊
* 所以改為從兩邊向中間遞進 遇到相等時的元素時交換一下
* 這樣在處理元素差較小又相對有序的陣列的時候可以更加平均的把相等的元素分配到兩邊
*
* @param ints 整個陣列
* @param l 當前最小索引
* @param r 當前最大索引
*/
private int optimizeQuickSort(int[] ints, int l, int r) {
//隨機獲取交換一個需要對比的索引
//如果不懂 可以看下面註釋
SortHelper.swap(ints, l, RANDOM.nextInt(r - l + 1) + l);
//以交換後第l個索引為為比較物件
int v = ints[l];
//初始化左開始索引 和 右開始索引
int i = l + 1, j = r;
//開始迴圈啦
while (true) {
//從左迴圈開始
//始終保持i<=r 不會超出迴圈界限 如果inst[i]<v 則繼續迴圈
//如果等於或者大於v則跳出當前迴圈 找到下一個需要交換的索引指標
while (i <= r && ints[i] < v) {
i++;
}
//從右迴圈開始
//始終保持j>=i 不會超出迴圈界限 如果inst[i]>v 則繼續迴圈
//如果等於或者小於v則跳出當前迴圈 開始準備i與j交換索引
while (j >= i && ints[j] > v) {
j--;
}
//如果i>j 說明其中一段已經遍歷到另一端的域中
//說明已經遍歷完成了 直接退出迴圈
if (i > j) {
break;
}
//通過以上判斷後 確認有出現相等或者小一端有大於v的資料或者大一端有小於v的資料
//開始交換索引資料
SortHelper.swap(ints, i++, j--);
}
//以上迴圈完成 j處於大一端的初始位置 i的話就是小一端的結束位置
//其實這裡與i 或者j 交換都可以
//交換j與l的位置
SortHelper.swap(ints, l, j);
return j;
}
相關文章
- O(nlogn)快速排序-基礎版+詳細註釋排序
- 快速排序平均時間複雜度O(nlogn)的推導排序時間複雜度
- 時間複雜度為 O(nlogn) 的排序演算法時間複雜度排序演算法
- 時間複雜度為O(nlogn)的排序演算法時間複雜度排序演算法
- JavaScript快速排序功能詳解JavaScript排序
- 演算法 | 快速排序詳解演算法排序
- 氣泡排序與選擇排序超詳細講解排序
- Mysql中的雙路排序和單路排序MySql排序
- 資料結構與演算法 排序演算法 快速排序【詳細步驟圖解】資料結構演算法排序圖解
- 圖解快速排序圖解排序
- 三路快速排序排序
- 排序:氣泡排序&快速排序排序
- MySQL:排序(filesort)詳細解析MySql排序
- 排序之快速排序排序
- 堆排序詳解排序
- 快速排序&&歸併排序排序
- 氣泡排序和選擇排序詳解排序
- php插入排序,快速排序,歸併排序,堆排序PHP排序
- 快速排序排序
- JavaScript表格排序詳解JavaScript排序
- Java 物件排序詳解Java物件排序
- 排序演算法__快速排序排序演算法
- 排序演算法:快速排序排序演算法
- 選擇排序和快速排序排序
- 四、歸併排序 && 快速排序排序
- 排序演算法 - 快速排序排序演算法
- 排序演算法之 '快速排序'排序演算法
- 資料結構 8 基礎排序演算法詳解、快速排序的實現、瞭解分治法資料結構排序演算法
- JS快速排序&三路快排JS排序
- 氣泡排序、歸併排序與快速排序比較排序
- PHP 常見4種排序 氣泡排序、選擇排序、插入排序、快速排序PHP排序
- javascript 快速排序JavaScript排序
- 快速排序javaScript排序JavaScript
- js 快速排序JS排序
- 快速排序-java排序Java
- LeetCode:快速排序LeetCode排序
- 快速排序法排序
- 分治—快速排序排序