LeetCode刷题实况(一)快速选择算法-------最小的K个数/最小的第K个数(剑指offer40题)

题目链接:查找最小的K个数
本题,一般人的第一思路是用排序,然后取前k个,那么这个时间复杂度,就取决于排序算法的时间复杂度。最小的O(nlogn)。
虽然这个很好想,但在面试时只能做最初的方案(面试时很推荐最开始用这个算法,后面再改进,可以让面试官看到你思考的过程)。如果就是排序,没有后续的优化,那这道题答的就太失败了

接下来咱们就介绍两种优化的方式,具体用哪一个,看面试官怎么说
第一种:快速选择算法:时间复杂度O(N)

该算法脱胎自快速排序。与快排的区别在于,快排是对标志两边进行分治,而快选是对单侧进行分治。
算法思路:在数组中随机或者指定一个标志,将所有比这个标志的值小的,放到左边;比这个标志值大的放到右边。(这里的例子举的是最小的k个,最大的类似)
每一次这样排序进行完,标志的位置有三种情况:
1、这个位置就是k,那么直接返回就好。
2、这个位置小于K,那么说明,虽然左边的都比标志小但是数量不够,或者还没定到K的位置。说明K在右边,这里我们对位置右边进行递归。
3、这个位置大于K,那么说明,K就在左边,那么就对标志左边进行递归。

以上就是思路,但是还有一个点我们要去解决,就是怎么对数组进行排序。
这里我们就给出两种方法,具体用哪个,看读者理解哪种了
第一种:双指针(这里的指针,只是代表秩)

在这里插入图片描述
如上图,在区间[lo,hi)中,一个指针指lo,另一个指hi,标记的数在lo位置
hi先启动,向左搜索,如果发现某个值小于标记的数,就停下,开启第二阶段
lo启动,向有搜索,如果发现某个值大于标记的数,就与hi交换数据。以此循环直至hi指针 <= lo指针或者hi与lo到达另一边界。
这时,hi指针指的数一定是小于标记的数的,这时将该数,与hi所对应的数,交换。
至此,一次排序结束。
**这里要注意:**要先右指针先动然后左指针再动。
下面为代码:

public int partition(int[] arr, int lo, int hi, int k){
        int i = lo, j = hi;
        int Part = arr[lo];
        while(true){
            while(j > lo && arr[--j] >= Part);
            while(i < hi - 1 && arr[++i] <= Part);
            if(i >= j) break;  // 这里注意下在结束时不要再进行交换了
            swap(arr, i, j);
        }  // 这里便是排序
        swap(arr, lo, j);
        if(j > k)
            partition(arr, lo, j, k);
        else if(j < k)
            partition(arr, j + 1, hi, k);  // 这里为分治
        return j;
    }
第二种:快慢指针

在这里插入图片描述
将标记值放在6的位置,快慢指针都从1开始。
快指针 f 一直向右遍历整个数组,如果遇到有比标记值小的,就把这个值与慢指针的数进行交换,同时慢指针向前移动一位。
当遍历结束,将标记数与慢指针的数进行交换,此时,慢指针前的所有数都比标记值小,如此完成排序。

分治思想与上面相同

注:这里举的例子是最小的K个数;两种实现方式的目的相同,都是将标记值左边的值都小于它,右边的都大于它。
第二种:维持最大堆:时间复杂度O(NlogK)

算法逻辑:堆的总量为K。当堆不满时,将数据放到堆中,同时更新堆,要保持根的值是最大的。当堆满时,再来的数据,与根进行比较,如果小于根就将根的值移除,同时把输入的值放到堆中,并更新堆。如果大于,直接舍弃。
因为java中有这个堆的类,实现起来偏简单,这里就不深入展开了

超级重要的两种算法比较:

这个问题也经常被问到,这里就来解释下
1、因为快选会破坏原有的数组结构,而大根堆不对原数组进行操作。所以当面试官要求不破坏原数组的情况下,不用怀疑,直接大根堆。
2、数据量:因为快选是把所有的数据放到内存中,然后再进行操作。这样势必会带来一个问题,就是当数据量过大的时候,内存爆了,这个就很难受。而大根堆,就维持这么大个堆,其他的不用管。

综上所述:要根据是否要破坏原有数组和数据量综合考虑要选择哪种算法
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值