面试题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问题,其最简单的思路莫过于 ...
随机推荐
- 从sample来学习Java堆(转)
1)Java堆 所有对象的实例分配都在Java堆上分配内存,堆大小由-Xmx和-Xms来调节,sample如下所示: public class HeapOOM { static class OOMOb ...
- java设计模式--行为型模式--命令模式
命令模式 概述 将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化:对请求排队或记录请求日志,以及支持可撤消的操作. 适用性 .抽象出待执行的动作以参数化某对象. .在不同的时刻指定.排 ...
- 【具体数学 读书笔记】1.2 Lines in the Plane
本节介绍平面划分问题,即n条直线最多把一个平面划分为几个区域(region). 问题描述: "What is the maximum number Ln of regions defined ...
- Wikioi 1294 全排列
先给出链接地址:Wikioi 1294 虽然题目很短,论难度也就是个深搜,算法方面我就不多说了,而且我知道c++有个函数叫next_permutation,谁用谁知道. 代码如下: #include& ...
- UML_行为图
活动图是UML用于对系统的动态行为建模的另一种常用工具,它描述活动的顺序,展现从一个活动到另一个活动的控制流.活动图在本质上是一种流程图.活动图着重表现从一个活动到另一个活动的控制流,是内部处理驱动的 ...
- flex容器解析
通常在Flex种有两种形式的容器:布局和导航. 在容器中我们可以同时设置一些空间和子容器,我们可以叫在容器内定义的任何组件为该容器的孩子. 在一个Flex程序的根部是一个叫做Application C ...
- C++匈牙利命名法
匈牙利命名法 匈牙利命名法是一种编程时的命名规范.基本原则是:变量名=属性+类型+对象描述,其中每一对象的名称都要求有明确含义,可以取对象名字全称或名字的一部分.命名要基于容易记忆容易理解的原则.保证 ...
- MySQL的零碎知识点
让Windows下的MySQL表名大小写敏感: 在mysql查询中执行:SHOW VARIABLES LIKE 'lower_case_table_names'; 值的含义--->0:大小写敏感 ...
- Spring Http Invoker
配置例如以下: ①web.xml配置 <servlet> <servlet-name>remote</servlet-name> <servlet-class ...
- POJ 3311 Hie with the Pie(状压DP + Floyd)
题目链接:http://poj.org/problem?id=3311 Description The Pizazz Pizzeria prides itself in delivering pizz ...