1.概述
国人向来喜欢论资排辈的,每个人都想当老大,实在当不成,当个老二,老三,老K也不错,您一定看过这样的争论: 两个人吵架,一个人非常强势,另外一个忍受不住了便说:"你算老几呀?",下面就通过这篇文章就是要解决找出老几的问题!
2.应用场景
在向量V[first,last)中查找出第K大元素的值
3.分析
如果利用排序算法将向量V排好序,那么第K大元素就是索引为v.length-k的元素了,这样能解决问题,但效率不高,因为这相当于为了歼灭敌人一个小队而动用了我们全军的力量,得不偿失,回想快速排序中的分表,每次都将目标向量分为两个子表,左子表中全部小于中间元素v[mid],右边都大于中间元素v[mid],这样就可以减小了查找范围,因为我可以只查找左子表或者右子表就能找到目标元素了。如下图所示,我们可以将向量 v划分成如下
Left(<=KLargest) | KLargest | Right(>=KLargest) |
按照这样的思路,我们仍使用快速排序中的分表策略,首先将向量V从中间位置分开,分成左和右,分好后,中间值的索引如果恰恰等于K,就找到了,否则如果中间元素索引大于K,则在左子表中继续查找,忽略右子表,如果中间值索引小于K,则在右子表中继续查找,如此循环往复。
快速排序中的子表划分函数为:
/// <summary> /// 交换位置 /// </summary> /// <param name="v"></param> /// <param name="index1"></param> /// <param name="index2"></param> private void Swrap(int[] v, int index1, int index2) { int temp = v[index1]; v[index1] = v[index2]; v[index2] = temp; } /// <summary> /// 将向量V中索引{first,last)划分成两个左子表和右子表 /// </summary> /// <param name="v">向量V</param> /// <param name="first">开始位置</param> /// <param name="last">结束位置</param> private int PivotIndex(int[] v, int first, int last) { if (last == first) { return last; } if (last - first == 1) { return first; } int mid = (first + last) / 2; int midVal = v[mid]; //交换v[first]和v[mid] Swrap(v, first, mid); int scanA = first + 1; int scanB = last - 1; for (; ; ) { while (scanA <= scanB && v[scanA] < midVal) { scanA++; } while (scanB > first && midVal <= v[scanB]) { scanB--; } if (scanA >= scanB) { break; } Swrap(v, scanA, scanB); scanA++; scanB--; } Swrap(v, first, scanB); return scanB; }
设计一个函数,FindKLargest(int[] v,int first,int last,int k);这个函数包括四个参数:向量V,开始位置first,结束位置last,和第k大中的K,则该函数为:
调用FindKLargest后,因为数组是从小到大排序,所以第K大元素的值为V[v.Length-k];
void FindKLargest(int[] v, int first, int last, int k) { //表示分表中值的索引 int index = 0; index = PivotIndex(v, first, last); if (index == k) { //找到了K大 return; } if (index > k) { //只在左子表中查找 FindKLargest(v, first, index, k); } else { //只在右子表中查找 FindKLargest(v, index, last, k); } }
4.运行结果:
原向量 :v = { 100, 200, 50, 23, 300, 560, 789, 456, 123, 258}
first = 0; last = v.Length;k=3
输出:456
5.结论
利用递归算法可以将比较复杂的问题划分为越来越小的小问题,这样能够使复杂问题简单化,这样的思路在系统设计和架构中同样有着至关重要的作用,一个好的架构师,面对复杂的问题,能庖丁解牛般化腐朽为神奇,而坏的却往往适得其反,他们的特长是简单问题复杂化。
6.项目文件
http://xiazai.jb51.net/201606/yuanma/FindK(jb51.net).rar
题目描述 给定若干整数,请设计一个高效的算法,确定第k小的数。 输入格式: 测试数据有多组,处理到文件尾。每组测试数据的第1行输入2个整数n,k(1≤k≤n≤1000000)。第2行输入n个整数,每个数据的取值范围在0到1000000之间。 输出格式: 对于每组测试,输出第k小的数。 输入样例: 5 3 1 2 2 2 1 9 3 1 2 3 4 5 6 9 8 7 输出样例: 2 3 提示: 如
本文向大家介绍寻找一数组中前K个最大的数相关面试题,主要包含被问及寻找一数组中前K个最大的数时的应答技巧和注意事项,需要的朋友参考一下 考察点:数组
题目描述 输入n个整数,输出其中最小的k个。 分析与解法 解法一 要求一个序列中最小的k个数,按照惯有的思维方式,则是先对这个序列从小到大排序,然后输出前面的最小的k个数。 至于选取什么的排序方法,我想你可能会第一时间想到快速排序(我们知道,快速排序平均所费时间为n*logn),然后再遍历序列中前k个元素输出即可。因此,总的时间复杂度:O(n * log n)+O(k)=O(n * log n)。
问题内容: 对于需要解决的问题之一,我使用for循环找到了数组的最大值,因此我尝试使用递归找到它,这就是我想出的: 因此它可以正常工作并获取最大值,但是我的问题是:对于基本情况,返回a [head]以及对于在开头处的值大于最后一个值的情况,可以吗? 问题答案: 您只需一个计数器即可轻松完成此操作,只需使用您这次想要比较的值的索引即可: 这样可以更好地显示正在发生的情况,并使用默认的“递归”布局,例
本文向大家介绍C#递归算法之归并排序,包括了C#递归算法之归并排序的使用技巧和注意事项,需要的朋友参考一下 归并排序是利用递归和分而治之的技术将数据序列划分成为越来越小的半子表,再对半子表排序,最后再用递归步骤将排好序的半子表合并成为越来越大的有序序列,归并排序包括两个步骤,分别为: 1)划分子表 2)合并半子表 首先我们来讨论归并算法,归并算法将一系列数据放到一个向量中,索引范围为[first,
给定一个数组大小n和一个正数max(max表示我们可以用来放置在数组中的数字范围)。 我想计算我可以在数组中放置多少个排序数字的组合。 例如: 如果n=3,则最大值为2。(我们只能使用1/2的数字,因为最大值是2)因此有4种排序数组组合 我编写了一些代码,并成功地通过了这个特定的示例,但任何其他示例 我发现的问题是,当递归到达最后一个索引时,它不会尝试第三个数字,而是向后折叠。 我的代码: