寻找最大的K个数(下)
接着昨天的写,里面的代码包含昨天的
#include <iostream>
using namespace std;
#define N 50 //初始化数组
int a[] = {, , , , , , , , , };
//int a[] = {10, 9, 8, 7, 6, 5, 4, 3, 2, 1};
//int a[] = {1, 2, 3, 4, 5, 6};
//int a[] = {6, 2, 3, 9, 4, 10, 1, 20, 40, 5};
int n = ;
int K = ; //快速排序,o(nlogn),最应该想到的思路,排好序要多大数就输出多大数
/*
partition就是挖第一个洞,从后往前找,找到,挖起来,把前面的洞埋上,再从前往后找,找到,挖起来,把后面的洞埋上,直到最后,high=low了,把这个洞补上
*/
int partition(int* p, int low, int high)
{
int i;
int pivot;
//把第一个数拿出来,挖个洞
pivot = p[low];
while (low < high)
{
//从后往前找,找到比pivot小的值
while (low < high && p[high] <= pivot)
high--;
//然后后面的数埋上前面的洞
//Note这里无须再加个if,因为即使相同了,那我再做一步也无妨,而且也无须把low指针往上移,因为,到时候我可以再判断一次,还是可以移动的
p[low] = p[high]; //从前往后找,找到比pivot大的值,然后把前面的数埋上
while (low < high && p[low] >= pivot)
low++;
p[high] = p[low];
}
//这里low和high已经相同了,所以也可以写成p[high]=pivot,这一步就是把洞埋上
p[low] = pivot;
return low;
}
/*
其实,两个可以写一起,但是,分开写更清楚
quickSort函数就是当low<high时,进行一次partition,然后再对分开的两块进行quickSort
*/
void quickSort(int* p, int low, int high)
{
if(low < high)
{
int breakpoint = partition(p, low, high);
quickSort(p, low, breakpoint - );
quickSort(p, breakpoint + , high);
}
} //堆排序, o(nlogk),考虑到只需取K大的数,那就无须对n个数都排序,只需记录下k个即可
int heap[N];
/*
//这里有点疑问哦,考虑到heap数组可能比较大,所以想定义成全局变量,可是这样就不必传递参数勒,定义成局部变量,参数又太多
目前定义成全局变量
input: lastIndex指heap数组要插入的value的位置(是要插入的位置哦); value指要插入的数字
function: heap数组是从index=0开始储存的,就是把value储存heap数组内,并进行相应的调整,符合最大堆的性质
*/
void MaxHeapPush(int lastIndex, int value)
{
//把value放在堆的末尾
heap[lastIndex] = value;
//记录下末尾的index
int index = lastIndex;
// 不断向上调整
while (index)
{
//若比上面的大,就交换
if (heap[index] > heap[(index - ) / ])
{
int temp = heap[index];
heap[index] = heap[(index - ) / ];
heap[(index - ) / ] = temp;
}
//否则,说明已经调整好了,立即停止
else
break;
//若没有break出来,就要一直调整了,所以index要变动
index = (index - ) / ;
}
}
/*
input:
p数组要初始化数组,提供数据的
n表示该数组的长度,c就是麻烦,连长度都要传入
heapSize表示要维护的堆的大小,Note,一定要大于K哦
*/
void MaxHeapInit(int *p, int n, int heapSize)
{
int i, lastIndex;
lastIndex = ;
for (i = ; i < n; i++)
{
//依次插入
MaxHeapPush(lastIndex, p[i]);
// 若比预定好的堆的大小小的话,最后一个value的值就要增加了
if (lastIndex < heapSize)
lastIndex++;
}
}
/*
input: lastIndex是要删除的value的位置(这里千万要注意,其实,跟前面的lastIndex有点不一样)
*/
int MaxHeapPop(int lastIndex)
{
// 交换头尾value
int temp, i;
temp = heap[];
heap[] = heap[lastIndex];
heap[lastIndex] = temp;
// 向下调整
i = ;
int child = * i + ;
while (child < lastIndex)
{
//若有右孩子节点,且右节点比左节点大,那要只需要比较右节点即可
if (child + < lastIndex && heap[ * i + ] > heap[ * i + ])
{
child = child + ;
}
//若孩子节点比父节点大,两个节点交换
if (heap[child] > heap[i])
{
temp = heap[child];
heap[child] = heap[i];
heap[i] = temp;
}
//否则说明已经有序,停止
else
break;
// 变化孩子节点的index
child = * i + ;
}
// 返回末尾value
return heap[lastIndex];
} //快排的优化,时间复杂度还是o(nlogn),但是时间会大大减少
/*
由于只需要知道前K大数,没必要把所有的数都进行排序,而快排的思想就是找到一个值一分为二,所以,我们正好利用这一点
有了之前写好的partition函数,实现起来就就是方便!!
*/
void optQuickSort(int* p, int low, int high, int k)
{
int cur = partition(p, low, high);
if (cur - low > k)
optQuickSort(p, low, cur - , k);
else if (cur - low < k - )
optQuickSort(p, cur + , high, k - (cur - low + ));
} //部分排序,o(nK)
/*
这本应该最新想到的呀,若K=1,其实就只需找最大值就好了
当K<logn时,才有用武之地呀
*/
void partSort(int* p, int n, int k)
{
int i, j, maxI, temp;
for (i = ; i < k; i++)
{
maxI = i;
for (j = i; j < n; j++)
{
if (p[j] > p[maxI])
maxI = j;
}
if (i != maxI)
{
temp = p[maxI];
p[maxI] = p[i];
p[i] = temp;
}
}
} //时间换空间的办法,o(n)
/*
适用于整数,且范围不是很大的情况
如果没有重复的话,还可以用bit数组
*/
void findCount(int*p, int n, int k)
{
int count[N] = {};
int i, j, sumCount;
//首先先对输入的元素进行计数
for (i = ; i < n; i++)
{
count[p[i]] += ;
}
sumCount = ;
for (i = N - ; i > ; i--)
{
sumCount += count[i];
//若累计最大的数大于k了,就把多余的部分去掉,把最大的k个数输出
if (sumCount > k)
{
for (j = ; j < count[i] - (sumCount - k); j++)
cout<<i<<" ";
break;
}
//若累计的没有到达K,那就直接输出啦
else
{
for (j = ; j < count[i]; j++)
cout<<i<<" ";
}
}
} int main()
{
int i, j;
for (i = ; i < n; i++)
cout<<a[i]<<" ";
cout<<endl;
/*
//快排,若取前K大的数,只需从末尾到前输出K个数即可
quickSort(a, 0, n - 1);
for (i = 0; i < n; i++)
cout<<a[i]<<" ";
cout<<endl; //注意这里之所以乘以2,是因为只维护K个数字的堆,不能得到前K个大的数!!
MaxHeapInit(a, n, K * 2 - 1);
for (i = 0; i < n; i++)
cout<<heap[i]<<" ";
cout<<endl;
// 输出,这里的lastIndex是变化的哦,因为之前维护的2 * K - 1的堆,所以这里也应该是2 * K - 1
for (i = 0; i < K; i++)
cout<<MaxHeapPop(2 * K - 1 - i)<<" ";
cout<<endl; optQuickSort(a, 0, n - 1, K); partSort(a, n, K);
for (i = 0; i < n; i++)
cout<<a[i]<<" ";
cout<<endl;
*/
findCount(a, n, K);
system("pause");
return ;
}
寻找最大的K个数(下)的更多相关文章
- 算法系列:寻找最大的 K 个数
Copyright © 1900-2016, NORYES, All Rights Reserved. http://www.cnblogs.com/noryes/ 欢迎转载,请保留此版权声明. -- ...
- 第2章 数字之魅——寻找最大的K个数
寻找最大的K个数 问题描述 在面试中,有下面的问答: 问:有很多个无序的数,我们姑且假定它们各不相等,怎么选出其中最大的若干个数呢? 答:可以这样写:int array[100] …… 问:好,如果有 ...
- O(N)的时间寻找最大的K个数
(转:http://www.cnblogs.com/luxiaoxun/archive/2012/08/06/2624799.html) 寻找N个数中最大的K个数,本质上就是寻找最大的K个数中最小的那 ...
- 03寻找最小的k个数
题目描述:查找最小的k个元素 题目:输入n个整数,输出其中最小的k个. 例如输入1,2,3,4,5,6,7和8这8个数字,则最小的4个数字为1,2,3和4. 1:最简单 ...
- 算法练习:寻找最小的k个数
参考July的文章:http://blog.csdn.net/v_JULY_v/article/details/6370650 寻找最小的k个数题目描述:查找最小的k个元素题目:输入n个整数,输出其中 ...
- 编程之美2.5:寻找最大的K个数
编程之美2.5:寻找最大的K个数 引申:寻找第k大的数: 方法一: // 选择第k大的数(通过改进快速排序来实现) public static void SelectShort(int[] array ...
- 寻找最大的k个数问题
这是编程之美书第2.5节的一道题目. 各种解法: 解法一,用nlgn复杂度的排序算法对数组进行从大到小排序,取前K个.但这方法做了两件不必要做的事:它对想得到的K个数进行了排序,对不想得到的n-K个数 ...
- 【编程之美】2.5 寻找最大的k个数
有若干个互不相等的无序的数,怎么选出其中最大的k个数. 我自己的方案:因为学过找第k大数的O(N)算法,所以第一反应就是找第K大的数.然后把所有大于等于第k大的数取出来. 写这个知道算法的代码都花了2 ...
- 寻找最大的K个数(上)
这是一道很经典的题目,有太多方法了,今天写了两种方法,分别是快排和堆排序 #include <iostream> using namespace std; #define N 25 //初 ...
随机推荐
- 关于RouterOS 国内DDNS服务
虽然RouterOS 加入了cloud功能,但最近在配置RB2011的时候发现不好使,更新域名后无法正确解析到我的IP地址,虽然在cloud的public address中显示了正确的公网ip地址,但 ...
- JQuery的筛选方法
前面咱们学习到了jquery的选择器的筛选用法,那么咱们接下来学习一下jquery的常用筛选方法,一张图搞定一切.
- vs2017运行时修改代码Changes are not allowed while code is running.
vs2017代码运行时不允许进行更改 工具->选项->调试->常规->启用编辑并继续不选择“启用编辑并继续”,这样就可以在调试时修改cs代码了.
- Java遍历一个目录下的所有文件
Java遍历一个目录下的所有文件 Java工具中为我们提供了一个用于管理文件系统的类,这个类就是File类,File类与其他流类不同的是,流类关心的是文件的内容,而File类关心的是磁盘上文件的存 ...
- yyblog2.0 数据库开发规范
一.基础规范 (1)必须使用InnoDB存储引擎 解读:支持事务.行级锁.并发性能更好.CPU及内存缓存页优化使得资源利用率更高 (2)表字符集默认使用utf8,必要时候使用utf8mb4 解读:1. ...
- linux下创建具有root权限的账户
http://blog.chinaunix.net/uid-24631445-id-2981034.html
- Cmder的安装
Cmder把conemu,git-for-windows和clink打包在一起,让你无需配置就能使用一个真正干净的Linux终端!性感的外观,强大的功能!代替了Windows原生的Cmd 1. 安裝 ...
- jdk1.6 eclipse kepler 中安装jda
原因这是个比较老的版本的jad 参考:https://www.cnblogs.com/zhikou/p/8098137.html 1.在eclipse的help—>Install New Sof ...
- Java常用的转义字符
以下为常用的转义字符对照表: 字母前面加上捺斜线"\"来表示常见的那些不能显示的ASCII字符.称为转义字符.如\0,\t,\n等,就称为转义字符. 转义字符 意义 ASCII码值 ...
- post get 区别
GET 请求能被缓存,POST不能: POST 相对安全,GET的请求都包含在URL里 POST 可以通过request body 来传输较多的数据 URL有长度限制,会影响到GET请求,这个长度是由 ...