面试题25:最小的K个数
方法一:对n个整数进行排序(快速排序或堆排序),取出前K个元素(最容易想到的最笨的方法,不可取)
时间复杂度:O(n*logn) + O(k) = O(n*logn)
采用快速排序的代码:
#include "stdafx.h"
#include <iostream>
using namespace std; //划分数组,找到枢轴元素下标,使得其左边的元素都比其小,右边的元素都比其大
int Partition(int nArr[], int nLength)
{
//初始状态下,选取数组的第一个元素作为枢轴元素
int nPivot = nArr[0]; //设置两个游标
int nLow = 0;
int nHigh = nLength - 1; while (nLow < nHigh)
{
while (nLow < nHigh && nArr[nHigh] >= nPivot)
{
nHigh--;
}
nArr[nLow] = nArr[nHigh]; while (nLow < nHigh && nArr[nLow] <= nPivot)
{
nLow++;
}
nArr[nHigh] = nArr[nLow];
} nArr[nLow] = nPivot;
return nLow;
} void FastSort(int nArr[], int nLength)
{
if (nArr == NULL || nLength <= 0)
{
return;
} int nMidIndex = Partition(nArr, nLength);
FastSort(nArr, nMidIndex);
FastSort(nArr + nMidIndex + 1, nLength - 1 - nMidIndex);
} int _tmain(int argc, _TCHAR* argv[])
{
int nArr1[8] = {4, 5, 1, 6, 2, 7, 3, 8};
int nArr2[8] = {4, 5, 2, 6, 2, 5, 3, 5};
int nArr3[8] = {1, 1, 1, 1, 1, 1, 1, 1}; while (1)
{
int k = 0;
cout << "请输入最小数的个数:";
cin >> k;
if (k == 0)
{
break;
} FastSort(nArr1, 8);
cout << "第一个数组的最小的" << k << "个数为:";
for (int i=0; i<k; i++)
{
cout << nArr1[i] << " ";
}
cout << endl; FastSort(nArr2, 8);
cout << "第二个数组的最小的" << k << "个数为:";
for (int i=0; i<k; i++)
{
cout << nArr2[i] << " ";
}
cout << endl; FastSort(nArr3, 8);
cout << "第三个数组的最小的" << k << "个数为:";
for (int i=0; i<k; i++)
{
cout << nArr3[i] << " ";
}
cout << endl;
} system("pause");
return 0;
}
运行结果:
采用堆排序的代码:
#include "stdafx.h"
#include <iostream>
using namespace std; //调整堆(大根堆)函数,待调整元素的下标nIndex,
void AdjustHeap(int nHeap[], int nLength, int nIndex)
{
int key = nHeap[nIndex];//待调整元素的值
for (int i=2*nIndex+1; i<nLength; i=2*i+1)
{
if (i+1 < nLength && (nHeap[i] < nHeap[i+1]))//有右孩子且右孩子的值比左孩子大
{
i++;//i指向表示较大孩子下标
}
if (nHeap[i] < key)//不需要调整
{
break;
}
nHeap[nIndex] = nHeap[i];
nIndex = i;
}
nHeap[nIndex] = key;
} //堆排序,从小到大
void HeapSort(int nArr[], int nLength)
{
if (nArr != NULL && nLength > 0)
{
for (int i=nLength/2-1; i>=0; i--)
{
AdjustHeap(nArr, nLength, i);//调整为大根堆
} for (int j=nLength-1; j>=0; j--)
{
int temp = nArr[j];
nArr[j] = nArr[0];
nArr[0] = temp;
AdjustHeap(nArr, j, 0);
}
}
else
{
return ;
}
} int _tmain(int argc, _TCHAR* argv[])
{
int nArr1[8] = {4, 5, 1, 6, 2, 7, 3, 8};
int nArr2[8] = {4, 5, 2, 6, 2, 5, 3, 5};
int nArr3[8] = {1, 1, 1, 1, 1, 1, 1, 1}; while (1)
{
int k = 0;
cout << "请输入最小数的个数:";
cin >> k;
if (k == 0)
{
break;
} HeapSort(nArr1, 8);
cout << "第一个数组的最小的" << k << "个数为:";
for (int i=0; i<k; i++)
{
cout << nArr1[i] << " ";
}
cout << endl; HeapSort(nArr2, 8);
cout << "第二个数组的最小的" << k << "个数为:";
for (int i=0; i<k; i++)
{
cout << nArr2[i] << " ";
}
cout << endl; HeapSort(nArr3, 8);
cout << "第三个数组的最小的" << k << "个数为:";
for (int i=0; i<k; i++)
{
cout << nArr3[i] << " ";
}
cout << endl;
} system("pause");
return 0;
}
运行结果:
方法二:使用选择排序或冒泡排序,进行K次选择,可得到最小的k个数
时间复杂度:O(n*k)
选择排序代码:
#include "stdafx.h"
#include <iostream>
using namespace std; //利用选择排序找到最小的k个数,进行k次选择即可
void SelectSort_KMinNum(int nArr[], int nLength, int k)
{
if (nArr == NULL || nLength <= 0 || k>nLength)
{
cout << "输入有误!" << endl;
return;
} int nMin = 0;
int nMinIndex = 0;
for (int i=0; i<k; i++)//进行k次选择
{
nMin = nArr[i];
nMinIndex = i;
for (int j=i+1; j<nLength; j++)
{
if (nArr[j] < nMin)
{
nMin = nArr[j];
nMinIndex = j;
}
} if (i != nMinIndex)
{
int temp = nArr[i];
nArr[i] = nArr[nMinIndex];
nArr[nMinIndex] = temp;
} cout << nArr[i] << " ";
}
cout << endl; } int _tmain(int argc, _TCHAR* argv[])
{
int nArr1[8] = {4, 5, 1, 6, 2, 7, 3, 8};
int nArr2[8] = {4, 5, 2, 6, 2, 5, 3, 5};
int nArr3[8] = {1, 1, 1, 1, 1, 1, 1, 1}; while (1)
{
int k = 0;
cout << "请输入最小数的个数:";
cin >> k;
if (k == 0)
{
break;
} cout << "第一个数组的最小的" << k << "个数为:";
SelectSort_KMinNum(nArr1, 8, k); cout << "第二个数组的最小的" << k << "个数为:";
SelectSort_KMinNum(nArr2, 8, k); cout << "第三个数组的最小的" << k << "个数为:";
SelectSort_KMinNum(nArr3, 8, k);
}
system("pause");
return 0;
}
运行结果:
冒泡排序代码:
#include "stdafx.h"
#include <iostream>
using namespace std; //利用冒泡排序找到最小的k个数,进行k次选择即可
void BubbleSort_KMinNum(int nArr[], int nLength, int k)
{
if (nArr == NULL || nLength <= 0 || k>nLength)
{
cout << "输入有误!" << endl;
return;
} for (int i=1; i<=k; i++)//进行k次冒泡过程
{
for (int j=0; j<nLength-i; j++)
{
if (nArr[j] < nArr[j+1])
{
int temp = nArr[j];
nArr[j] = nArr[j+1];
nArr[j+1] = temp;
}
} cout << nArr[nLength-i] << " ";
}
cout << endl;
} int _tmain(int argc, _TCHAR* argv[])
{
int nArr1[8] = {4, 5, 1, 6, 2, 7, 3, 8};
int nArr2[8] = {4, 5, 2, 6, 2, 5, 3, 5};
int nArr3[8] = {1, 1, 1, 1, 1, 1, 1, 1}; while (1)
{
int k = 0;
cout << "请输入最小数的个数:";
cin >> k;
if (k == 0)
{
break;
} cout << "第一个数组的最小的" << k << "个数为:";
BubbleSort_KMinNum(nArr1, 8, k); cout << "第二个数组的最小的" << k << "个数为:";
BubbleSort_KMinNum(nArr2, 8, k); cout << "第三个数组的最小的" << k << "个数为:";
BubbleSort_KMinNum(nArr3, 8, k);
}
system("pause");
return 0;
}
运行结果:
、计数排序 + 数组实现
适合数据量小的数据,不提倡使用
使用计数排序,另开辟一个数组,记录每个整数出现的次数,然后再从大到小取最大的 K 个。
代码:
#include "stdafx.h"
#include <iostream>
using namespace std; //利用计数+数组找到最小的k个数
void CountArr_KMinNum(int nArr[], int nLength, int k)
{
if (nArr == NULL || nLength <= 0 || k>nLength)
{
cout << "输入有误!" << endl;
return;
} int nMax = nArr[0];
for (int i=1; i<nLength; i++)
{
if (nMax < nArr[i])
{
nMax = nArr[i];
}
} int *pArr = new int[nMax+1];//开辟一个数组
memset(pArr, 0, (nMax+1)*sizeof(int)); for (int j=0; j<nLength; j++)
{
pArr[nArr[j]]++;
} int nCount = 0;
for (int z=0; z<nMax+1; z++)
{
if (pArr[z]>0)
{
while ((pArr[z]--)>0)
{
if (nCount < k)
{
cout << z << " ";
nCount++;
}
else
{
break;
}
}
}
} delete []pArr;
pArr = NULL;
cout << endl;
} int _tmain(int argc, _TCHAR* argv[])
{
int nArr1[8] = {4, 5, 1, 6, 2, 7, 3, 8};
int nArr2[8] = {4, 5, 2, 6, 2, 5, 3, 5};
int nArr3[8] = {1, 1, 1, 1, 1, 1, 1, 1}; while (1)
{
int k = 0;
cout << "请输入最小数的个数:";
cin >> k;
if (k == 0)
{
break;
} cout << "第一个数组的最小的" << k << "个数为:";
CountArr_KMinNum(nArr1, 8, k); cout << "第二个数组的最小的" << k << "个数为:";
CountArr_KMinNum(nArr2, 8, k); cout << "第三个数组的最小的" << k << "个数为:";
CountArr_KMinNum(nArr3, 8, k);
}
system("pause");
return 0;
}
运行结果:
方法四、利用STL中的map保存每个数出现的次数,找到K个数
时间复杂度O(n*logn) 空间复杂度O(n)
注意:1、不能使用CMap实现,因为Cmap不能根据key自动为其排序;2、map内部是由红黑树实现的,每次插入都是logn,总的复杂度为n*logn。
代码:
#include "stdafx.h"
#include <iostream>
#include <map>
using namespace std; //利用map计数找到最小的k个数
void MapCount_KMinNum(int nArr[], int nLength, int k)
{
if (nArr == NULL || nLength <= 0 || k>nLength)
{
cout << "输入有误!" << endl;
return;
} map<int,int> countMap; for (int j=0; j<nLength; j++)
{
if (countMap[nArr[j]]==0)
{
countMap[nArr[j]] = 1;
}
else
{
countMap[nArr[j]]++;
}
} int nCount = 0;
for (map<int, int>::iterator itr=countMap.begin(); itr!=countMap.end(); itr++)
{
if (itr->second > 0)
{
while ((itr->second--) > 0)
{
if (nCount < k)
{
cout << itr->first << " ";
nCount++;
}
else
{
break;
}
}
}
}
cout << endl;
} int _tmain(int argc, _TCHAR* argv[])
{
int nArr1[8] = {4, 5, 1, 6, 2, 7, 3, 8};
int nArr2[8] = {4, 5, 2, 6, 2, 5, 3, 5};
int nArr3[8] = {1, 1, 1, 1, 1, 1, 1, 1}; while (1)
{
int k = 0;
cout << "请输入最小数的个数:";
cin >> k;
if (k == 0)
{
break;
} cout << "第一个数组的最小的" << k << "个数为:";
MapCount_KMinNum(nArr1, 8, k); cout << "第二个数组的最小的" << k << "个数为:";
MapCount_KMinNum(nArr2, 8, k); cout << "第三个数组的最小的" << k << "个数为:";
MapCount_KMinNum(nArr3, 8, k);
}
system("pause");
return 0;
}
运行结果:
方法五、维护一个大小为k的大根堆,初始化为数组中前k个元素,调整为大根堆。对于数组中的剩下的数,判断与堆顶的大小。如果比堆顶大,则不需要改变原来的堆;如果比堆顶小,要将其替换堆顶的元素,调整为大根堆。
时间复杂度: O (N * log2 K ),算法只需扫描所有的数一次,调整堆的时间复杂度为O(log2K)
空间复杂度:O(k),需一个大小为k 的堆。
代码:
#include "stdafx.h"
#include <iostream>
using namespace std; //调整堆(大根堆)函数,待调整元素的下标nIndex,
void AdjustHeap(int nHeap[], int nLength, int nIndex)
{
int key = nHeap[nIndex];//待调整元素的值
for (int i=2*nIndex+1; i<nLength; i=2*i+1)
{
if (i+1 < nLength && (nHeap[i] < nHeap[i+1]))//有右孩子且右孩子的值比左孩子大
{
i++;//i指向表示较大孩子下标
}
if (nHeap[i] < key)//不需要调整
{
break;
}
nHeap[nIndex] = nHeap[i];
nIndex = i;
}
nHeap[nIndex] = key;
} //找到数组nArr中最小的k个数
int* MinKNum(int nArr[], int nLength, int k)
{
if (nArr != NULL && nLength > 0 && k <= nLength)
{
//维护一个大小为k的大根堆,初始化为数组的前k个元素
int *nHeap = new int[k];
for (int i=0; i<k; i++)
{
nHeap[i] = nArr[i];
}
for (int t=k/2-1; t>=0; t--)
{
AdjustHeap(nHeap, k, t);//调整为大根堆
} for (int j=k; j<nLength; j++)
{
if (nArr[j] < nHeap[0])//剩下的元素依次与堆顶元素进行比较,若比其小则替换堆顶元素
{
nHeap[0] = nArr[j];
AdjustHeap(nHeap, k, 0);
}
}
return nHeap;
}
else
{
return NULL;
}
} int _tmain(int argc, _TCHAR* argv[])
{
int nArr[8] = {4, 5, 1, 6, 2, 7, 3, 8};
//int nArr[8] = {1, 2, 3, 4, 5, 6, 7, 8};
//int nArr[8] = {2, 2, 2, 2, 2, 2, 2, 2};
int k = 0;
while (1)
{
cout << "请输入最小数的个数:";
cin >> k;
if (k == 0)//输入0表示程序结束
{
break;
}
int *p_nHeap = MinKNum(nArr, 8, k);
cout << "最小的" << k << "个数为:";
for (int i=0; i<k; i++)
{
cout << p_nHeap[i] << " ";
}
cout << endl; delete [] p_nHeap;
p_nHeap = NULL;
} system("pause");
return 0;
}
运行结果:
面试题25:最小的K个数的更多相关文章
- 剑指Offer:面试题30——最小的k个数(java实现)
问题描述: 输入n个整数,找出其中最小的k个数 思路1: 先排序,再取前k个 时间复杂度O(nlogn) 下面给出快排序的代码(基于下面Partition函数的方法) public void Quic ...
- 面试题30.最小的k个数
题目:输入n个整数,找出其中最小的k个数,例如输入4,5,1,6,2,7,3,8 这8个数字,则最小的四个数字为1,2,3,4, 这道题是典型的TopK问题,剑指Offer提供了两种方法来实现,一种方 ...
- 剑指offer 面试题40. 最小的k个数
O(N)划分法,注意这个方法会改变原数据(函数参数是引用的情况下)!当然也可以再定义一个新容器对其划分 要求前k小的数,只要执行快排划分,每次划分都会把数据分成大小两拨.直到某一次划分的中心点正好在k ...
- leetcode 签到 面试题40. 最小的k个数
题目 输入整数数组 arr ,找出其中最小的 k 个数.例如,输入4.5.1.6.2.7.3.8这8个数字,则最小的4个数字是1.2.3.4. 示例 1: 输入:arr = [3,2,1], k = ...
- 《剑指offer》面试题40. 最小的k个数
问题描述 输入整数数组 arr ,找出其中最小的 k 个数.例如,输入4.5.1.6.2.7.3.8这8个数字,则最小的4个数字是1.2.3.4. 示例 1: 输入:arr = [3,2,1], k ...
- 【面试题030】最小的k个数
[面试题030]最小的k个数 题目: 输入n个整数,找出其中最小的k个数. 例如输入4.5.1.6.2.7.3.8这8个字,则其中最小的4个数字是1.2.3.4. 思路一: ...
- 【剑指Offer面试题】 九度OJ1371:最小的K个数
题目链接地址: http://ac.jobdu.com/problem.php?pid=1371 题目1371:最小的K个数 时间限制:1 秒内存限制:32 兆特殊判题:否提交:5938解决:1265 ...
- 面试题40:最小的 k 个数
import java.util.Arrays; /** * Created by clearbug on 2018/2/26. * * 面试题40:最小的 k 个数 * * 注意:因为前两天在陌陌面 ...
- 剑指Offer面试题:27.最小的k个数
一.题目:最小的k个数 题目:输入n个整数,找出其中最小的k个数.例如输入4.5.1.6.2.7.3.8这8个数字,则最小的4个数字是1.2.3.4. 这道题是典型的TopK问题,其最简单的思路莫过于 ...
随机推荐
- Jar
http://docs.oracle.com/javase/6/docs/technotes/guides/jar/jar.html#Intro http://docs.oracle.com/java ...
- wafer
Wafer Dicing:晶圆划片 wafer:晶圆 Wafer bumping:晶圆凸起 300mm wafer line:300mm晶圆线 wafer fabrication:晶圆加工 silic ...
- haproxy path_end不能忽略
C:\>ping www.zjtest7.com 正在 Ping www.zjtest7.com [192.168.32.82] 具有 32 字节的数据: 来自 192.168.32.82 的回 ...
- 10年程序员谈.Net程序员的职业规划(图/文)
原文地址:http://bbs.csdn.net/topics/390736769 从事Dotnet程序开发工作近10年了,从开始的月薪3k的小程序员菜鸟,到现在年薪60w的项目总经理,从战战兢兢的去 ...
- 网易云课堂_C++程序设计入门(上)_第1单元:C++概览_第1单元作业 - 写代码 - 互评 (难度:易)
第1单元作业 - 写代码 - 互评 (难度:易) 查看帮助 返回 提交作业(截止时间已过) 完成并提交作业 作业批改 互评训练 互评作业 自评作业 成绩公布 查看成绩 温 ...
- Longest Substring Without Repeating Characters 最长不重复子串
只遍历一次字符串即可求出最长不重复子串的长度. int lengthOfLongestSubstring(string s) { vector<,-); //记录字符上一次出现的位置,ASCII ...
- VS2010中更改项目名称(转载)
一.修改解决方案的名称:直接选择解决方案,右键重命名即可.
- 搭建PhoneCat项目的开发与测试环境
AngularJS官方网站提供了一个用于学习的示例项目:PhoneCat.这是一个Web应用,用户可以浏览一些Android手机,了解它们的详细信息,并进行搜索和排序操作. 获取源代码 PhoneCa ...
- LeetCode Day2
Power of Two /** * LeetCode: Power of Two * Given an integer, write a function to determine if it is ...
- C#操作项目配置文件
前言 对于项目配置文件的读取和修改,.net 提供了ConfigurationManager(位于System.Configuration命名空间) 和WebConfigurationManager( ...