题目:
输入n个整数,找出其中最小的K个数。
边界条件及异常:
n小于k,是否会有相同的数字
思路:
方法一:
先进行排序,然后输出最小的K个数
注意:会改变原来的数组
时间复杂度:O(nlgn)
空间复杂度:O(1)
方法二:
用额外的K个空间来存最小的K个数,再往后如果有小于这K个数的,按顺序插进来,并将较大的移出去。
用带排序的hash表(map和set带自动排序功能)来存储,插入操作时间复杂度O(lgK)
时间复杂度:O(nlgK)
空间复杂度:O(K)
#include <iostream>
#include <vector>
#include <queue>
#include <string>
#include <stack>
#include <algorithm>
#include <hash_set> //for hashtable
#include <hash_map>
#include <set>
using namespace std;
void getMinKNums(vector<int> nums, int k, set<int> &result)
{
int size = nums.size();
if (size < k) return;
for (int i = 0; i < size; ++i)
{
if (i < k) result.insert(nums[i]);
else
{
set<int>::iterator it = result.end();
--it;
if (nums[i] < *it)
{
result.erase(it);
result.insert(nums[i]);
}
}
}
}
int main()
{
int arr[] = { 6, 7, 9, 8, 5, 4, 1, 2, 3 };
vector<int> nums(arr, arr + 9);
set<int> result;
getMinKNums(nums, 4, result);
set<int>::iterator it = result.begin();
for (; it != result.end(); ++it)
cout << *it << " ";
cout << endl;
return 0;
}
方法三:利用partition函数的思想,将原数组以第K个数为枢纽,小于该枢纽的值位于数组左边,大于该枢纽的值位于右边,最后再对前K个数进行排序即可。
时间复杂度:O(n)
#include <iostream>
#include <vector>
#include <queue>
#include <string>
#include <stack>
#include <algorithm>
#include <hash_set> //for hashtable
#include <hash_map>
#include <set>
#include <ctime>
using namespace std;
int RandomInRange(int start, int end)
{
srand(time(0));
return (rand() % (end-start) + start);
}
int Partition(vector<int> &data, int start, int end)
{
int length = data.size();
if (length <= 0 || start < 0 || end >= length) throw new std::exception("Invalid Parameters");
int index = RandomInRange(start, end);
swap(data[index], data[start]); //将随机中枢交换的第一个
int pivotkey = data[start]; //取第一个为中枢,且将中枢值存下来
while (start < end)
{
while (start < end && data[end] >= pivotkey) --end;
data[start] = data[end];
while (start < end && data[start] <= pivotkey) ++start;
data[end] = data[start];
}
data[start] = pivotkey;
return start;
}
void getMinKNums(vector<int> nums, int k, vector<int> &result)
{
int size = nums.size();
if (size == 0 || size < k || k <= 0) return;
int end = size - 1;
int start = 0;
int index = Partition(nums, start, end);
while (index != k - 1)
{
if (index > k - 1)
{
end = index - 1;
index = Partition(nums, start, end);
}
else
{
start = index + 1;
index = Partition(nums, start, end);
}
}
for (int i = 0; i < k; ++i)
result.push_back(nums[i]);
}
int main()
{
int arr[] = { 6, 7, 9, 8, 5, 4, 1, 2, 3 };
vector<int> nums(arr, arr + 9);
vector<int> result;
getMinKNums(nums, 4, result);
vector<int>::iterator it = result.begin();
for (; it != result.end(); ++it)
cout << *it << " ";
cout << endl;
return 0;
}
注意:方法二除了有不改变原来数据外,还有适合海量数据的输入。假设题目要求重海量数据中找出最小的K个数字,由于内存大小的限制,不可能把海量数据一次性全部载入内存。这时候可以借助辅助存储空间中每次读入一个数字。