一、题目描述

描述:

输入n个整数,输出其中最小的k个。

输入:

  1. 输入 n 和 k
  2. 输入一个整数数组

输出:

输出一个整数数组

样例输入:

5 2
1 3 5 7 2

样例输出:

1 2

二、Top K问题

对于 Top K 问题有很多种解法。

解法一:排序

相信很多人会首先想到这种方法,先把数组按升序/降序进行排序,然后输出 K 个最小/最大的数。

  • 常规的排序方法时间复杂度至少是Θ(nlog2n)。(快排或堆排序
  • 可能你会说,我们可以使用线性时间的排序算法。当然可以,但通常它们对输入的数组有一定的要求。比如计数排序要求 n 个数都是正整数,且它们的取值范围不太大。

解法二:部分排序 O(n∗k)

由于我们只需要找出最小/最大的 k 个数,所以我们可以进行部分排序,比如简单选择排序冒泡排序,它们每一趟都能把一个最小/最大元素放在最终位置上,所以进行 k 趟就能把 n 个数中的前 k 个排序出来。

部分简单选择排序:

void select_sort(int A[], int n, int k)
{
for(int i=0; i<k; ++i) { // k趟
int Min = i; // 记录最小元素的位置 for(int j=i+1; j<n; ++j)
if(A[j] < A[Min])
Min = j; if(Min != i) // 与A[i]交换
{
int tmp = A[Min];
A[Min] = A[i];
A[i] = tmp;
}
}
}

部分冒泡排序:

void bubble_sort(int A[], int n, int k)
{
for(int i=0; i<k; ++i) // k趟
{
bool flag = false;
for(int j=n-1; j>i; --j) // 一趟冒泡过程
if(A[j-1] > A[j])
{
int tmp = A[j-1];
A[j-1] = A[j];
A[j] = tmp;
flag = true;
}
if(flag == false) // 已经有序
return ;
}
}

那么,O(nlog2n) 与 O(n∗k) 哪一个更好呢?这取决于 k 的大小。在 k 较小的情况下,即 k<=log2n,可以选择部分排序。

解法三:快排划分 O(n∗log2k)

根据基于快排partition操作的《第k顺序统计量的求解》,我们知道,当我们求出第 k 顺序统计量时,位于它前面的元素都比它小,位于它后面的元素都比它大。这时,数组的前 k 个数就是最小的 k 个数。

int partition(int A[], int low, int high)
{
int pivot = A[low];
while(low < high)
{
while(low < high && A[high]>=pivot)
--high;
A[low] = A[high];
while(low < high && A[low]<=pivot)
++low;
A[high] = A[low];
}
A[low] = pivot;
return low;
} int topK(int A[], int low, int high, int k)
{
if(k <= 0)
return -1;
if(low == high)
return low; int pos = partition(A, low, high);
int i = pos - low + 1;
if(i == k)
return pos; // 返回前k个数的
else if(i > k)
return topK(A, low, pos, k);
else
return topK(A, pos+1, high, k-i);
}

我们说这个算法的平均时间复杂度是线性的,更准确地说,是 O(n∗log2k)。另外,为了避免特殊数据下的算法退化,最好使用随机化版本的划分操作。

解法四:大根堆 O(n∗log2k)

参见《堆排序》,可以用大小为 k 的大根堆来存储最小的 k 个数。大根堆的堆顶元素就是最小 k 个数中最大的一个。每次新考虑一个数 X:

  • 如果 X 比堆顶的元素 Y 大,则不需要改变原来的堆,因为这个元素比最小的 k 个数都大。

  • 如果 X 比堆顶元素 Y 小,那么用 X 替换堆顶的元素 Y。在 X 替换堆顶元素 Y 之后,大根堆的结构可能被破坏,需要进行向下调整。调整过程的时间复杂度是 O(log2k) 。

遍历完成以后,数组的前 k 个数就是最小的 k 个数,但是它们并非有序,而是以堆的形式存在。C++代码如下:

void AdjustDown(int A[], int i, int len)
{
int temp = A[i]; // 暂存A[i] for(int largest=2*i+1; largest<len; largest=2*largest+1)
{
if(largest!=len-1 && A[largest+1]>A[largest])
++largest; // 如果右子结点大
if(temp < A[largest])
{
A[i] = A[largest];
i = largest; // 记录交换后的位置
}
else
break;
}
A[i] = temp; // 被筛选结点的值放入最终位置
} /* 建堆 */
void BuildMaxHeap(int A[], int len)
{
for(int i=len/2-1; i>=0; --i) // 从i=n/2-1到0,反复调整堆
AdjustDown(A, i, len);
} /* 维护 A[0...k-1] 这个大根堆 */
void topK(int A[], int n, int k)
{
BuildMaxHeap(A, k); // 先用前面的k个数建大根堆
for(int i=k; i<n; ++i)
{
if(A[i] < A[0]) // 如果小于堆顶元素,替换之
{
int tmp = A[0];
A[0] = A[i];
A[i] = tmp;
AdjustDown(A, 0, k); // 向下调整
}
}
}

注意:找最小的 k 个数,就维护一个大根堆;找最大的 k 个数,就维护一个小根堆。

三、解题报告

第二部分已经讲解地很清楚了,几种解法都可以,只要注意输入输出的格式就行了。

个人站点:http://songlee24.github.com

华为OJ2051-最小的K个数(Top K问题)的更多相关文章

  1. 最大/最小de K个数/第K个数

    题目 在未排序的数组中找到第 k 个最大的元素.请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素. 思路 堆排序 收获 用优先队列实现最大最小堆 注意下列代码中优先队列 ...

  2. 海量数据处理之top K问题

    题目: CVTE笔试题https://www.1024do.com/?p=3949 搜索引擎会通过日志文件把用户每次检索使用的所有检索串都记录下来,每个查询串的长度为1-255字节. 假设目前有一千万 ...

  3. [IR] Ranking - top k

    PageRanking 通过: Input degree of link "Flow" model - 流量判断喜好度 传统的方式又是什么呢? Every term在某个doc中的 ...

  4. 华为OJ平台——输出最小的k个数

    输入n个整数,输出其中最小的k个. 详细描述: 接口说明 原型: bool GetMinK(unsignedint uiInputNum, int *pInputArray, unsignedint ...

  5. 算法题解:最大或最小的K个数(海量数据Top K问题)

    题目 输入 n 个整数,找出其中最小的 k 个数.例如输入4.5.1.6.2.7.3.8 这8个数字,则最小的4个数字是1.2.3.4. 初窥 这道题最简单的思路莫过于把输入的 n 个整数排序,排序之 ...

  6. 算法题解:最小的K个数(海量数据Top K问题)

    [本文版权归微信公众号"代码艺术"(ID:onblog)所有,若是转载请务必保留本段原创声明,违者必究.若是文章有不足之处,欢迎关注微信公众号私信与我进行交流!] 题目 输入 n ...

  7. 最小的k个数

    // 最小的k个数.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include <iostream> #include & ...

  8. (剑指Offer)面试题30:最小的k个数

    题目: 输入n个整数,找出其中最小的K个数.例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,. 思路: 1.排序 把输入的n个整数排序,然后取前k个数: 时间复杂度 ...

  9. 求给定数据中最小的K个数

    public class MinHeap { /* * * Top K个问题,求给定数据中最小的K个数 * * 最小堆解决:堆顶元素为堆中最大元素 * * * */ private int MAX_D ...

随机推荐

  1. (转)淘淘商城系列——SSM框架整合之Service层整合

    http://blog.csdn.net/yerenyuan_pku/article/details/72721108 上文我们一起学习了Dao层的整合,本文将教大家如何整合Service层. 我们需 ...

  2. 【BIEE】新建用户,并且赋予组BIconsumer,访问BIpublisher报表报错:检索数据xml时出错

    问题描述 今天新建一个用户用户查看报表,并且赋予该用户属于BIConsumer组,但是在访问报表的时候出现以下两个错: 1.xdo格式类的报表 2.RTF模板制作的报表 解决方案: 出现这个问题的原因 ...

  3. 2018 CCPC 桂林站(upc复现赛)总结

    比赛一开始盯上了A题和G题,一个小时过去了还没有出题,心里有些乱.这时我看D题很多人过了,于是宝儿去看D题,说D题简单,转化成二进制暴力,于是就去做了.写的时候好像思路有点卡,WA了一发,后来马上发现 ...

  4. [GXOI/GZOI2019]与或和(单调栈)

    想了想决定把这几题也随便水个解题报告... bzoj  luogu 思路: 首先肯定得拆成二进制30位啊 此后每一位的就是个01矩阵 Q1就是全是1的矩阵个数 Q2就是总矩阵个数减去全是0的矩阵个数 ...

  5. Java权限管理(授权与认证)

    CRM权限管理 有兴趣的同学也可以阅读我最近分享的:Shiro框架原理分析   (PS : 这篇博客里面介绍了使用Shiro框架的方式实现权限管理) https://www.cnblogs.com/y ...

  6. php - namespace篇

    之前没有系统学习过PHP语言,直接上手TP框架了,所以认为namespace和use是TP框架的一部分,最近学习语言模块的时候遇到了这个问题,所以汇总了一下. PHP中命名空间可以解决两类问题: 用户 ...

  7. Broadcasting

    目录 Broadcasting Key idea How to understand? Why broadcasting? Broadcastable? Broadcast VS Tile Broad ...

  8. Reparameterization Trick

    目录 Sample() is not differentiable Reparameterization trick Too Complex Sample() is not differentiabl ...

  9. Far Relative’s Problem (贪心 计算来的最多客人)

    Description Famil Door wants to celebrate his birthday with his friends from Far Far Away. He has n  ...

  10. hive 删除表内容

    TRUNCATE:truncate用于删除所有的行,这个行为在hive元存储删除数据是不可逆的delect:用于删除特定行条件,你可以从给定表中删除所有的行insert overwrite table ...