1.快排
时间复杂度退化情况
基本有序的序列对于快速排序是最坏的场景,也是上文中面试官提到的其中一个优化点。因为每次只分割出一个基准点,需要N次分割,时间复杂度为O(N^2)
public static void shuffle(int[] nums) {int n = nums.length;Random rand = new Random();for (int i = 0 ; i < n; i++) {// 从 i 到最后随机选一个元素int r = i + rand.nextInt(n - i);swap(nums, i, r);}public static void quickSort(int[] list, int startIndex, int endIndex) {if (startIndex >= endIndex) return;int base = partition(list, startIndex, endIndex);quickSort(list, startIndex, base - 1);quickSort(list, base + 1, endIndex);}public static int partition(int[] list, int startIndex, int endIndex) {int pivot = list[startIndex];int left = startIndex;int right = endIndex;while (left < right) {// 保证右指针先向左移动,使左右指针重合时,一定指向比基准数小的数while (left < right && list[right] > pivot) {right--;}while (left < right && list[left] <= pivot) {left++;}if (left < right) {swap(list, left, right);}}swap(list, startIndex, left);return left;}private static void swap(int[] list, int a, int b) {int temp;temp = list[a];list[a] = list[b];list[b] = temp;}
2.归并
需额外数组
public void mergeSort(int array[], int first, int last, int temp[]) {if (first < last) {int mid = (first + last) / 2;mergeSort(array, first, mid, temp); // 递归归并左边元素mergeSort(array, mid + 1, last, temp); // 递归归并右边元素merge(array, first, mid, last, temp); // 再将二个有序数列合并}}private void merge(int list[], int first, int mid, int last, int temp[]) {int i = first, j = mid + 1; // i为第一组的起点, j为第二组的起点int k = 0; // k用于指向temp数组当前放到哪个位置while (i <= mid && j <= last) { // 将两个有序序列循环比较, 填入数组tempif (list[i] <= list[j])temp[k++] = list[i++];elsetemp[k++] = list[j++];}while (i <= mid) { // 如果比较完毕, 第一组还有数剩下, 则全部填入temptemp[k++] = list[i++];}while (j <= last) {// 如果比较完毕, 第二组还有数剩下, 则全部填入temptemp[k++] = list[j++];}for (i = 0; i < k; i++) {// 将排好序的数填回到array数组的对应位置list[first + i] = temp[i];}}
3.堆排
上浮 下沉
public class MaxPQ<Key extends Comparable<Key>> {
private Key[] pq;
private int N = 0;
public MaxPQ(int cap) {
pq = (Key[]) new Comparable[cap + 1];
}
public Key max() {
return pq[1];
}
public void insert(Key e) {
N++;
// 先把新元素加到最后
pq[N] = e;
// 然后让它上浮到正确的位置
swim(N);
}
public Key delMax() {
// 最大堆的堆顶就是最大元素
Key max = pq[1];
// 把这个最大元素换到最后,删除之
exchange(1, N);
pq[N] = null;
N--;
// 让 pq[1] 下沉到正确位置
sink(1);
return max;
}
private void swim(int k) {
while (k > 1 && less(parent(k), k)) {
exchange(parent(k), k);
k = parent(k);
}
}
private void sink(int k) {
while (left(k) <= N) {
int older = left(k);
if (right(k) <= N && less(older, right(k))) {
older = right(k);
}
if(less(older,k)) break;
exchange(k,older);
k = older;
}
}
private void exchange(int i, int j) {
Key temp = pq[i];
pq[i] = pq[j];
pq[j] = temp;
}
private boolean less(int i, int j) {
return pq[i].compareTo(pq[j]) < 0;
}
// 父节点的索引
int parent(int root) {
return root / 2;
}
// 左孩子的索引
int left(int root) {
return root * 2;
}
// 右孩子的索引
int right(int root) {
return root * 2 + 1;
}
}
