给定n个元素,要求解其中第k小的元素,一般采用先排序后然后直接得结果的过程。在数据量小的情况下没问题,时间复杂度是O(n*logn). 但是当数据量非常大的时候就要考虑是否有更好的算法来代替全局排序。
这里,采用剪枝策略,即如果要在线性时间内得到问题的解,必须在每次迭代的过程中用O(n)的时间剪去部分元素,使得在下次迭代过程中不参与比较。
《算法设计与分析导论》一书给出了一个比较经典的线性时间复杂度下的求解算法。
核心思想:
我们以每5个元素为子集划分源数组,然后对每个子集排序求出中位数,这样的中位数集合组成一个新的序列M,对M递归的求解其中位数p,得到的p即为划分点。p将源数组划分为3部分:
* s1:所有小于p的元素
* s2: 所有等于p的元素
* s3: 所有大于p的元素
下一轮迭代的时候根据输入的level参数判断该剪去哪部分元素,具体参见代码实现部分。
这样,每一轮迭代至少剪去n/4的元素(画个中位数排列的图出来就容易看出),则最坏情况下的时间复杂度表示为:
T(n)=T(3n/4)+T(n/5)+O(n)
其中
* T(3n/4)是下一轮迭代的最大数据量情况下的时间复杂度
* T(n/5)是求寻找中位数组成集合的中位数的时间复杂度
*
根据递推公式可以求得该算法的时间复杂度是O(n)
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class ElementsHandler {
/**
* 找出浮点型数组array的第level小元素
* 核心思想:如果要在线性时间内得到问题的解,必须在每次迭代的过程中用O(n)的时间剪去部分元素,使得在下次迭代过程中不参与比较
* 我们以每5个元素为子集划分源数组,然后对每个子集排序求出中位数,这样的中位数集合组成一个新的序列M,对M递归的求解其中位数p,得到的p
* 即为划分点。p将源数组划分为3部分:
* s1:所有小于p的元素
* s2: 所有等于p的元素
* s3: 所有大于p的元素
*
* 下一轮迭代的时候根据输入的level参数判断该剪去哪部分元素,具体参见代码实现部分。
*
* 这样,每一轮迭代至少剪去n/4的元素,则最坏情况下的时间复杂度表示为:
* T(n)=T(3n/4)+T(n/5)+O(n)--其中T(3n/4)是下一轮迭代的最大数据量情况下的时间复杂度,
* T(n/5)是求寻找中位数组成集合的中位数的时间复杂度
*
* 可以求得
* 找出浮点型数组array的第level小元素的算法时间复杂度是O(n)
* @param level
* @param array
* @return
*/
public static double calcValueLevel(int level, Double[] array)
{
if(array==null||level<=0||level>array.length)
throw new IllegalArgumentException("Wrong input!");
int len=array.length;
if(len==1)
return array[0];
int blockSize=5;//以每5个元素为子集划分源数组
int blocks=len%blockSize==0?len/blockSize:len/blockSize+1;
List<Double> midList=new ArrayList<Double>();
for(int i=0;i<blocks;i++)
{
int start=i*blockSize;
int end=sortBlock(array, start, blockSize);
midList.add(array[start+(end-start-1)/2]);//添加各个5元素且排好序组的中位数(偶数个的情况取第N/2个数,注意这里不存在偶数情况,因为以5个元素为单位划分)
}
Double[] midsArray=null;
midsArray=midList.toArray(new Double[midList.size()]);//获得每5个元素组中的中位数组成的数组
double mmidValue=calcValueLevel((midList.size()+1)/2,midsArray);//递归求解[n/5]组数的中位数组成的集合的中位数
List<Double> l_list=new ArrayList<Double>();
List<Double> e_list=new ArrayList<Double>();
List<Double> h_list=new ArrayList<Double>();
for(int i1=0;i1<len;i1++)
{
if(array[i1]<mmidValue)
{
l_list.add(array[i1]);
}
else
{
if(array[i1]>mmidValue)
h_list.add(array[i1]);
else
e_list.add(array[i1]);
}
}
if(level<=l_list.size())
{
Double[] lArray=null;
lArray=l_list.toArray(new Double[midList.size()]);
return calcValueLevel(level,lArray);
}
else
{
if(l_list.size()+e_list.size()>=level)
{
return mmidValue;
}
else
{
Double[] hArray=null;
hArray=h_list.toArray(new Double[h_list.size()]);
return calcValueLevel(level-l_list.size()-e_list.size(),hArray);
}
}
}
/**
* 在blockSize比较小的情况下,这里针对5个单位排序,任意方法都可以,我选择简单的选择排序算法
* @param array
* @param start
* @param blockSize
*/
public static int sortBlock(Double[] array, int start, int blockSize) {
int end=(start+blockSize)>array.length?array.length:start+blockSize;
//选择排序:小->大
for(int i=start;i<end-1;i++)
{
for(int j=i+1;j<end;j++)
{
if(array[i]>array[j])
{
double temp=array[i];
array[i]=array[j];
array[j]=temp;
}
}
}
return end;
}
/**
* @param args
*/
public static void main(String[] args) {
int level=10;
Double[] d=new Double[]{2.1,1.2,3.1,1.2,2.1,7.3,4.8,5.3,5.2,2.1,10.0,0.1,-1.1};
double res=ElementsHandler.calcValueLevel(level,d);
System.out.println("第"+level+"小的数是:"+res);
Arrays.sort(d);
System.out.println(Arrays.toString(d));
}
}
分享到:
相关推荐
例如,在一个循环中对数组的每个元素进行一次操作,循环次数与 n 相关,因此总执行次数是 n 的线性函数,即 T(n) = n。 3. **时间复杂度为 O(n^2)**: 这表示算法的运行时间与输入数据规模 n 的平方成正比。这种情况...
递归方程在算法的时间复杂度分析中扮演着关键角色,通过对递归方程进行求解,我们可以更好地理解和优化算法性能。 #### 时间复杂度分析与递归方程 在计算机科学中,算法的时间复杂度是指随着输入规模的增长,算法...
P类问题是指可以在多项式时间内求解的问题,这类问题的算法效率相对较高,如简单的数学运算、图的最短路径等。NOI和NOIP竞赛中的题目大多属于P类问题。 NP类问题是指可以在多项式时间内验证解的问题,但不一定能在...
线性时间复杂度求解数组中第K大数的问题是一个经典的数据结构与算法问题,它主要涉及到了排序算法中的快速选择算法。这篇文章将详细介绍如何在O(n)的时间复杂度内找到数组中的第K大数。 首先,理解这个问题的核心是...
- **O(n)**:线性时间复杂度,如遍历数组。 - **O(n log n)**:如归并排序和快速排序的平均情况。 - **O(n^2)**:二次时间复杂度,如冒泡排序、选择排序。 - **O(n^3)**:三次时间复杂度,一些矩阵运算可能涉及。 - ...
时间复杂度是衡量算法运行时间随输入规模增长而变化的函数,它在计算机科学与编程领域扮演着至关重要的角色。接下来,我们将围绕以下几个方面进行深入讨论: ### 1. 时间复杂度的基本概念 时间复杂度是用来表示一...
线性选择问题是指在未排序的序列中寻找第k小的元素,它的解法与快速排序紧密相关,但能在最坏情况下保持O(n)的时间复杂度,避免了快速排序在某些极端情况下的性能退化。 在本实验中,我们通过在快速排序算法的基础...
对普通用户来说,大型线性方程组的求解是一个困难问题,可通过外包计算进行解决。现有的大型线性方程组外包求解方案计算效率较低或计算结果无法完全验证。提出了一个可验证的大型线性方程组求解的外包计算协议。在...
在计算机科学领域中,**时间复杂度**是一个关键概念,用于衡量算法执行效率的一个重要指标。它描述的是算法运行时间与输入数据规模之间的关系。时间复杂度通常用大O表示法来表示,这是一种用来描述算法效率的数学...
- 分析:对于含有\( n \)个元素的集合,共有\( 2^n \)个子集,因此求解所有子集的算法的时间复杂度为\( O(2^n) \)。 2. **快速排序的时间复杂度** - 最坏情况:\( O(n^2) \),当输入数组已经是有序或接近有序时...
在计算机科学与信息学竞赛中,算法的时间复杂度分析是一个核心知识点,它直接关系到算法的效率以及解决问题的可行性。本文将对这些关键概念进行深入探讨,并提供一些实例来加深理解。 首先,算法的时间复杂度是衡量...
在算法分析与研究中,时间复杂度的分析是一项核心内容。算法的时间复杂度通常指的是算法运行时间随输入数据规模增长的变化趋势。对于非递归算法来说,时间复杂度的分析相对直观,而对于递归算法,由于其自身的调用...
1. **例3.7**:设有两个算法\( A_1 \)和\( A_2 \)求解同一个问题,时间复杂度分别为\( T_1(n)=100n^2 \)和\( T_2(n)=5n^3 \)。 - 当\( n)时,\( T_1(n)>T_2(n) \),即算法\( A_2 \)更优。 - 随着\( n \)的增大,\...
**线性时间选择问题:** 给定一个由n个元素组成的集合及一个整数k (1 ≤ k ≤ n),目标是在O(n)的时间复杂度内找到第k小的元素。 - **随机快速排序:** 快速排序是一种高效的排序算法,基于分治法。在解决线性时间...
在计算机科学领域,时间复杂度是衡量算法效率的重要指标,它描述了算法执行时间与输入数据大小之间的关系。本项目提供了用C语言编写的代码,用于测试不同算法解决最近邻问题的时间复杂度。最近邻问题是在给定一组...
- 堆排序:对于n个元素的排序,需要进行n次插入操作,每次插入的时间复杂度为O(logn),总时间复杂度为O(nlogn)。 - 二分插入排序:每次插入元素需要进行O(logn)次比较,对于n个元素,总共需要比较O(nlogn)次,因此...
值得注意的是,尽管算法的时间复杂度降低,但它生成的对称请求集的长度仍然保持在2的指数级别,这意味着它在保证高效的同时,也确保了请求集的大小相对较小,避免了不必要的通信开销。 与同样具有O(n)时间复杂度的...
然而,通常在讨论时间复杂度时,我们关注的是随着问题规模呈线性、对数、平方、立方等增长的算法,而不是指数级别的。尽管汉诺塔问题的时间复杂度非常高,但它是一个很好的教学实例,帮助初学者理解递归和时间复杂度...
序列复杂度的求解的matlab源程序,给定序列,返回其复杂度