转http://blog.csdn.net/will_lee_buaa/article/details/12884989
题目:给定具有n个元素的集合,找出最大的两个元素,算法要求比较次数尽可能少
这个题目要做出来很简单,但是要找出比较次数尽可能少的算法也不是件容易的事情。
对于这个题目,目前有三种方法比较流行。
第一种:对集合扫描两遍(当然也可以扫描一遍,同时最大和第二大元素,但花费的比较次数和扫描两遍,在最坏情况下是一样的),第一遍找出最大的元素,第二遍从剩余的元素中找出最大的元素,即整个集合的第二大元素。两遍扫描需要的比较次数为2*n-3次
第二种:将集合每两个元素一组,分成n/2组,组内两个元素先进行比较,初始化两个元素,max1,max2,代表当前最大和第二大元素,然后从第一组开始,让max1和max2,依次和每组进行比较,并更新max1和max2,得到最终的整个集合的max1和max2。整个过程需要的比较次数为3*n/2次
第三种:此种方案,似乎被几乎所有人认为是最好的方案,可是事实却是相反的!下面进行第三种方案的分析:
第一步:将集合每两个元素一组,分成n/2组,组内元素进行比较,选出比较大的那个元素。
第二步:将第一步选出的n/2个元素递归使用第一步的处理方式。直到选出集合中最大的元素。
第三步:从和最大元素比较过的元素中,选出最大的元素即为第二大元素。
从第三步可以看出,我们需要开辟一块空间,在每一步中记录和每一个元素比较过的元素集合。因为,在第二部执行结束之前,我们并不知道最大元素是哪个元素。从第一步和第二步我们可以想象出来,整个比较过程像一棵二叉树,先从n个子节点开始,一层一层比较,一直到根节点(最大元素)。两个子节点的父节点是两个子节点中较大的那个元素。因此我们可以得到和最大元素比较过的元素的个数等于树的高度,即lgn,所以第三步需要的比较次数为lgn-1,而第一步和第二步需要的比较次数为n-1次,所以总的比较次数为n+lgn-2次,似乎比第一种和第二种方案要好些。
但是,第三种方案需要一块内存空间记录和每一个元素比较过的元素,这个内存空间用数组表示是个二维数组(可以用链表等其他数据结构表示,不影响后面的分析结果),A[1..n][1..lgn],由于n是要处理元素的规模,因此,二位数组需要动态申请,并初始化,这需要花费nlgn的时间!!从这里就可以看出来,第三种方案属于捡了芝麻,丢了西瓜!因此,此种方案是不可取的。
我们可以尝试着对第三种方案进行优化,能不能把A[1..n][1..lgn]二维数组优化为一维数组。
我们可以在算法最开始的时候对集合扫描一遍,找到最大元素,这个时候,我们只需要一个大小为lgn的一维数组来记录和最大元素比较过的元素了,但是对集合扫描一遍需要n-1次比较,所以优化后的总的比较次数为2*n+lgn-3次,还是比前两种方案要差!!
注:在我们专注于比较次数的时候,忽略了赋值操作,在三种算法中赋值操作的次数不比比较次数要少,但是因为三种算法的赋值操作次数差不多,上面的分析方法就忽略了赋值操作的影响。
相关推荐
在这个"Python算法集合"中,我们可以深入探讨Python在实现各种算法上的强大能力。这个资源可能包含了一个全面的代码库,涵盖了从基础排序算法到复杂优化算法的各种实现。 1. **排序算法**: - 冒泡排序:简单的...
这个压缩包提供的cpp代码实现了一个基于五元中值组取中值分割法的线性时间选择算法,特别针对找出n个元素集合s中的第k个最小元素。下面我们将详细探讨这一算法。 首先,线性时间复杂度的选择算法在处理大规模数据时...
在这个问题中,我们需要找出一组硬币中的假币,假币具有不同于真币的重量。下面我们将详细讨论如何运用“分三堆”的策略来解决这个问题。 首先,我们要明确问题的基本设定。假设我们有一组n个硬币,其中只有一个或...
3. 选择排序:选择排序每次找出未排序部分的最小(或最大)元素,然后将其与第一个未排序的位置交换。尽管其概念简单,但选择排序的时间复杂度在所有情况下都是O(n^2)。 4. 快速排序:由C.A.R. Hoare提出的快速排序...
- **交集**:找出两个集合共有的元素,形成新的集合。 - **差集**:在一个集合中去除另一个集合的元素,得到的新集合只包含第一个集合独有的元素。 - **集合的幂集**:所有可能子集构成的集合,包括空集和自身。 ...
二分图是一种特殊的图,其中的节点可以分为两个不相交的集合,所有的边都连接不同集合中的节点。在这样的图中,最大匹配是指找到尽可能多的边,使得这些边没有公共端点,即任意两条边都不会共享同一个节点。 匈牙利...
// 二分图的两个集合分别含有 n 和 m 个元素 bool visit[100], map[100][100]; // map 存储邻接矩阵 bool dfs(int k) { for (int i = 0; i ; i++) { if (map[k][i] && !visit[i]) { visit[i] = true; t = ...
4.5 子集判定模块:比较两个集合大小,再逐一检查第一个集合的元素是否都在第二个集合中。 4.6 元素判定模块:遍历集合,检查指定元素是否存在。 5. 调试与测试: 完成代码编写后,需要进行单元测试和集成测试,...
最大匹配问题则是在这样的图中寻找最大的匹配集合,即尽可能多地找到相互独立的边。 本资源“数学建模常用经典算法集合均已成功编译-二分图最大匹配算法BGMMA.rar”提供了一个已经编译完成的二分图最大匹配算法...
对于活动安排问题,贪心算法`greedySelector`确实能找出最大的相容活动子集合,即找到可以同时进行的最大数量的活动,这得益于问题本身的特性——最优子结构和贪心选择性质。 最优子结构意味着一个问题的最优解可以...
5. **合并石子问题**:这是一个博弈论问题,两个玩家轮流从一堆石子中拿走一定数量的石子,目标是尽可能拿走最后一颗石子。解决此类问题通常需要用到博弈论策略,如最小最大策略或动态规划。 6. **编辑距离**:编辑...
在找零钱的问题中,假设我们有一个有限的硬币面额集合,例如1元、5元、10元、20元等,目标是用最少数量的硬币来凑出一个给定的金额。贪心算法会尝试每次选取面额最大的硬币,直到金额被完全凑齐。它的基本思想是局部...
5. 交集:找出两个集合共有的元素,组成新的集合。 6. 差集:在第一个集合中找出不包含在第二个集合中的所有元素,形成新的集合。 在C语言中,实现这些集合运算是通过编写自定义函数来完成的。例如,你可以使用...
目标是找出总价值为Y的硬币集合,同时确保这个集合的总重量尽可能小。这个问题可以通过动态规划的方法来解决。 动态规划是一种自底向上的方法,它将复杂问题分解成更小的子问题,并存储子问题的解以便后续使用,...
- **最近点对问题**:找出平面上最近的两个点。 - **凸多边形的交**:求两个凸多边形的交集。 - **离散化与扫描**:将连续区间转化为离散点,便于处理。 ### 数据结构 - **广度优先搜索(BFS)**:从根节点开始逐层...
三分法查找假币问题是一种基于分治策略的高效算法,主要应用于解决一类特殊的搜索问题,即在一组看似相同的物品中,通过最小次数的比较找出唯一一个重量不同的“假货”。在这个场景下,假币通常较轻,而目标是确定其...
- **深度优先搜索**:在图中搜索路径的一种方法,首先沿着一条路径尽可能深入探索,直到无法前进再回溯。 - **强连通分量**:对于有向图而言,如果图中任意两个顶点都相互可达,则这两个顶点属于同一个强连通分量。 ...