下面是我的算法,它简化了Dijkstra的3路分区算法的通用列表:
static <T extends Comparable> void dutchSort(List<T> list, int left, int right) {
if (left >= right) return;
T pivot = list.get(left);
// smaller - index of the last element smaller than pivot value
// equal - index of the last element equal to pivot value
// larger - index of the first element larger than pivot value
int smaller = left-1, equal = left, larger = right;
// before sorting is completed, 'equal' is the current value
// much like 'i' in a for-loop
// O(N) time
while (equal < larger) {
if (list.get(equal).compareTo(pivot) < 0)
Collections.swap(list, equal, ++smaller);
else if (list.get(equal).equals(pivot))
equal++;
else
Collections.swap(list, equal, --larger);
}
// recursively sort smaller subarray
dutchSort(list, left, smaller+1);
// recursively sort larger subarray
dutchSort(list, equal, list.size());
}
这是O(1)空间,我认为它是O(N ^ N)时间,但我不确定。 Toptal's post on 3-way QuickSort说它是O(N ^ 2),但区别在于我的算法更天真。我的思考过程是:while
循环需要O(N)时间,在最坏的情况下(所有N个元素都不同?)问题被分解为大小为1的N个子数组。
我尝试了主定理,但我不确定任何变量值。我认为子问题的数量是2,每个递归调用将问题减少2倍,并且合并子问题需要O(1)工作。
所有这些只是受过教育的猜测,而且我很可能很漂亮,所以我非常想严格解决时间复杂问题。
O(N ^ N)时间是否正确?如果是这样,为什么?
非常感谢:)
答案 0 :(得分:1)
因此while
循环在初始调用时为O(n)。如果我们假设一个[1, 2, 3, 4, 5]
数组,那么第一次通过循环list[equal] == pivot
,我们会增加equal
。
循环的第二次及以后的时间list[equal] > pivot
,所以我们递减larger
并与该元素交换。循环完成后,您equal=1
,smaller
未更改。您的递归调用变为:
dutchSort(list, 0, 0)
dutchSort(list, 1, n)
所以其中一件物品掉了下来。
对于更多的递归深度进行相同的心理练习,我认为您将了解分区的工作原理。
对于你的算法是O(N ^ N),它必须多次将每个元素与每个其他元素进行比较。但这不会发生,因为在每个递归级别,您将问题分成两部分。一旦某些东西被分成数组的左半部分,它就无法与移动到数组右半部分的东西进行比较。所以最糟糕的情况是每个元素都与其他元素进行比较。那将是O(N ^ 2)。
当所有元素相等时,算法为O(N)。
我认为算法的复杂性取决于唯一项目的数量。初始数组顺序似乎没有任何影响。