快速排序
中心思想:分治法
核心問題
1、基準元素的選擇
方案一:以數組的第一個元素爲基準元素
極端情況:原數組有序,所選的元素剛好是最大/小值,導致每次分治後,數據嚴重傾斜,效率低下,時間複雜度最差(n^2)
方案二:隨機選一個作爲基準元素
也有極小几率選到數列的最大/小值
2、元素的移動
(1)、挖坑法
代碼如下:
import java.util.Arrays;
public class QuickSort {
public static void quickSort(int[] arr, int startIndex, int endIndex) {
// 遞歸結束條件:startIndex大等於endIndex的時候
if (startIndex >= endIndex) {
return;
}
// 得到基準元素位置
int pivotIndex = partition(arr, startIndex, endIndex);
// 用分治法遞歸數列的兩部分
quickSort(arr, startIndex, pivotIndex - 1);
quickSort(arr, pivotIndex + 1, endIndex);
}
private static int partition(int[] arr, int startIndex, int endIndex) {
// 取第一個位置的元素作爲基準元素
int pivot = arr[startIndex];
int left = startIndex;
int right = endIndex;
// 坑的位置,初始等於pivot的位置
int index = startIndex;
//大循環在左右指針重合或者交錯時結束
while (right >= left) {
//right指針從右向左進行比較
while (right >= left) {
if (arr[right] < pivot) {
arr[left] = arr[right];
index = right;
left++;
break;
}
right--;
}
//left指針從左向右進行比較
while (right >= left) {
if (arr[left] > pivot) {
arr[right] = arr[left];
index = left;
right--;
break;
}
left++;
}
}
arr[index] = pivot;
return index;
}
public static void main(String[] args) {
int[] arr = new int[]{4, 7, 6, 5, 3, 2, 8, 1};
quickSort(arr, 0, arr.length - 1);
System.out.println(Arrays.toString(arr));
}
}
(2)、指針交換法
指針交換法與挖坑法的不同在於partition函數的實現,交換次數比挖坑法更少。代碼如下:
// 參考:https://blog.csdn.net/caofengtao1314/article/details/81978388
import java.util.Arrays;
public class QuickSort {
public static void quickSort(int[] arr, int startIndex, int endIndex) {
// 遞歸結束條件:startIndex大等於endIndex的時候
if (startIndex >= endIndex) {
return;
}
// 得到基準元素位置
int pivotIndex = partition(arr, startIndex, endIndex);
// 根據基準元素,分成兩部分遞歸排序
quickSort(arr, startIndex, pivotIndex - 1);
quickSort(arr, pivotIndex + 1, endIndex);
}
private static int partition(int[] arr, int startIndex, int endIndex) {
// 取第一個位置的元素作爲基準元素
int pivot = arr[startIndex];
int left = startIndex;
int right = endIndex;
while (left != right) {
//控制right指針比較並左移
while (left < right && arr[right] > pivot) {
right--;
}
//控制right指針比較並右移
while (left < right && arr[left] <= pivot) {
left++;
}
//交換left和right指向的元素
if (left < right) {
int p = arr[left];
arr[left] = arr[right];
arr[right] = p;
}
}
//pivot和指針重合點交換
int p = arr[left];
arr[left] = arr[startIndex];
arr[startIndex] = p;
return left;
}
public static void main(String[] args) {
int[] arr = new int[]{4, 7, 6, 5, 3, 2, 8, 1};
quickSort(arr, 0, arr.length - 1);
System.out.println(Arrays.toString(arr));
}
}
非遞歸實現(棧):
// 參考:https://blog.csdn.net/caofengtao1314/article/details/81978388
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Stack;
public class QuickSortWithStack {
public static void quickSort(int[] arr, int startIndex, int endIndex) {
// 用一個集合棧來代替遞歸的函數棧
Stack<Map<String, Integer>> quickSortStack = new Stack<>();
// 整個數列的起止下標,以哈希的形式入棧
Map rootParam = new HashMap();
rootParam.put("startIndex", startIndex);
rootParam.put("endIndex", endIndex);
quickSortStack.push(rootParam);
// 循環結束條件:棧爲空時結束
while (!quickSortStack.isEmpty()) {
// 棧頂元素出棧,得到起止下標
Map<String, Integer> param = quickSortStack.pop();
int start = param.get("startIndex");
int end = param.get("endIndex");
// 得到基準元素位置
int pivotIndex = partition(arr, start, end);
// 根據基準元素分成兩部分, 把每一部分的起止下標入棧
if (start < pivotIndex - 1) {
Map<String, Integer> leftParam = new HashMap<>();
leftParam.put("startIndex", start);
leftParam.put("endIndex", pivotIndex - 1);
quickSortStack.push(leftParam);
}
if (pivotIndex + 1 < end) {
Map<String, Integer> rightParam = new HashMap<>();
rightParam.put("startIndex", pivotIndex + 1);
rightParam.put("endIndex", end);
quickSortStack.push(rightParam);
}
}
}
private static int partition(int[] arr, int startIndex, int endIndex) {
// 取第一個位置的元素作爲基準元素
int pivot = arr[startIndex];
int left = startIndex;
int right = endIndex;
while (left != right) {
//控制right指針比較並左移
while (left < right && arr[right] > pivot) {
right--;
}
//控制right指針比較並右移
while (left < right && arr[left] <= pivot) {
left++;
}
//交換left和right指向的元素
if (left < right) {
int p = arr[left];
arr[left] = arr[right];
arr[right] = p;
}
}
//pivot和指針重合點交換
int p = arr[left];
arr[left] = arr[startIndex];
arr[startIndex] = p;
return left;
}
public static void main(String[] args) {
int[] arr = new int[]{4, 7, 6, 5, 3, 2, 8, 1};
quickSort(arr, 0, arr.length - 1);
System.out.println(Arrays.toString(arr));
}
}
還有python版本的實現:
# 快速排序-指針交換法-非遞歸版本
def quick_sort(arr, start_index, end_index):
# arr : [start_index, end_index] 左右閉區間
parameter_stack = [[start_index, end_index]]
while parameter_stack:
param = parameter_stack.pop()
s = param[0]
e = param[1]
pivot = partition(arr, s, e)
# print(arr)
if pivot + 1 < e:
parameter_stack.append([pivot + 1, e])
if pivot - 1 > s:
parameter_stack.append([s, pivot - 1])
def partition(arr, start_index, end_index):
left = start_index
right = end_index
value = arr[start_index]
while left != right:
# 注意這個細節:先從右邊開始,就返回left位置
while left < right and arr[right] > value:
right -= 1
while left < right and arr[left] <= value:
left += 1
if left < right:
arr[left], arr[right] = arr[right], arr[left]
arr[left], arr[start_index] = arr[start_index], arr[left]
return left
if __name__ == '__main__':
arr = [4, 7, 6, 5, 3, 2, 8, 1]
quick_sort(arr, 0, len(arr) - 1)
print(arr)
參考:https://blog.csdn.net/caofengtao1314/article/details/81978388