归并排序1(改进)与快速排序一览

2024-05-28

上一次尝试实现归并排序,直接将待排序数组拆分,这次遍历数组,遇到未按顺序的地方再进行分割。


    #自编:改进后的归并排序
    import math
    node = [0]
    def split_arr(arr):
        global node
        #使用extend而非append,避免多次调用append影响性能,下同
        arr.append(-float('inf'))
        for i in range(len(arr)-1):
            if arr[i] > arr[i+1]:
                node.append(i+1)
        A = []
        for j in range(len(node)-1):
            A.append(arr[node[j]:node[j+1]])
        return A
    def merge(L, R):
        res = []
        n1 = len(L)
        n2 = len(R)
        L.append(float('inf'))
        R.append(float('inf'))
        i, j = 0, 0
        for k in range(n1 + n2):
            if L[i] <= R[j]:
                res.append(L[i])
                i += 1
            else:
                res.append(R[j])
                j += 1
        return res

    arr_before = [3, 41, 52, 26, 38, 57, 9, 49]
    A = split_arr(arr_before)
    while len(A) > 1:
        a = merge(A[-2], A[-1])  # 合并最后一个和倒数第二个元素
        A = A[:-2] + [a]        # 直接用合并后的新元素替换掉最后两个元素

    print('after sort:', A[0])#由于A处理到最后,是一个只有一个元素的数组,所以直接输出A[0]即可

最坏的情况,时间复杂度为O(n log n),空间复杂度为O(n)

快速排序


    #快速排序
    def quicksort(arr):
        if len(arr) <= 1:
            return arr
        pivot = arr[len(arr) // 2]  # 选择中间元素作为基准
        left = [x for x in arr if x < pivot]
        middle = [x for x in arr if x == pivot]
        right = [x for x in arr if x > pivot]
        
        return quicksort(left) + middle + quicksort(right)

    # 示例
    arr = [3, 41, 52, 26, 38, 57, 9, 49]
    sorted_arr = quicksort(arr)
    print(sorted_arr)

快速排序是一种高效的排序算法,由英国计算机科学家 Tony Hoare 在 1960 年提出。它的基本思想是使用分治法(Divide and Conquer)来解决问题:

选择基准(Pivot Selection):

首先,选择一个基准元素。在这个例子中,基准是数组的中间元素(arr[len(arr) // 2])。其他方法包括随机选择或三数取中法。 分区(Partitioning):

将数组分为三个部分: left 子数组:包含所有小于基准的元素。 middle 子数组:包含所有等于基准的元素。 right 子数组:包含所有大于基准的元素。 递归排序:

对 left 和 right 子数组分别进行快速排序,这一步是递归的。由于这两个子数组都比原数组小,所以问题规模逐渐减小。 最终,将排序后的 left 子数组、middle 子数组(无需排序,因为元素已经等于基准)和排序后的 right 子数组连接起来,就得到了排序后的数组。 终止条件:

当数组长度小于或等于 1 时,我们知道数组已经是有序的,因此不需要再进行排序。这就是递归的基本停止条件。 这个例子中的快速排序实现是“非原地”的,因为它创建了新的子数组 left, middle, 和 right。原地快速排序可以减少内存使用,但实现更复杂。此外,快速排序在最坏情况下的时间复杂度是 O(n²),当输入数组已经完全有序时。然而,平均时间复杂度是 O(n log n),在实际应用中表现出良好的性能。