详解堆排序 附代码 无序数组中找到最小的k个数

堆排序:
堆排序时间复杂度是O(NlogN)
空间复杂度如果不用递归的话可以达到O(1),在数组上原地排序
这里给出堆排序代码
注意此处在筛操作的时候sift(Object[] a, int n, int i),中间的参数n是代表这时候数组的总大小。因为在建完堆后,数组从后往前会一轮一轮的有序,n会渐渐减小。在无序数组中找最小的k个数的代码中就不需要记录这个值,在这代码中,需要建大根堆,用数组中的其他数跟大根堆堆顶比较。
筛操作也就是sift()方法,它代表,在建好的二叉堆上如果堆顶换了一个值,通过筛操作使得该数组又符合二叉堆的条件。那为什么建堆的时候也是用这个方法呢,因为这里是从底向上建堆,每次经过父节点时,都能保证它的左孩子和右孩子是二叉堆。
这里建堆的复杂度就是O(N),而不是我们想象中的O(NlogN)。
建堆过程和时间复杂度证明如下
这里写图片描述
但是后面排序,即把堆顶放到数组后面的过程,在进行筛操作,时间复杂度就变成O(NlogN)了。

public class Solution {
    public static void main(String[] args) {
        Integer[] a = new Integer[]{3,4,2,6,5,7,9,1,8};
        heapSort(a, 9);
        System.out.println(Arrays.toString(a));
        //buildHeap(a);
        return;
    }

    public static void heapSort(Object[] a, int n) {
        Object x;
        for (int i = n / 2 - 1; i >= 0; i--) {
            sift(a, n, i);
        }
        for (int i = 1; i < n; i++) {
            x = a[0];
            a[0] = a[n-i];
            a[n-i] = x;
            sift(a, n-i, 0);
        }
        return;
    }

    public static void sift(Object[] a, int n, int i){
        int j = 2*i+1;
        while (j <= n-1){
            if (j<n-1 && ((Comparable)a[j]).compareTo((Comparable)a[j+1]) <0){
                j++;
            }

            if (((Comparable)a[i]).compareTo((Comparable)a[j]) <0){
                swap(a, i, j);
                i = j;
                j = 2*i+1;
            }
            else {
                break;
            }
        }

    }

    public static void swap(Object[] a, int i, int j){
        Object tmp = a[i];
        a[i] = a[j];
        a[j] = tmp;
    }
}

【无序数组中找到最小的k个数】
假设数组长度是N
下面代码的时间复杂度O(NlogK)
还有一种方法可以做到时间复杂度O(N),利用快速排序的原理。

public class Main {
    public static void main(String[] args) {
        int[] arr = { 6, 9, 1, 3, 1, 2, 2, 5, 6, 1, 3, 5, 9, 7, 2, 5, 6, 1, 9 };
        int k = 10;
        System.out.println(Arrays.toString(getMinKNumsByHeap(arr, k)));
    }

    //自己建立大根堆排序
    public static int[] getMinKNumsByHeap(int[] arr, int k){
        int[] heap = new int[k];
        for (int i = 0; i < k; i++) {
            heap[i] = arr[i];
        }
        for (int i = k/2 -1; i >= 0 ; i--) {
            seaf(heap, i);
        }
        //k+1..,N个值一一放进去比对
        for (int i = k; i < arr.length; i++) {
            if (arr[i] < heap[0]){
                heap[0] = arr[i];
                seaf(heap, 0);
            }
        }
        return heap;
    }

    //筛选堆
    public static void seaf(int[] arr, int i){
        int father = i;
        int index = 2 * i + 1;
        while (index < arr.length){
            if (index < arr.length -1){
                if (arr[index+1] > arr[index]){
                    index = index +1;
                }
            }
            if (arr[index] > arr[father]){
                int tmp = arr[index];
                arr[index] = arr[father];
                arr[father] = tmp;
                father = index;
                index = 2* index + 1;
            }
            else {
                break;
            }
        }
    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值