堆
堆是一个完全二叉树结构(包括满二叉树和从左到右变满的二叉树)
二叉树中,对于i节点,左子孩子为2*i+1,右子孩子为2*i+2 父节点为(i-1)/2 用于后续的找
大根堆:完全二叉树里,每一棵子树的最大值是头结点的值
小根堆:每一棵子树的最小值是头结点的值
heapInsert
public static void heapInsert(int[] arr, int index) {
while(arr[index] > arr[(index-1)/2]) {//与父节点比较,0位置也不成立
swap(arr, index, (index-1)/2);
index = (index-1)/2;
}
}
给定一组数,找出最大值,大根堆的0位置的数;去掉最大值,先设定一个临时变量存最大值,然后把以及形成的堆结构的最后一个位置的数放到0位置上,heapsize-1,然后从头结点开始,与其子节点的最大值进行比较交换,若还有孩子,则继续比较,直到无孩子或不大于则停止
heapify
//某个数在index位置,能否向下移动
public static void heapify(int[] arr, int index, int heapSize){
int left = index * 2 + 1;//左孩子的下标
while(left < heapSize){//下方还有孩子时
//两个孩子中,谁的值大,把下表给largest
int largest = left + 1 < heapSize && arr[left + 1] > arr[left] ? left + 1 : left;
//父和孩子之间,谁的值大,把下标给largest
largest = arr[largest] > arr[index] ? largest : index;
if(largest == index){
break;
}
swap(arr, largest, index);
index = largest;
left = index * 2 + 1;
}
}
给定一个堆,修改i位置数,如果i位置数变大,则往上heapInsert ,变小,往下heapify
完全二叉树的高度
heapInsert和heapify 级别的调整代价
堆排序heapSort
代码:
public static void heapSort(int[] arr){
if(arr == null || arr.length < 2){
return ;
}
for (int i = 0; i< arr.length; i++){
heapInsert(arr,i);
}
int heapSize = arr.length;
swap(arr, 0 --heapSize);
while(heapSize > 0){
heapify(arr, o ,heapSize);
swap(arr, o ,--heapSize);
}//把最大值放在刚失效的位置
}
堆排序的时间复杂度 空间复杂度O(1)
问题:给一组数,怎么变成大根堆
public static void heapSort(int[] arr){
if(arr == null || arr.length < 2){
return ;
}
for(int i = arr.length - 1; i>= 0; i--){
heapify(arr, i ,arr.length);
}
int heapSize = arr.length;
swap(arr, 0 --heapSize);
while(heapSize > 0){
heapify(arr, o ,heapSize);
swap(arr, o ,--heapSize);
}//把最大值放在刚失效的位置
}
堆排序扩展题目
堆结构的扩容也是logN级别的,不会影响性能。
Java中现存的堆排序是一个黑盒,不支持已形成的堆结构修改其中一个数,再以小代价形成对结构,所以有时候要自己手写堆结构。
比较器的使用
返回负数的时候,第一个参数放前面;正数的时候,第二个在前面;0无所谓
1)实质上是重载比较运算符
2)比较器可以很好的应用在特殊标准的排序上
3)可以很好的应用在根据特殊标准排序的结构上
比较器可以使用在堆结构里,实现复杂数据的排序。
不基于比较的排序
计数排序
基数排序
//only for no-negative value
public static void radixSort(int[] arr){
if (arr == null || arr.length < 2){
return;
}
radixSort(arr, 0, arr.length - 1, maxbits(arr));
}
public static int maxbits(int[] arr){
int max = Integer.MIN_VALUE;
for(int i = 0; i < arr.length; i++){
max = Math.max(max, arr[i]);
}
int res = 0;
while(max != 0){
res++;
max /= 10;
}
return res;
}
//arr[begin..end]排序
public static void radixSort(int[] arr, int L, int R, int digit){//digit最大值有几个十进制位
final int radix = 10;
int i = 0, j = 0;
//有多少个数准备多少个辅助空间
int[] bucket = new int[R - L + 1];
for(int d = 1; d <= digit; d++){//有多少位就进出几次
//10个空间
//count[0]当前位(d位)是0的数字有多少个
//count[1]当前位(d位)是0和1的数字有多少个
//count[2]当前位(d位)是0、1和2的数字有多少个
//count[i]当前位(d位)是(0~i)的数字有多少个
int[] count = new int[radix]; // count[0..9]
for (i = L; i <= R; i++){
j=getDigit(arr[i], d);
count[j]++;
}
for(i = 1; i < radix; i++){
count[i] = count[i] + count[i-1];
}
for(i = R; i >= L; i--){ //倒着看数字放
j = getDigit(arr[i],d);
bucket[count[j]-1]=arr[i];
count[j]--;
}
for(i = L, j = 0; i <= R; i++, j--){
arr[i] = bucket[j];
}
}
}
public static int getDigit(int x, int d){
return (( x / (( int) Math.pow(10, d-1))) % 10);
}