我一直认为迭代搜索是在未排序列表中查找最大值的首选方法。
这个想法相当随机,但简而言之:我相信我可以在O(logn)时间内完成任务,n是输入数组的大小。
关于合并排序的方法:分而治之。
步骤1:将findMax()任务划分为两个子任务findMax(leftHalf)
和findMax(rightHalf)
。此划分应在O(logn)
时间内完成。
第2步:合并两个最大候选人。 此步骤中的每个图层都应该保持恒定的时间这是错误的。每次比较都是在恒定的时间内完成的,但是有O(1)
,并且按照上一步,O(logn)
这样的图层。所以它也应该在O(1) * O(logn) = O(logn)
时间内完成(原谅滥用符号)。2^j/2
次这样的比较(第j级的2 ^ j对候选者)。
因此,整个任务应该在 O(logn)
时间内完成。O(n)
时间。
但是,当我尝试计时时,我会得到明确反映线性O(n)
运行时间的结果。
size = 100000000 max = 0 time = 556
size = 200000000 max = 0 time = 1087
size = 300000000 max = 0 time = 1648
size = 400000000 max = 0 time = 1990
size = 500000000 max = 0 time = 2190
size = 600000000 max = 0 time = 2788
size = 700000000 max = 0 time = 3586
怎么回事?
这是代码(我保留未初始化的数组以保存预处理时间,该方法,据我测试它,准确识别未排序数组中的最大值):
public static short findMax(short[] list) {
return findMax(list, 0, list.length);
}
public static short findMax(short[] list, int start, int end) {
if(end - start == 1) {
return list[start];
}
else {
short leftMax = findMax(list, start, start+(end-start)/2);
short rightMax = findMax(list, start+(end-start)/2, end);
return (leftMax <= rightMax) ? (rightMax) : (leftMax);
}
}
public static void main(String[] args) {
for(int j=1; j < 10; j++) {
int size = j*100000000; // 100mil to 900mil
short[] x = new short[size];
long start = System.currentTimeMillis();
int max = findMax(x);
long end = System.currentTimeMillis();
System.out.println("size = " + size + "\t\t\tmax = " + max + "\t\t\t time = " + (end - start));
System.out.println();
}
}
答案 0 :(得分:6)
您应该计算实际发生的比较次数:
在最后一步中,在找到前n / 2个数字和最后n / 2个nubmers的最大值后,需要再进行1次比较才能找到整个数字的最大值。
在上一步中,您必须找到第一组和第二组n / 4数的最大值以及第三组和第四组n / 4数的最大值,因此您有2次比较。
最后,在递归结束时,你有n / 2组2个数字,你必须比较每一对,所以你有n / 2个比较。
当你总结它们时:
1 + 2 + 4 + ... + n / 2 = n-1 = O(n)
答案 1 :(得分:1)
您确实创建了log(n)
个图层。
但是在一天结束时,你仍然可以浏览每个创建的桶的每个元素。因此,你要经历每个元素。总的来说,你仍然是O(n)
。
答案 2 :(得分:1)
根据Eran的回答,你已经知道你的推理有什么问题了。
但无论如何,有一个称为主定理的定理,它有助于递归函数的运行时分析。
它反映了以下等式:
T(n) = a*T(n/b) + O(n^d)
其中T(n)是大小为n的问题的运行时间。
在您的情况下,递推方程式为T(n) = 2*T(n/2) + O(1)
所以a=2
,b=2
和d=0
。情况就是这样,因为对于每个n大小的问题实例,将其分解为大小为n / 2(b)的2(a)个子问题,并将它们组合在O(1)= O(n ^ 0)中
主定理简单地陈述了三种情况:
如果a = b^d
,则总运行时间为O(n^d*log n)
如果a < b^d
,则总运行时间为O(n^d)
如果a > b^d
,则总运行时间为O(n^(log a / log b))
您的案例与第三个匹配,因此总运行时间为O(n ^(log 2 / log 2))= O(n)
尝试理解这三种情况背后的原因是一个很好的练习。它们仅仅是以下情况:
1st)我们为每个递归级别执行相同数量的总工作(这是mergesort的情况),因此我们只将合并时间O(n ^ d)乘以级别数log n。 / p>
第二)我们对第二个递归级别的工作少于第一次递归,依此类推。因此,总工作基本上是最后一个合并步骤(第一个递归级别)的工作,O(n ^ d)。
3rd)我们为更深层次(您的情况)做更多工作,因此运行时间为O(递归树中的叶数)。在你的情况下,你有n个叶子用于更深的递归级别,所以O(n)。
Stanford cousera课程中有一些简短的视频非常适合解释Master Method,https://www.coursera.org/course/algo。我相信你总是可以预览课程,即使没有注册。