有人能用简单明了的英语或易于理解的方式向我解释吗?
归并排序使用“分而治之”的方法来解决排序问题。首先,使用递归将输入分成两半。分割后,对其进行排序并将它们合并成一个已排序的输出。见下图:
这意味着最好先排序一半的问题,然后再执行一个简单的合并子程序。因此,了解合并子程序的复杂度以及在递归中调用的次数非常重要。
归并排序的伪代码非常简单。
# C = output [length = N]
# A 1st sorted half [N/2]
# B 2nd sorted half [N/2]
i = j = 1
for k = 1 to n
if A[i] < B[j]
C[k] = A[i]
i++
else
C[k] = B[j]
j++
很容易看出,在每次循环中您将有4个操作:k++,i++或j++,if语句和赋值C = A | B。因此,您将拥有不超过4N + 2个操作,从而得到O(N)的复杂度。为了证明,将4N + 2视为6N,因为对于N = 1(4N + 2 <= 6N),这是成立的。
因此,假设您有一个输入具有N个元素,并且假设N是2的幂。在每个级别上,您会有两倍于前一个输入的一半元素的子问题。这意味着在级别j = 0,1,2,...,lgN处,将有个子问题,其输入长度为N / 2^j。在每个级别j处的操作次数将小于或等于
= 6N
请注意,无论哪个级别,您始终不会超过6N次操作。
由于有lgN + 1个级别,因此复杂度将为
O(6N *(lgN + 1))= O(6N*lgN + 6N)= O(n lgN)
参考资料:
n
是小写,而第二个 N
是大写?这有什么意义吗? - Pacerier2^j * 6(N / 2^j) = 6N
还需要进行两个操作。
虽然这些操作并不重要,但在这种情况下,它应该是这样的:
2^j * 6(N / 2^j) + 2 = 6N
正如你所说,将会有不超过6N次操作。 - Jorman Bustos在传统的归并排序中,每次对数据进行排序时都会将已排序的子段长度加倍。第一次排序后,文件将被排序成长度为两个记录的小节,第二遍排序后是长度为四个记录的小节,然后是八个、十六个等,一直到整个文件的大小。
必须不断地将已排序的子段长度加倍,直到只剩下一个包含整个文件的子段。需要lg(N)次对子段大小进行加倍才能达到文件大小,并且每次对数据进行排序的时间都与记录数成比例。
将数组分割到只剩单个元素为止,即生成子列表。
在每个阶段,我们将每个子列表的元素与其相邻子列表的元素进行比较。例如,[重用@Davi的图像]
log(n) base 2
因此,总复杂度为 == (每个阶段的最大比较次数*阶段数) == O((n/2)*log(n)) ==> O(nlog(n))这是因为无论是最坏情况还是平均情况,归并排序在每个阶段只是将数组分成两半,这使得它具有lg(n)的复杂度。另外一个N的复杂度来自于每个阶段进行的比较。因此结合起来就近似于O(nlgn)。无论最坏情况还是平均情况,lg(n)这个因子总是存在的。其余的N因子取决于所做的比较,在两种情况下完成。现在最坏情况是当每一阶段对N个输入进行N次比较时发生的。这样就变成了O(nlgn)。
// 双方取以2为底的对数
log(2 ^ i)= logN;
i * log2 = logN;
// log 2 = 1,因为2 ^ 1 = 2;
因此:
i = 树的高度= logN
- bourgeois247[1,5] [3,4] --> []
^ ^
[1,5] [3,4] --> [1]
^ ^
[1,5] [3,4] --> [1,3]
^ ^
[1,5] [3,4] --> [1,3,4]
^ x
[1,5] [3,4] --> [1,3,4,5]
x x
Runtime = O(A + B)
归并排序示例
你的递归调用栈将会是这样的。工作从底部叶子节点开始,并向上冒泡。
beginning with [1,5,3,4], N = 4, depth k = log(4) = 2
[1,5] [3,4] depth = k-1 (2^1 nodes) * (N/2^1 values to merge per node) == N
[1] [5] [3] [4] depth = k (2^2 nodes) * (N/2^2 values to merge per node) == N
因此,您在树的每个k
层上都要执行N
个工作,其中k = log(N)
N * k = N * log(N)
让我们以8个元素{1,2,3,4,5,6,7,8}为例,您首先必须将其分成两半,即n/2=4({1,2,3,4} {5,6,7,8}),这两个分割部分各需要0(n/2)和0(n/2)次,因此在第一步中,它需要0(n/2+n/2)=0(n)的时间。 2.下一步是将n/22分成(({1,2}{3,4})({5,6}{7,8})),分别需要(0(n/4),0(n/4),0(n/4),0(n/4)),这意味着这一步需要总共0(n/4+n/4+n/4+n/4)=0(n)的时间。 3.接下来与上一步类似,我们必须进一步将第二步除以2,即n/222 ((({1},{2},{3},{4})({5},{6},{7},{8})),其时间为0(n/8+n/8+n/8+n/8+n/8+n/8+n/8+n/8)=0(n),这意味着每个步骤都需要0(n)的时间。假设步骤为a,则归并排序所需的时间为0(an),这意味着a必须是log(n),因为步骤始终会被2除。因此,归并排序的TC最终为0(nlog(n))。