快速排序
中心思想:分治法
核心问题
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