将一个几乎有序的数组,排序好,几乎就是:要挪动i位置,最远不会挪动k个位置

将一个几乎有序的数组arr,排序好,几乎就是:要挪动i位置,最远不会挪动k个位置

提示:这是一个经典的使用堆排序的案例
本题的基础知识:
(1)堆,系统堆结构,手动改写堆结构,堆结构远比比堆排序更重要


题目

将一个几乎有序的数组arr,排序好,几乎就是:要挪动i位置,最远不会挪动k个位置(k<<远小于N)


一、审题

示例:6 1 2 3 4 5 7 8
0 1 2 3 4 5 6 7
不妨设k=5;也就是说,任意位置i的数,想排序挪动后前面后面的话,最多移动k次
比如本例,0位置的6,想移动冒泡到5后面,不断跟1 2 3 4 5交换,最大交换次数是k=5;
这就是几乎排序好的数组。


二、解题

此时如果我们说给你的缓存就很少,加之,你不能用系统排序函数来排,因为复杂度会高达o(n*log(n))
所以需要我们另做设计

(1)既然是最大移动k次,那我们想,要是将arr前面k+1个都放入小根堆heap,
岂不是就自然用log(k)的复杂度排序好了,k很小,自然这log(k)都可以忽略不计。
而且,我们不用把整个N全部都排序排一遍

(2)然后,我们怎么做呢,把heap弹出1个,让arr的0位置开始收集
(3)每次弹出1个,立马从arr中没进过堆的最左边那个位置放一个进堆,保证排序可能会移动的位置最大不会超过k
(4)不断循环整个过程,最终所有arr都进过堆,把堆全部弹出放入arr,完成排序!!!
在这里插入图片描述
(1)最开始k+1个,6个数,全放入heap中,第一次6做堆顶,依次来了1 2 3 4 5,然后6下沉了,1坐了堆顶,得到了绿色那个树
(2)弹出1,到arr0位置,将7放入小根堆heap,得到了蓝色那个树
(3)弹出2,到arr1位置,将8放入小根堆heap,得到了橘色那个树
(4)弹出3,到arr2位置,弹出4,去3位置,弹出5去4位置,弹出6去5位置,弹出7去6位置,弹出8去7位置,完成arr
arr几乎有序了,通过这种方式,每次加一个数,排序log(k)
咱们加N个数,所以用了o(n*log(k))<<o(nlog(n)) 【系统排序算法复杂度】
而且,既然只用了有限个额外空间,忽略不计,空间复杂度当o(1)

明白了吧,
手撕一下:

//复习:将一个几乎有序的数组arr,排序好,几乎就是:要挪动i位置,最远不会挪动k个位置(k<<远小于N)
    public static void sortAlmostSortedArray(int[] arr, int k){
        if (arr == null || arr.length < 2) return;

        int N = arr.length;
        //题目说了k<<N所以不用考虑N太短的事情
        //当然了,左神也考虑过,如果k比N还大
        //那堆就提前结束放数

        PriorityQueue<Integer> heap = new PriorityQueue();
        //从arr的index=0开始放入,这个index用到最后
        int index = 0;
        for(;index < Math.min(k, N); index++){
            //考虑k可能大于N的话,--这不可能
            heap.add(arr[index]);
        }

        //堆放了k个
        //循环弹出,arr加入
        int i = 0;//弹出的数,放哪里呢?放i=0开始,覆盖arr原有的数
        for(; index < N; index++, i++){
            //每一次index随着增加,最后到N停止,与此同时,每次i++
            arr[i] = heap.poll();
            heap.add(arr[index]);
        }
        //直到arr加完了,剩下的只余下弹了
        while (!heap.isEmpty()){
            arr[i++] = heap.poll();
        }
        //此时arr有序了
    }

验证一下:

//对数器之构建随机数组
    public static int[] createArray(int arrSize, int maxValue){
        int[] arr = new int[arrSize];
        for (int i = 0; i < arrSize; i++) {
            arr[i] = (int)(maxValue * Math.random());//0-N-1的随机数
        }
        return arr;
    }

    public static void checker(){
        //生成检验数组
        int[] arr = {6,1,2,3,4,5,7,9,8,10,11,12};
        //这个数组几乎就是排序好的,k==5,最多不会移动超过5个
        int[] arr2 = new int[arr.length];//赋值同样一个数组arr
        for (int i = 0; i < arr.length; i++) {
            arr2[i] = arr[i];//copy即可
        }
        int[] arr3 = new int[arr.length];//赋值同样一个数组arr
        for (int i = 0; i < arr.length; i++) {
            arr3[i] = arr[i];//copy即可
        }

        //绝对的正确方法——暴力方法,或系统函数,操作arr
        Arrays.sort(arr);
        //优化方法,操作arr2
        sortedAlmostArrLessK(arr2, 5);
        //复习优化方法,操作arr3
        sortAlmostSortedArray(arr3, 5);
        //然后两个数组对位校验
        boolean isTrue = true;
        for (int i = 0; i < arr.length; i++) {
            if(arr[i] != arr2[i]) isTrue = false;
        }
        System.out.println(isTrue == false ? "oops,wrong!" : "right!");
        isTrue = true;
        for (int i = 0; i < arr.length; i++) {
            if(arr[i] != arr3[i]) isTrue = false;
        }
        System.out.println(isTrue == false ? "oops,wrong!" : "right!");
    }


    public static void main(String[] args) {
        checker();
//        test();
    }

没问题:

right!
right!

总结

提示:重要经验:

1)几乎有序:就要考虑到可能会用堆来搞定,未来我们会遇到当资源空间受限时,你也要用堆解决问题
2)堆结构远比堆排序重要

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

冰露可乐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值