堆排序

思想

堆是什么?
堆是一个数组,它可以被看成一个近似的完全二叉树,树上的每一个结点对应数组中的一个元素。除去最底层外,该树是完全充满的,而且从左到右填充。
堆分为大顶堆和小顶堆
大顶堆:结点 i 的值 都大于其左右孩子结点的值
小顶堆:结点 i 的值 都小于其左右孩子结点的值
如何建堆?
建堆的过程是一个不断将元素插入到堆的过程。这里的插入过程,和插入排序很类似,插入排序重点是找到插入的位置,找到位置后更新该位置值。对于堆,重要的位置有三个:left,right,parent,对当前i位置,其左孩子位置left,右孩子位置right,当插入 i 位置的元素时候,当 i 位的元素 比left和right位置的元素大,i位置符合大顶堆的条件,不需要调整,当i位置的元素和left、right位置元素比较时候不是最大值,需要调整堆
如何调整堆?
最大位置id元素和i位置处的元素进行交换,交换后可能不是大顶堆了,这时候需要调整的位置就是交换后的最大位置id元素,这就是一个递归的过程。
如何堆排序?
堆已经建立好了,0号位置是最大值,但是这个堆不一定是有序的,其只是满足 parent大于left right位置的值,所以,把0号位置元素和最后位置进行交换,然后调整0号位置使得是大顶堆。

结点

public int parent(int i){
        int p = (i-1)/2;
        return p;
    }
    public int left(int i){
        int l = i*2+1;
        return l;
    }
    public int right(int i){
        int r = i*2+2;
        return r;
    }

插入元素

插入新元素调整为大顶堆
递归算法

/**
     * i 位置元素插入到堆中,调整为大顶堆
     * 递归实现
     * @param A
     * @param n
     * @param i
     */
    public void heap(int[] A,int n,int i){
        // 左孩子
        int l = left(i);
        // 右孩子
        int r = right(i);
        // 最大结点下标
        int largest = -1;
        // i l r 找到最大值的下标,找到后进行交换
        if(l<=n && A[l] >A[i])
            largest = l;
        else
            largest = i;
        if(r<=n && A[r]>A[largest])
            largest = r;
        if(largest!=i){
            swap(A,largest,i); // 最大值下标和i处值进行交换
            heap(A,n,largest); // 交换后,largest 所在的子树也应该是大顶堆,下面一个递归的过程了
        }
    }

时间复杂度: O(log2(n))
循环算法

public void heap1(int[] A,int n,int i){
        // 左孩子
        int l = left(i);
        // 右孩子
        int r = right(i);
        // 最大结点下标
        int largest = -1;
        // i l r 找到最大值的下标,找到后进行交换
        if(l<=n && A[l] >A[i])
            largest = l;
        else
            largest = i;
        if(r<=n && A[r]>A[largest])
            largest = r;
        while(largest!=i){
            swap(A,largest,i);
            i = largest;
            l = left(i);
            r = right(i);
            if(l<=n && A[l] >A[i])
                largest = l;
            else
                largest = i;
            if(r<=n && A[r]>A[largest])
                largest = r;
        }
    }
public void swap(int[] A,int i,int j){
        int tmp= A[i];
        A[i] = A[j];
        A[j] = tmp;
    }

建堆

    /**
     * 建堆是自底向上的过程
     * @param A
     * @param n
     */
    public void buildMaxHeap(int[] A,int n){
        for(int i=n/2;i>=0;i--){
            heap1(A,n,i);
        }
    }

堆排序

    public void heapSort(int[] A){
        int n = A.length - 1;
        buildMaxHeap(A,n);// 建立大顶堆,0 位置是最大值
        for(int i =n;i>=1;i--){
            swap(A,i,0); // 0 位置是最大值,调整为升序,0位置元素和局部最大id位置进行交换
            heap1(A,i-1,0); // 交换后,可能不是大顶堆,进行调整  
        }
    }

主函数

    public static void main(String[] args) {
        int[] A = new int[]{49,38,65,97,76,13,27,49};
        Print.printArray(A);
        HeapSort heapSort = new HeapSort();
        heapSort.heapSort(A);
        Print.printArray(A);

    }

输出

49  38  65  97  76  13  27  49  
13  27  38  49  49  65  76  97  

复杂度分析

(1)时间复杂度
O(nlog2(n))
(2)空间复杂度
O(1)

参考
1.cnblogs

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值