快速排序(时间O(nlogn),空间O(1)就地排序):
public void quickSort(int[] input,int p,int q) {
int partition;
if (p < q) {
partition= partition(input,p,q);
quickSort(input,p,partition-1);
quickSort(input,partition+1,q);
}
}
public int partition(int[] input, int p, int q) {
int temp = input[p];
int i = p,j = q;
while (i != j) {
while (input[j] >= temp && i < j) { /**必须是i<j,不能是<=*/
j--;
}
while (input[i] <= temp && i < j) {/**必须是i<j,不能是<=*/
i++;
}
if (i >= j) {
break;
}
swap(input,i,j);
}
/***======================必须==================**/
input[p] = input[i];
input[i] = temp;
/***======================必须==================**/
return i;
}
堆排序(时间O(nlogn),空间O(1)就地排序):
参考https://blog.csdn.net/qq_36186690/article/details/82505569
用于求解 TopK Elements 问题,也就是 K 个最小元素的问题。可以维护一个大小为 K 的最小堆,最小堆中的元素就是最小元素。最小堆需要使用大顶堆来实现,大顶堆表示堆顶元素是堆中最大元素。这是因为我们要得到 k 个最小的元素,因此当遍历到一个新的元素时,需要知道这个新元素是否比堆中最大的元素更小,更小的话就把堆中最大元素去除,并将新元素添加到堆中。所以我们需要很容易得到最大元素并移除最大元素,大顶堆就能很好满足这个要求。
堆也可以用于求解 Kth Element 问题,得到了大小为 k 的最小堆之后,因为使用了大顶堆来实现,因此堆顶元素就是第 k 大的元素。
快速选择也可以求解 TopK Elements 问题,因为找到 Kth Element 之后,再遍历一次数组,所有小于等于 Kth Element 的元素都是 TopK Elements。
一般升序采用大顶堆,降序采用小顶堆
堆是具有以下性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;或者每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆。如下图:
大顶堆:arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2]
小顶堆:arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2]
二叉树最后一个非叶子节点序号:n/2-1(序号从0开始)
堆排序的基本思想是:
1) 从最后一个非叶子节点开始(因为叶子本身就是堆),将待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点。
2) 将其与末尾元素进行交换,此时末尾就为最大值。
3) 然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。
如此反复执行,便能得到一个有序序列了
//初始化堆:时间复杂度O(n)
private void heapSort(int[] input) {
//1. 构建初始大顶堆
for (int i = input.length/2 - 1 ; i >= 0 ;i--) {
// 从第一个非叶子节点开始,从下往上,从左往右
adjustHeap(input,i,input.length);
}
// 2. 调整堆结构,堆顶元素与末尾元素交换
for (int i = input.length-1; i >= 0; i--) {
swap(input,0,i);
// 重新调整
adjustHeap(input,0,i);
}
}
/**
* 调整大顶堆,仅仅是调整,这时候大顶堆已经建立好
时间复杂度O(nlogn)
* @param arr
* @param start
* @param length
*/
private void adjustHeap(int[] arr, int start, int length) {
int temp = arr[start];
// start的左子树
for (int i = start*2+1;i < length;i = i*2+1) {
//有右子树且左子树的值小于右子树的值,就是找出两个子树中比较大的那一个
if (i+1 < length && arr[i] < arr[i+1]) {
i++;
}
if (arr[i] > temp) {
arr[start] = arr[i];
start = i;
}
else {
break;
}
}
arr[start] = temp;
}
private void swap(int[] arr,int i,int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
归并排序(时间O(nlogn),空间O(n)就地排序):
//两路归并算法,两个排好序的子序列合并为一个子序列
public void merge(int []a,int left,int mid,int right){
int []tmp=new int[a.length];//辅助数组
int p1=left,p2=mid+1,k=left;//p1、p2是检测指针,k是存放指针
while(p1<=mid && p2<=right){
if(a[p1]<=a[p2])
tmp[k++]=a[p1++];
else
tmp[k++]=a[p2++];
}
while(p1<=mid) tmp[k++]=a[p1++];//如果第一个序列未检测完,直接将后面所有元素加到合并的序列中
while(p2<=right) tmp[k++]=a[p2++];//同上
//复制回原素组
for (int i = left; i <=right; i++)
a[i]=tmp[i];
}
public void mergeSort(int [] a,int start,int end){
if(start<end){//当子序列中只有一个元素时结束递归
int mid=(start+end)/2;//划分子序列
mergeSort(a, start, mid);//对左侧子序列进行递归排序
mergeSort(a, mid+1, end);//对右侧子序列进行递归排序
merge(a, start, mid, end);//合并
}
}
出现频率问题:
桶排序!!
下标就是出现的次数