剑指offer:最小的k个数

题目描述

输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,。

本题最直接快速的解法就是对输入的数组进行排序,然后取它的前k个数即为所求,这种操作的时间复杂度是o(nlogn).

思路一、基于partition函数实现(时间复杂度o(n)

本题和上一个一样,https://blog.csdn.net/orangefly0214/article/details/84817266,都可以用快速排序中的partition函数,基于数组中的第k个数来做调整,使得数组中比第k个数小的数都位于它的左边,比第k个数大的数都位于它的右边,调整之后位于最前面的k个数就是所求。(因为是快速排序,这k个数只是比枢轴小,但并不是完全排好序的。这种方法是在面试官允许修改输入数组的情况下才可以采用的)

实现1:

未改进的partition函数,需要使用swap进行交换:

   

思路二:适合处理海量数据的o(nlogk)解法

step1:创建一个大小为k的数据容器来存储最小的k个数,接下来,每次从输入的n个整数中读入一个数. 

step2:如果容器已有的数小于k,则直接把这次读入的整数放入容器中
step3:如果容器中已经有k个数了,容器已满。此时不能再插入新的数字,而需要替换已有的数。
①找到已有的k个数的最大值,将这次待插入的整数和最大值进行比较。

如果待插入的数比当前已有的最大值小,则用这个数替换当前已有数的最大值;

如果待插入的数比当前已有数的最大值还要大,则这个数肯定不是最小的k个数之一,则抛弃它。
所以,当容器满了之后,需要做三件事,
(1)在k个整数中找到最大值;
(2)在这个容器中删除最大数。
(3)插入一个新的数
用二叉树来实现这个步骤,可以在o(logk)时间内实现这3个操作。对输入的n个数字而言,总的时间效率是o(nlogk)。

二叉树:TreeSet里面绝大部分方法都市直接调用TreeMap方法来实现的。TreeMap和TreeSet都是有序的集合,也就是说他们存储的值都是排好序的。

实现1:

该方法使用了额外的空间,而且运行的速度也比较慢,但是有两个优点。

首先,没有修改输入的数据,对数据的操作都是在额外创建的容器中进行修改的。

其次,该方法也很适合处理海量数据,海量数据因为数据量过大而没办法一次性加载到内存中来,因此用这个额外的容器来辅助读入数据,取出符合条件的数。

 

实现2:利用另外一种数据结构最大堆来实现,和前面利用treeset的思路是一模一样的,只是换了一种容器来存储

 

 

实现3:不利用java中的数据结构,自行构建最大堆

这种实现的前提是我们必须熟悉掌握堆排序,

堆排序:利用大顶堆进行排序,基本思想是:将待排序的序列构造成一个大顶堆。此时整个序列的最大值就是堆顶的根节点。将他移走(也就是将它和堆数组末尾的元素进行交换,此时末尾元素就是最大值),然后将剩余的n-1个元素重新构造成大顶堆,这样就会得到n个元素中的次小值,如此反复,便能得到一个有序序列。

实现堆排序,需要解决的两个问题:

①如何由一个无序序列构建成一个堆;

②如何在输出堆顶元素后,调整剩余元素成为新的堆。

 

本题中基于自行构建大顶堆的实现:

   

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值