堆排序

121 阅读2分钟

堆肯定是完全二叉树。

完全二叉树,每个节点的值>=左右节点的称为大顶堆。否则是小顶堆。

“完全二叉树的特点:叶子结点只能出现在最下层和次下层,且最下层的叶子结点集中在树的左部。”

排序就排序和二叉树有什么关系呢?我们实际上可以用数组表示二叉树

832.8父节点,32分别人左右儿子节点 。

只有完全二叉树才有这种规则:

N[i] 的父节点为N[(i - 1)/2]

N[i] 的左节点为N[(2i + 1)]

N[i] 的右节点为N[(2i + 2)]

堆排序也分两步:

1.先建立一个维护堆的函数,一旦发生交换,就要维护,保持数组一直是堆结构。并且能用这个函数从底层开始建堆。

2.维护堆数组后,那么arr[0]就是最大的数,我们那最后一个元素不断和她交换,就能获取最大值,放在数组末(升序排列)。

交换后继续维护,此时传入维护的数组长度-1。又是最大值arr[0]如此往复,最终排序成功。

时间复杂度: 建堆O(n)

维护堆的函数:建堆O(logN)

所以最终O(NlogN)

public class QuickSort {
    public static void main(String[] args) {
        int[] arr = {-9,78,0,23,-567,70};
        build(arr,arr.length);
        System.out.println(Arrays.toString(arr));
    }

    //建立具有堆特征的数组
    public static void build(int[] arr,int n){
        int i = 0;
        //数组最后一个数的父节点(n - 1 - 1) / 2
        for (i = (n - 1 - 1) / 2;i >= 0;i--){
            quickSort(arr,i,n);
        }
        //建堆完成,就要开始排序了,由于是大顶堆
        //我们把最顶元素与,最后一个元素交换,并且断开最后一个元素的连接,放入数组最后
        //就能实现从小到大排序了
        for (i = n - 1;i > 0; i--){
            int temp = arr[i];
            arr[i] = arr[0];
            arr[0] = temp;
            //交换一次再次维护数组堆,剩余i个元素,n-1。
            quickSort(arr,0,i);
        }
    }


    //这个方法,负责每次检查二叉树是否为大小堆。同时能负责刚开始的建立堆
    public static void quickSort(int[] arr,int i,int n){
        //i表示父节点,n表示数组长度
        int parent = i;
        int left = 2 * parent + 1;
        int right = 2 * parent + 2;
        if (left < n && arr[left] > arr[parent]){
            parent = left;
        }
        if (right < n && arr[right] > arr[parent]){
            parent = right;
        }
        //表名发生了树结构的改变,我们需要继续校验,受到影响的树。因此需要递归
        if (parent != i){
            int temp = arr[parent];
            arr[parent] = arr[i];
            arr[i] = temp;
            //i换到parent的位置了,我们需要维护更换后的节点
            quickSort(arr,parent,n);
        }
    }
}