堆结构:完全二叉树结构
i的 左子节点:2*1+1 右子节点:2*1+2 父节点:(i-1)/2
大根堆:完全二叉树,每一颗子树的最大值都是头节点的值就是大根堆
小根堆:完全二叉树,每一颗子树的最小值都是头节点的值就是小根堆
heapInsert过程 实现大根堆
// 某个数现在处在index位置,往上继续移动
public static void heapInsert(int[] arr, int index) {
while(arr[index] > arr[(index - 1)/2]) {
swap(arr, index, (index - 1)/2);
index = (index - 1)/2;
}
}
大根堆中 依次删除头节点,再实现大根堆的过程 其实是依次将头节点进行排序:删除头节点,将最后一个节点换到头节点,heapSize-1,指针指向头节点进行heapify
public static void heapify(int[] arr, int index, int heapSize) {
int left = index*2+1; // 左孩子的下标
while(left < heapSize) { // 当下方还有孩子的时候
// 两个孩子中,谁的值大,把下标给largest
// left+1是右孩子,当右孩子还存在 并大于左孩子时,右孩子为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;
}
}
堆排序:时间复杂度:O(N*logN) 空间复杂度O(l)
public static void heapSort(int[] arr) {
if(arr == null || arr.length < 2) {
return;
}
// 把数组整体范围搞成大根堆
// for(int i = 0; i < arr.length; i++) { // O(N)
// heapInsert(arr, i); // O(logN)
// }
// 或者每个位置进行heapify 稍微快一点 复杂度不变
for(int i = arr.length-1;i >= 0; i--) {
heapify(arr,i, arr.length);
}
// 0位置数和最后一个位置数交换,然后堆大小--
int heapSize = arr.length;
swap(arr,0,--heapSize);
while(heapSize > 0) { // O(N)
heapify(arr,0,heapSize); // O(logN)
swap(arr,0, --heapSize); // O(1)
}
}}
}
优先级队列---堆结构
问题:已知一个几乎有序的数组,几乎有序是指,如果把数组排好顺序的话,每个元素移动的距离不可以超过k,并且k相对于数组来说比较小。请选择一个合适的排序算法针对这个数据进行排序。
O(N*logN)
public void sortArrayDistanceLessK(int[] arr, int k) {
// 默认小根堆
PriorityQueue<Integer> heap = new PriorityQueue<>();
int index = 0;
for(; index <= Math.min(arr.length, k); index++) {
heap.add(arr[index]);
}
int i = 0;
for(; index < arr.length; i++, index++) {
heap.add(arr[index]);
arr[i] = heap.poll();
}
while(!heap.isEmpty()) {
arr[i++] = heap.poll();
}
}
优先级队列默认小根堆即升序排序,可以 结合比较器进行 降序排序
public static class AComp implements Comparator<Integer>{
@Override
public int compare(Integer arg0, Integer arg1) {
return arg1 - arg0;
}
}
public static void main(String[] args) {
PriorityQueue<Integer> heap = new PriorityQueue<>(new AComp());
heap.add(8);
heap.add(4);
heap.add(4);
heap.add(9);
heap.add(10);
heap.add(3);
while(!heap.isEmpty()) {
System.out.println(heap.poll());
}
}
桶排序 :不基于比较的排序
计数排序:
基数排序: 时间复杂度:O(N)
public static void radixSort(int[] arr) {
if(arr == null || arr.length < 2) {
return;
}
radixSort(arr, 0, arr.length - 1, maxbits(arr));
}
// 获取arr中最大值 有多少10进制位
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 /= 0;
}
return res;
}
// arr[begin...end]排序
public static void radixSort(int[] arr, int l, int r, int 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]++;
}
// 将count处理成前缀和
for(i = 1; i <= radix; i++) {
count[i] = count[i] + count[i - 1];
}
// 出桶 放到bucket中
for(i = r; i >= l; i--) {
j = getDigit(arr[i],d);
bucket[count[j] - 1] = arr[i];
count[j]--;
}
// 将backet中的数倒倒arr中
for(i = l, j = 0; i <= r; i++, j++) {
arr[i] = bucket[j];
}
}
}
//d是1 取出个位数字,d是2取出十位数字,d是3取出百位数字
public static int getDigit(int x, int d) {
return ((x/((int)Math.pow(10, d-1)))%10);
}