我在分析算法的时间复杂度方面遇到了问题。
例如以下Haskell代码,它将对列表进行排序。
sort xs
|isSorted xs= xs
|otherwise= sort (check xs)
where
isSorted xs=all (==True) (zipWith (<=) xs ( drop 1 xs))
check [] =[]
check [x]=[x]
check (x:y:xs)
|x<=y = x:check (y:xs)
|otherwise=y:check (x:xs)
因此,n是列表的长度,t_isSorted(n)是运行时间函数:有一个常量t_drop(n)= c和t_all(n)= n,t_zipWith(n)= n:
t_isSorted(n)= c + n +n
对于t_check:
t_check(1)=c1
t_check(n)=c2 + t_check(n-1), c2= for comparing and changing an element
.
.
.
t_check(n)=i*c2 + tcheck_(n-i), with i=n-1
=(n-1)*c2 + t_check(1)
=n*c2 - c2 + c1
我究竟如何将这些组合起来得到t_sort(n)?我想在最坏的情况下,排序xs必须运行n-1次。
答案 0 :(得分:3)
isSorted
确实是O(n)
,因为它由zipWith
支配,而O(n)
又是check
,因为它对其参数进行了线性传递。
O(n)
本身是O(n*log(n))
,因为它每次执行只调用一次,并且它总是从列表中删除一定数量的元素。最快的排序算法(不知道关于列表的更多信息)在O(log(n!))
中运行(相当于check
时间)。有一个数学证明,这个算法更快,所以它不可能对整个列表进行排序。
[3,2,1]
只会使事情迈出一步;它实际上是一次冒泡排序。
考虑对此列表进行排序:check [3,2,1] = 2:(check [3,1]) -- since 3 > 2
check [3,1] = 1:(check [3]) -- since 3 > 1
check [3] = [3]
[2,1,3]
将返回“已排序”列表3
。
然后,只要列表没有排序,我们循环。由于我们可能只将一个元素放在正确的位置(如上例中的O(n)
所示),我们可能需要O(n) * O(n) = O(n^2)
循环迭代。
这总计时间复杂度为{{1}}
答案 1 :(得分:1)
时间复杂度为O(n^2)
。
您是对的,一步需要O(n)
次(对于isSorted
和check
个功能)。它被称为不超过n
次(甚至可能是n - 1
,它对时间复杂度并不重要)(在第一次调用之后,最大元素保证是最后一个元素,第二次调用后第二大的情况也是如此。我们可以证明最后k
个元素是最大的,并在k
调用后正确排序。它仅交换相邻元素,因此每步最多可以移除一个反转。由于最坏情况下的反转次数为O(n^2)
(即n * (n - 1) / 2
),因此时间复杂度为O(n^2)
。