归并排序的时间复杂度:函数似乎被调用了 2*n-1 次而不是 O(log n) 次

Time complexity of merge sort: function appears to be called 2*n-1 times rather than O(log n) times

我正在教授编码 class,需要一种直观且明显的方式来解释归并排序的时间复杂度。我尝试在 merge_sort() 函数的开头包含一个打印语句,预计该打印语句将执行 O(log n) 次。但是,据我所知,它执行了 2*n-1 次(下面的 Python 代码):

merge_sort() 函数:

def merge_sort(my_list):
    print("hi") #prints 2*n-1 times??
    if(len(my_list) <= 1):
        return
    mid = len(my_list)//2
    l = my_list[:mid]
    r = my_list[mid:]
    merge_sort(l)
    merge_sort(r)
    i = 0
    j = 0
    k = 0
    while(i < len(l) or j < len(r)):
        #print("hey") #prints nlogn times as expected
        if(i >= len(l)):
            my_list[k] = r[j]
            j += 1
        elif(j >= len(r)):
            my_list[k] = l[i]
            i += 1
        elif(l[i] < r[j]):
            my_list[k] = l[i]
            i += 1
        elif(l[i] > r[j]):
            my_list[k] = r[j]
            j += 1
        k += 1

Driver代码:

#print("Enter a list")
my_list = list(map(int, input().split()))
#print("Sorted list:")
#merge_sort(my_list)
print(my_list)

输入:

1 2 3 4 5 6 7 8

预期输出:

hi
hi
hi

或其一些与 log n 成比例变化的变体。

实际输出:

hi
hi
hi
hi
hi
hi
hi
hi
hi
hi
hi
hi
hi
hi
hi #15 times, i.e. 2*n-1

用不同的输入大小对它进行多次迭代,给我的印象是这是 2*n-1,这对我来说毫无意义。有人对此有解释吗?

只有O(logn)次递归调用是不正确的。 O(logn) 的东西是递归树的深度,而不是递归树中节点的数量。

当我们查看递归树的一个层时,我们可以注意到那个层中的每个调用都处理一个不同的数组的分区。该递归级别中的“节点”一起处理数组的所有元素,这为 level 提供了 O(n) 时间复杂度。每个级别都是如此。

由于有 O(logn) 个级别,因此总复杂度归结为 O(nlogn)。

这里有一个关于如何说明这一点的建议:

statistics = []

def merge_sort(my_list, depth=0):
    if len(my_list) <= 1:
        return
    # manage statistics
    if depth >= len(statistics):
        statistics.append(0)  # for each depth we count operations
    mid = len(my_list)//2
    l = my_list[:mid]
    r = my_list[mid:]
    merge_sort(l, depth+1)
    merge_sort(r, depth+1)
    i = 0
    j = 0
    k = 0
    while i < len(l) or j < len(r):
        statistics[depth] += 1  # count this as a O(1) unit of work
        if i >= len(l):
            my_list[k] = r[j]
            j += 1
        elif j >= len(r):
            my_list[k] = l[i]
            i += 1
        elif l[i] < r[j]:
            my_list[k] = l[i]
            i += 1
        elif l[i] > r[j]:
            my_list[k] = r[j]
            j += 1
        k += 1

import random

my_list = list(range(32))
random.shuffle(my_list)
merge_sort(my_list)
print(my_list)
print(statistics)

统计信息将输出每个级别完成的工作单元数。在输入大小为 32 的示例中,您将得到一个包含 5 个这样的数字的列表。

注意:在 Python 中,if 条件不需要括号