剑指Offer - 面试题40:最小的k个数

Posted 林夕07

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了剑指Offer - 面试题40:最小的k个数相关的知识,希望对你有一定的参考价值。

题目

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

分析

排序法

我们可以将这个整数数组先排序在取出前k个值就完成了题目要求。若题目要求不能修改原数组,这个时候我们还需要自己创建一个数组。这里只实现不能修改原数组的解法,排序函数用的是C库函数
C++

#include <iostream>
#include <cstdlib>
using namespace std;

int compar_int(const void* a, const void* b)
{
	/*
	如果a > b,返回>0
	如果a == b, 返回0
	如果a < b,返回<0
	*/
	return *(int*)a - *(int*)b;
}

void GetLeaseNumbers(int* n, int len,int* output, int k)
{
	qsort(n, len, sizeof(n[0]), compar_int);
	for (int i = 0; i < len && i < k; ++i)
	{
		output[i] = n[i];
	}
}

int main()
{
	int n[] = { 4,5,1,6,2,7,3,8 };
	int output[4];
	GetLeaseNumbers(n, 8, output, 4);

	cout << "最小k个数为:";
	for (int i = 0; i < 4; ++i)
	{
		cout << output[i] << " ";
	}
	cout << endl;
}

在这里插入图片描述
这个方法的复杂度取决于排序的算符复杂度,空间复杂度为O(1)

哈希表

我们用数组构建哈希表,时间复杂度会降到O(n),但是空间复杂度为O(n)。最主要的问题就是我们构建多大一个数组合适呢。可能还有负数等等。所以就这里稍微提一提。我用一个标志记录的最大值,因为我们构建一个很大的数组,不用全部遍历完。
C++

#include <iostream>
#include <cstdlib>
using namespace std;


void GetLeaseNumbers(int* n, int len,int* output, int k)
{	//假设没有负数
	int tmp[100000] = { 0 };
	int max = 0;
	for (int i = 0; i < len; ++i)
	{
		if (max < n[i])
			max = n[i];
		tmp[n[i]]++;

	}
	int j = 0;
	for (int i = 0; i < 100000 && i <= max && j < k; ++i)
	{
		while (tmp[i] > 0)
		{
			output[j++] = i;
			tmp[i]--;
		}
	}
}

在这里插入图片描述

替换法

我们可以先拿出部分数据放到最小数组中,然后让之后的数据一个个与最小数组的最大值相比谁小留谁。这个方法就很好的解决了上面方法的问题。问题就转换为如何从数组中找最大值,然后比较替换。我们很容易想到最大堆。可以自己构建一个最大堆算法也可以直接用STL容器multiset(允许又重复值)。
C++

#include <iostream>
#include <set>
using namespace std;


void GetLeaseNumbers(int* n, int len, int* output, int k)
{	
	multiset<int> set;
	int i = 0;
	for (i = 0; i < k; ++i)//初始化容器
	{
		set.insert(n[i]);
	}
	multiset<int>::const_iterator it = set.begin();
	//选拔环节
	for (i = k; i < len; ++i)
	{
		it = set.end(); it--;
		if (*it > n[i])
		{
			set.erase(*it - 1);
			set.insert(n[i]);
		}
	}
	it = set.begin();
	for (int i = 0; i < k; ++i)
	{
		output[i] = *it;
		it++;
	}
}

int main()
{
	int n[] = { 4,5,1,6,2,7,3,8 };
	int output[4] = { 0 };
	GetLeaseNumbers(n, 8, output, 4);

	cout << "最小k个数为:";
	for (int i = 0; i < 4; ++i)
	{
		cout << output[i] << " ";
	}
	cout << endl;
}

本章完!

以上是关于剑指Offer - 面试题40:最小的k个数的主要内容,如果未能解决你的问题,请参考以下文章

剑指Offer对答如流系列 - 最小的k个数

[剑指offer题解][Java]最小的k个数

剑指 Offer 45. 把数组排成最小的数 剑指 Offer 61. 扑克牌中的顺子 剑指 Offer 40. 最小的k个数

⭐算法入门⭐《堆》简单01 —— LeetCode 剑指 Offer 40. 最小的k个数

剑指offer--40最小的k个数

剑指offer--40最小的k个数