原地堆排序

12 篇文章 0 订阅
4 篇文章 0 订阅

大家可以看一下我上一篇文章使用的堆排序

堆排序
我之前使用的堆排序是要开 一个长度为 N的数组,事实上,我们可以把原数组看成一个堆,也就是说我们不用开一个数组,而是在原地排序
怎么说呢…
还是给我一个数组,我对这个数组进行Heapify 操作,这样就得到了一个大根堆(脑袋是最大的) ,然后我们把 最大的那个数 和数组的最后一个元素交换,这样一来,数组末尾最大那个数就不用考虑了

至此,堆排序的一个子过程已经完成,是不是觉得很迷
比如说冒泡排序吧
每一轮排序,我都会把最大的那个元素堆在数组的末尾,也就是说下一次排序我就不看数组末尾了,
因为已经排好序了

好,我们再回来看这个堆排序,也就是说我在Heapify后,把数组头和数组尾进行交换,那么这个大根堆的结构就被破坏了,但是,只是0号元素的结构被破坏了而已,该元素的左右子树还是大根堆
这个时候我们再对0 号元素进行shiftDown 操作,这样一来,0号小的下去
下面大的上来,就又构建了一个大根堆【 注意:上一次Heapify操作完了以后,我把最大的元素放在了 第 (arr.length -1)为,也就是说,在下一轮shiftDown后,我把第0位和 第(arr.length-2)位,也就是倒数第二位进行交换,这样,我们就把最大的数和次大的数放在了倒数第一位和倒数第二位

也就是说:倒数两位的数都被我排序好了,然后我们继续shiftDown ,再交换0,和 (len-3)位
继续shiftDown ,交换 0和(len-4)位

继续shiftDown,交换0 和2位元素,这样,整一个堆排序也就完成了
继续shiftDown ,然后交换0 和1 为元素,这样这个数组也就有序了
在这里插入图片描述
画图不易,望理解
每一次shiftDown 都在调整和维护一个大根堆并且这个堆的size是上一次堆的(size-1),每一次swap就相当于一次 poll,也就是优先队列的出队操作

下面是代码演示:

public class Main {

    static void swap(int [] arr,int l,int r)
    {
        int t = arr[l];
        arr[l]=arr[r];
        arr[r]=t;
    }


    public static void main(String[] args) {


        int[] arr={
                1,3,7,5,4,-1,999,58,5,8,2
        };
        HeapSort(arr);
        for (int a:
             arr) {
            System.out.println(a);

        }


    }


    public static void HeapSort(int[]arr) {
        int N = arr.length;

        for(int i=(N-1)/2;i>=0;i--)
        {
            shiftDown(arr,N,i);
        }
        for(int i=arr.length-1;i>0;i--)
        {
            swap(arr,0,i);
            shiftDown(arr,i,0);
        }



    }

    static void shiftDown(int[] arr,int heapSize,int i)
    {
        while (leftChild(i)<heapSize)
        {
            int j=leftChild(i);
            if(rightChild(i)<heapSize&&arr[rightChild(i)]>arr[j])
            {
                j+=1;//j=rightChild(i),选择左孩子还是选择右孩子,看哪个更强
            }
            if(arr[i]>=arr[j])
            {
                break;
            }
            swap(arr,i,j);
            i=j;
        }

    }



    static int parent(int i)
    {
        return (i-1)/2;
    }
    static int leftChild(int i)
    {
        return 2*i+1;
    }
    static int rightChild(int i)
    {
        return 2*i+2;
    }
    static int lastNodeForHeapify(int N)
    {
        return (N-1)/2;
    }







}

  /**
     * 堆排序工具类
     */
    private static class HeapSortHelper{
        private static void swap(int[] p,int l,int r)
        {
            if(p[l]==p[r]) return;
            p[l]^=p[r];
            p[r]^=p[l];
            p[l]^=p[r];
        }
        private static void heapify(int[] p,int cur,int heapSize)
        {

            int next = (cur<<1)+1;
            while (next<heapSize)
            {
                //默认选中左孩子
                if((next+1)<heapSize&&p[next+1]>p[next])
                {
                    ++next;//选择右孩子
                }

                if(p[cur]>=p[next])
                {
                    break;
                }
                swap(p,cur,next);
                cur = next;
                next = 1+(next<<1);
            }


        }
        private static void build(int[] p,int rightEnd)
        {
            if(p.length<=1) return;
            for(int i=((rightEnd>>1)-1);i>=0;--i)
            {
                heapify(p,i,rightEnd);
            }
        }


        private static void heapSort(int[] p)
        {
            if(p==null||p.length==0)return;

            build(p,p.length);

            for(int i=(p.length-1);i>=0;--i)
            {
                swap(p,0,i);
                heapify(p,0,i);
            }

        }
    }

 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值