对于n 个已排序的数组 A[1...n],其中项是其中间元素。
如果 n 是奇数,则中项是序列中第 (n+1)/2 个元素;
如果 n 是偶数,则存在两个中间元素,所处的位置分别是 n/2 和 n/2+1,在这种情况下,我们将选择第 n/2 个最小元素。
这样,综合两种情况,中项是第 ⌈n/2⌉ 最小元素。
寻找中项的一个直接的方法是对所有的元素排序并取出中间一个元素。
但是在一个具有 n 个元素的集合中,中项或通常意义上的第 k 小元素,能够在最优线性时间内找到,这个问题也称为选择问题。
其基本思想如下:假设递归算法中,在每个递归调用的划分步骤后,我们丢弃元素的一个固定部分并且对剩余的元素递归,则问题的规模以几何级数递减,也就是在每个调用过程中,问题的规模以一个常因子被减小。
为了具体性,我们假设不管处理什么样的对象,算法丢弃 1/3 并对剩余的 2/3 部分递归,那么在第二次调用中,元素的数目变为 2n/3 个,第三次调用中为 4n/9 个,第四次调用中为 8n/27 个,等等。现在,假定在每次调用中,算法对每个元素耗费的时间不超过一个常数,则耗费在处理所有元素上的全部时间产生一个几何级数
cn + (2/3)cn + (2/3)2cn + ... + (2/3)jcn + ... < 3cn
这正好是选择算法的工作。下面给出的寻找第 k 小元素的算法 Select 以同样的方法运作。
首先,如果元素个数小于预定义的阀值44(阀值可以自己定,这里先定义为44),则算法使用直接的方法计算第 k 小元素。
下一步把 n 个元素划分成 ⌊n/5⌋ 组,每组由5个元素组成,如果 n 不是5的倍数,则排除剩余的元素。
每组进行排序并取出它的中项即第三个元素。接着将这些中项序列中的中项元素记为 mm,它是通过递归计算得到的。
然后将原始数组划分成三个数组:A1, A2 和 A3,其中分别包含小于、等于和大于 mm 的元素。
最后,求出第 k 小的元素出现在三个数组中的哪一个,并根据测试结果,返回第 k 小的元素或者在 A1 或 A3 上递归。
过程 Select
输入 n 个元素的数组 A[1...n] 和整数 k,1 ≤ k ≤ n
输出 A 中的第 k 小元素
算法描述 select(A, low, high, k)
1. n ← high - low + 1
2. if n < 44 then 将 A 排序 return (A[k])
3. 令 q = ⌊n/5⌋。将 A 分成 q 组,每组5个元素。如果5不整除 n ,则排除剩余的元素。
4. 将 q 组中的每一组单独排序,找出中项。所有中项的集合为 M。
5. mm ← select(M, 1, q, ⌈q/2⌉) { mm 为中项集合的中项 }
6. 将 A[low...high] 分成三组
A1 = { a | a < mm }
A2 = { a | a = mm }
A3 = { a | a > mm }
7. case
|A1| ≥ k : return select(A1, 1, |A1|, k)
|A1| + |A2| ≥ k : return mm
|A1| + |A2| < k : return select(A3, 1, |A3|, k - |A1| - |A2|)
8. end case
算法中的数组是以数学角度来描述的,起始索引都是1,我们用程序来实现时需要注意一下
我这里用 MergeSort 来完成此算法中需要的排序操作,当然你也可以用任意其他的排序方法
public static int select(int[] sourceArray, int low, int high, int nthMinIndex)
{
if (nthMinIndex < 0 || low > high)
return -1;
int sourceLength = high - low + 1;
if (nthMinIndex >= sourceLength)
return -1;
if (sourceLength < 44)
{
mergeSort(sourceArray, low, high);
return sourceArray[nthMinIndex];
}
int middleArrayLength = 5;
int middleArrayQuantity = sourceLength / middleArrayLength;
int[] middleValueArray = new int[middleArrayLength];
for (int i = 0; i < middleArrayLength; i++)
{
mergeSort(sourceArray,
i * middleArrayQuantity, (i + 1) * middleArrayQuantity - 1);
int middleIndex = ((i * 2 + 1) * middleArrayQuantity - 1) / 2 +
(((i * 2 + 1) * middleArrayQuantity - 1) % 2 == 0 ? 0 : 1);
middleValueArray[i] = sourceArray[middleIndex];
}
int middleValue = select(middleValueArray, 0, middleArrayLength - 1,
(middleArrayLength - 1) / 2 + ((middleArrayLength - 1) % 2 == 0 ? 0 : 1));
List<Integer> lessThanMiddleValueList = new LinkedList<Integer>();
List<Integer> equalsWithMiddleValueList = new LinkedList<Integer>();
List<Integer> greaterThanMiddleValueList = new LinkedList<Integer>();
for (int i = 0; i < sourceArray.length; i++)
{
if (sourceArray[i] < middleValue)
{
lessThanMiddleValueList.add(sourceArray[i]);
}
else if (sourceArray[i] == middleValue)
{
equalsWithMiddleValueList.add(middleValue);
}
else
{
greaterThanMiddleValueList.add(sourceArray[i]);
}
}
Integer[] lessThanMiddleValueArray = new Integer[lessThanMiddleValueList.size()];
lessThanMiddleValueList.toArray(lessThanMiddleValueArray);
Integer[] greaterThanMiddleValueArray =
new Integer[greaterThanMiddleValueList.size()];
greaterThanMiddleValueList.toArray(greaterThanMiddleValueArray);
if (lessThanMiddleValueList.size() > nthMinIndex)
{
return select(ArrayUtils.toPrimitive(lessThanMiddleValueArray),
0, lessThanMiddleValueList.size() - 1, nthMinIndex);
}
else if (lessThanMiddleValueList.size() + equalsWithMiddleValueList.size()
> nthMinIndex)
{
return middleValue;
}
else
{
return select(ArrayUtils.toPrimitive(greaterThanMiddleValueArray),
0,
greaterThanMiddleValueList.size() - 1,
nthMinIndex
- lessThanMiddleValueList.size() - equalsWithMiddleValueList.size());
}
}
相关推荐
在计算机科学和编程领域,寻找第k小元素是一项常见的任务,它涉及到排序和搜索算法的知识。这个主题是对基本算法的复习,旨在强化我们对高效解决问题的理解。在此,我们将深入探讨几种寻找第k小元素的算法,并分析...
在本题中,我们关注的是如何运用分治算法来找到一组数字中的最大值和最小值,以及如何找出第k个最小元素。这两个问题都可以通过分治思想进行有效解决。 首先,寻找最大值和最小值。传统的做法是遍历整个数组,但...
寻找一个数组中的第k小元素可以使用分治策略。一种常见的方法是“快速选择算法”,它类似于快速排序,但只对找到第k小元素所需的部分进行排序。通过每次选择一个元素与第k小元素的候选值进行比较,可以减少不必要的...
在计算机科学中,"求第k小元素"是算法设计中的一个重要问题,它涉及到排序和查找的技巧。这个问题的目标是从一个未排序或者部分排序的数组或集合中找到第k个最小的元素,其中k通常是一个正整数。这个问题在数据分析...
在计算机科学中,"算法中最小K元素的选择问题"是一个重要的数据结构和算法主题,它涉及到从一个无序或有序的数组中找到第K小(或大)的元素。这个问题在各种应用场景中都有广泛的应用,比如数据库查询优化、排序算法...
在计算机科学领域,寻找一个数组或集合中的第k小元素是一项常见的任务,它在排序、数据分析和算法设计中都有重要应用。本主题将深入探讨四种不同的算法实现:选择排序、快速排序的选择法、中位数法和随机快速排序。...
在计算机科学中,分治...在实际编程中,这种算法不仅适用于寻找第k小元素,还广泛应用于排序(如快速排序)、查找(如二分查找)等多种场景。熟练掌握分治法及其应用,对于提升编程技能和解决复杂问题的能力至关重要。
在编程领域,寻找第k小元素(或第k大元素)是常见的算法问题,它涉及到对数据集合的排序和查找。在这个问题中,我们要在数组中找到第k个最小的元素,同时考虑到数组中可能存在重复的元素。这个问题在实际应用中广泛...
分治法是一种重要的算法设计思想,其核心在于将一个复杂的问题分解成若干个规模较小但结构相似的子问题,然后递归地解决这些子问题,并将各个子问题的解组合起来得到原问题的解。分治法在很多情况下都能达到较高的...
在本课程件中,将从多个方面对分治算法进行详细阐述,包括其基本概念、典型应用案例(如数组排序、快速排序、数组中选取Top K元素、寻找相邻点对问题),以及分治策略的具体实现和运行时间分析等。 首先,分治算法...
实验六的目的是寻找数组中的第k小的元素,这一任务可以通过使用分治策略来实现。分治法是一种高效解决问题的方法,适用于处理规模较大的问题。它将大问题分解为多个独立的子问题,然后分别解决子问题,最后将子问题...
在IT领域,尤其是在数据结构与算法这一分支中,“求数列中的第1~k小元素”的问题是一个经典且实用的问题,广泛应用于各种场景,如数据库查询优化、统计分析、机器学习中的特征选择等。该算法的目标是在一个无序的...
线性时间选择问题是在一个无序数组中找到第k小(或第k大)的元素,而分治算法在这个问题上的应用通常称为“快速选择”算法。这个算法是快速排序的一个变种,它利用分治的思想,通过随机选取一个基准元素,将数组分为...
分治策略选中第k小的数_源码”中,我们关注的是如何利用分治策略来找到一个数组中的第k小的元素。这个题目常见于编程竞赛和面试中,对于理解分治算法有着很好的实践意义。 首先,让我们深入理解分治策略。分治策略...
算法分析:这个问题可以转化为一个具有n个元素的数组中,寻找最大和最小元素的问题。一般算法是遍历数组,依次将每个元素和当前最大、最小值判断,直到遍历数组结束,返回最大、最小值。但是,这个算法的比较次数是...
在计算机科学中,"选择第k小问题"是一个常见的算法问题,主要涉及到数据排序和查找。这个问题要求在一组无序的元素中找到第k小(或第k大)的元素,而不必对整个序列进行完全排序。在本案例中,我们关注的是如何使用...
总之,有序矩阵中寻找第k小元素的问题是一个典型的优化问题,通过巧妙的算法设计,可以将原本可能需要O(n log n)时间的复杂任务降低到O(n),极大地提高了效率。Mirzaian和Arjomandi的贡献不仅在于他们的算法,更在于...
这样就可以在两个子数组中进一步递归寻找第k小的元素。 - 合并:对于每一轮递归,我们可能会找到一个新的分界点,这个分界点表示的就是当前范围内的第k小的元素。最终,递归返回的就是原数组中的第k小的元素。 实验...
在编程领域,寻找一个数组中的第k小元素是一项常见的任务,尤其在算法设计和数据结构的学习中占有重要地位。这个任务通常涉及到排序和查找技术,而在这个特定的案例中,我们使用C++语言来实现,并且采取了一种将元素...