排序算法---堆排序

不稳定 O(nlogn)

大顶堆(从大到小排序):每个结点的值都大于等于其左右孩子的值的二叉树

小顶堆(从小到大排序):每个结点的值都小于等于其左右孩子的值的二叉树

如何排序?

将待排序序列构建成一个小顶堆,堆顶的元素即为所有元素中的最小值

在输出堆顶的最小值之后,调整堆,使剩余的n-1个元素又建成一个小顶堆,此时的堆顶元素为次小元素

以此类推,直到堆为空

如何在输出堆顶元素之后调整堆?

(1)以堆中的最后一个元素代替堆顶元素,此时堆顶元素的左右子树均符合小顶堆或者大顶堆,只需从上到下调整即可

(2)比较对顶元素和其左右孩子,

  如果右孩子的值最小,则,将右孩子和堆顶元素交换,此时,可能导致堆顶元素的右子树不满足条件,以相同方法调整右子树

  如果左孩子的值最小,则,将左孩子和堆顶元素交换,此时,可能导致堆顶元素的左子树不满足条件,以相同方法调整左子树

  如果堆顶元素就是最小的,不用调整。

如何将待排序序列构建成一个堆?

将待排序序列看成一个完全二叉树,有n个元素,标号从1开始,则最后一个标号就是n,最后一个元素即为完全二叉树的最后一个叶子节点,最后一个叶子节点的父节点即为最后一个非叶子节点。编号为n的节点的父结点的编号为n/2向下取整,从最后一个非叶子节点开始,像上述调整过程一样,从下到上调整完全二叉树为小顶堆或者大顶堆。

 

    public static void heapSort(int[] array){
        //1.根据待排序序列构建一个小顶堆
        createHeap(array);
        System.out.println("建立小顶堆完成");
        //2.输出堆顶元素,然后调整堆
        //输出堆顶元素,用最后一个元素替换堆顶元素,然后调整堆为小顶堆,循环上述过程,直到最后一个元素
        for(int i=0;i<array.length;i++){
            System.out.print(array[0]+" ");
            array[0] = array[array.length-1-i];
            //用最后一个元素代替堆顶元素之后,可能只会导致堆顶和它的左右孩子不满足要求,调整堆顶即可
            judgeHeap(array, 0, array.length-i-1);
        }
    }
    public static void createHeap(int[] array){
        int length = array.length;
        //从第n/2向下取整个元素开始调整,第n/2向下取整个元素在数组中的下标为n/2向下取整-1
        for(int i=length/2-1;i>=0;i--){
            judgeHeap(array, i, length);
        }
        
    }
    
    public static void judgeHeap(int array[],int k,int length){
        //根据完全二叉树的定义,一个节点i如果有左孩子,左孩子的编号为2*i,右孩子为2*i+1
        //左孩子在数组中的下标为2(i+1)-1=2*i+1   右孩子在数组中的下标为2(i+1)+1-1=2*(i+1)
        //每次调整的时候,都要从当前节点一直调整到当前节点下的叶子节点
        for(int i=k;i<length;){
            if(2*i+1>=length){//表示当前节点就是叶子节点
                break;
            }
            int root = array[i];
            int leftChild = Integer.MAX_VALUE;
            int rightChild = Integer.MAX_VALUE;
            //判断是否有左孩子
            if(2*i+1  < length){
                leftChild = array[2*i+1];
            }
            if(2*(i+1) < length){
                rightChild = array[2*(i+1)];
            }
            int index = 0;//记录当前节点和它的左右孩子中最小的那个数的下标
            if(leftChild<=rightChild){
                index = 2*i+1;
            }else{
                index = 2*(i+1);
            }
            
            if(root<array[index]){
                index = i;
            }
            //System.out.println("要替换的下标:"+index+"--"+i);
            if(index != i){//一个节点跟它的左右孩子节点中,最小的不是根节点,交换
                int temp = array[index];
                array[index] = array[i];
                array[i] = temp;
                //交换之后,可能会导致被交换的那个节点组成的子树不满足小顶堆,因此,要再调整
                
                i = index;
            }else{
                //如果没有交换,则不会打乱小顶堆,什么也不做,直接调整下一个节点
                break;
            }
            
            //listArray(array);
        }

    }
原始数据:49 38 65 97 76 13 27 49 
要替换的下标:7--3
49 38 65 49 76 13 27 97 
要替换的下标:5--2
49 38 13 49 76 65 27 97 
要替换的下标:1--1
要替换的下标:2--0
13 38 49 49 76 65 27 97 
要替换的下标:6--2
13 38 27 49 76 65 49 97 
建立小顶堆完成
要替换的下标:2--0
27 38 97 49 76 65 49 97 
要替换的下标:6--2
27 38 49 49 76 65 97 97 
要替换的下标:1--0
38 97 49 49 76 65 97 97 
要替换的下标:3--1
38 49 49 97 76 65 97 97 
要替换的下标:1--0
49 65 49 97 76 65 97 97 
要替换的下标:1--1
要替换的下标:2--0
49 65 76 97 76 65 97 97 
要替换的下标:1--0
65 97 76 97 76 65 97 97 
要替换的下标:0--0

 

原文地址:https://www.cnblogs.com/duanjiapingjy/p/9557586.html