1. 快速排序
1)思路(先整体排序,再局部排序):
从无序数组中选择一个pivot(中心点),小于pivot的数放到左边,大于pivot的数放到右边,得到两个无序子数组。再分别对两个无序子数组选择pivot(中心点),小于pivot的数放在左边,大于pivot的数放在右边,以此类推,层层递归。递归终止条件是子数组中只剩下一个数,无需排序。
2)时间复杂度:最优为 O(nlogn),最坏为 O(n^2)
空间复杂度:O(1),不需要额外的空间,直接改动原数组
public class Solution {
/*
* @param A: an integer array
* @return:
*/
public void sortIntegers2(int[] A) {
quickSort(A, 0, A.length - 1);
}
public void quickSort(int[] A, int start, int end) {
if (start >= end) {
return;
}
int pivot = A[start + (end - start) / 2];
int left = start;
int right = end;
// left <= right not left < right
while (left <= right) {
// A[left] < pivot not <=, 避免极端情况导致时间复杂度变大
while (left <= right && A[left] < pivot) {
left++;
}
while (left <= right && A[right] > pivot) {
right--;
}
if (left <= right) {
int temp = A[left];
A[left] = A[right];
A[right] = temp;
left++;
right--;
}
}
// sort A[start... right]
quickSort(A, start, right);
// sort A[left ... end]
quickSort(A, left, end);
}
}
注意:
1)如果将left<=right 改成 left<right,则在指针相遇时便跳出while循环。
考虑不断将数组分区至子数组包含两个整数时,往下再分区其子数组分别包含1个整数、2个整数,即 [2,3] ----> [2] , [2,3]
递归会无限调用,遇到stackOverFlowError
2) 如果将代码中A[left] < pivot 改成 A[left] <= pivot
当遇到极端情况,数组中有大量相同整数,即[1,1,1,1,1,1,2,4,7]
每当A[left] <= pivot,left指针便右移,则分割后的子数组会一大一小,导致时间复杂度变大。
2. 归并排序
1) 思路(先局部排序,再整体排序)
将给定的无序数组从中间分割成两个子数组,再分别将分割后的子数组从中间分隔,以此类推,直至数列中只有一个元素。从小到大将子数组排序后再合并。
2)时间复杂度:稳定为O(nlogn)
空间复杂度:O(n),因为需要额外的空间存放排好序的数,再对应赋值给原数组
public class Solution {
/**
* @param A an integer array
* @return void
*/
public void sortIntegers2(int[] A) {
// use a shared temp array, the extra memory is O(n) at least
int[] temp = new int[A.length];
mergeSort(A, 0, A.length - 1, temp);
}
private void mergeSort(int[] A, int start, int end, int[] temp) {
if (start >= end) {
return;
}
int left = start, right = end;
int mid = (start + end) / 2;
mergeSort(A, start, mid, temp);
mergeSort(A, mid + 1, end, temp);
merge(A, start, mid, end, temp);
}
private void merge(int[] A, int start, int mid, int end, int[] temp) {
int left = start;
int right = mid + 1;
int index = start;
// merge two sorted subarrays in A to temp array
while (left <= mid && right <= end) {
if (A[left] < A[right]) {
temp[index++] = A[left++];
} else {
temp[index++] = A[right++];
}
}
while (left <= mid) {
temp[index++] = A[left++];
}
while (right <= end) {
temp[index++] = A[right++];
}
// copy temp back to A
for (index = start; index <= end; index++) {
A[index] = temp[index];
}
}
}
3. 快速选择算法
1)思路:
利用了快速排序算法的分区(partition) 思路,选取数组的中心点pivot,小于/大于pivot的数分别放到两边,只考虑包含target的子数组并对其进行排序。
2)时间复杂度 为O(n)
4. 例题
leetcode:剑指 Offer II 076. 数组中的第 k 大的数字,75. 颜色分类, 912. 排序数组