• Leetcode算法入门与数组丨4. 数组排序


    1 冒泡排序

    冒泡排序(Bubble Sort)是一种简单的排序算法。它重复地遍历待排序的元素列表,一次比较相邻的两个元素,并按照顺序交换它们,直到整个列表排序完成。

    基本步骤

    下面是冒泡排序的基本步骤:

    1. 从列表的第一个元素开始,比较它与下一个元素的大小。
    2. 如果顺序不正确,交换这两个元素的位置。
    3. 继续比较下一个相邻的元素,重复上述步骤,直到到达列表的最后一个元素。
    4. 重复以上步骤,每次遍历列表时,最大的元素会被推到列表的末尾。
    5. 重复执行上述步骤,直到整个列表排序完成,即没有需要交换的元素。

    冒泡排序的时间复杂度为 O ( n 2 ) O(n^2) O(n2) ,其中n是待排序列表的长度。虽然冒泡排序的效率相对较低,但对于简单的排序任务或较小的数据集来说,它仍然是一个可行的选择。

    代码实现

    Python实现冒泡排序算法的示例代码

    def bubble_sort(arr):
        n = len(arr)
    
        # 遍历数组元素
    
    for i in range(n):
        
        # 每次遍历都会将最大的元素放到末尾,所以只需遍历到 n-i-1
        for j in range(n-i-1):
            
            # 比较相邻的元素
            if arr[j] > arr[j+1]:
                
                # 交换元素位置
                arr[j], arr[j+1] = arr[j+1], arr[j]
    
    return arr
    
    # 测试示例
    
    arr = [64, 34, 25, 12, 22, 11, 90]
    sorted_arr = bubble_sort(arr)
    print("排序后的数组:", sorted_arr)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    代码优化

    下面是一个经过优化的冒泡排序算法的示例代码:

    def bubble_sort(arr):
        n = len(arr)
        
        for i in range(n):
            # 初始化标志位,表示本次遍历是否发生了交换
            swapped = False
            
            for j in range(n-i-1):
                if arr[j] > arr[j+1]:
                    # 交换元素位置
                    arr[j], arr[j+1] = arr[j+1], arr[j]
                    swapped = True
            
            # 如果本次遍历没有发生交换,说明列表已经有序,提前结束排序
            if not swapped:
                break
        
        return arr
    
    # 测试示例
    
    arr = [64, 34, 25, 12, 22, 11, 90]
    sorted_arr = bubble_sort(arr)
    print("排序后的数组:", sorted_arr)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    在这个优化的代码中,添加了一个 swapped 标志位 来记录每次遍历是否发生了交换。如果某次遍历没有发生交换,说明列表已经有序,可以提前结束排序。这样可以减少不必要的比较次数,从而节约时间。

    算法分析

    • 最佳情况时间复杂度: O ( n ) O(n) O(n)
      当待排序的列表已经是有序的情况下,冒泡排序只需进行一次遍历,就可以确定列表已经有序,时间复杂度为 O ( n ) O(n) O(n)

    • 最坏情况时间复杂度: O ( n 2 ) O(n^2) O(n2)
      当待排序的列表是逆序的情况下,冒泡排序需要进行 n − 1 n-1 n1 次遍历,每次遍历需要比较相邻元素并可能进行交换,时间复杂度为 O ( n 2 ) O(n^2) O(n2)

    • 平均情况时间复杂度: O ( n 2 ) O(n^2) O(n2)
      在平均情况下,冒泡排序需要进行 n − 1 n-1 n1 次遍历,每次遍历需要比较相邻元素并可能进行交换,时间复杂度为 O ( n 2 ) O(n^2) O(n2)

    • 空间复杂度: O ( 1 ) O(1) O(1)
      冒泡排序是一种原地排序算法,不需要额外的空间来存储临时数据,所以空间复杂度为 O ( 1 ) O(1) O(1)

    • 稳定性:
      冒泡排序是一种稳定的排序算法,相等元素的相对顺序在排序后不会改变。

    2 选择排序

    选择排序(Selection Sort)是一种简单直观的排序算法。它的基本思想是每次从待排序的数据中选择最小(或最大)的元素,放到已排序的序列的末尾,直到全部待排序的数据元素排完为止。

    基本步骤

    选择排序的步骤如下:

    1. 在未排序序列中,找到最小(或最大)的元素,将其与序列的第一个元素交换位置。
    2. 在剩余的未排序序列中,找到最小(或最大)的元素,将其与序列的第二个元素交换位置。
    3. 重复上述步骤,直到所有元素都排好序。

    选择排序的时间复杂度为 O ( n 2 ) O(n^2) O(n2) ,其中 n n n 是待排序序列的长度。虽然选择排序的时间复杂度较高,但是它的实现简单,适合小规模数据。

    代码实现

    当然,以下是选择排序的Python代码实现:

    def selection_sort(arr):
        n = len(arr)
    
        for i in range(n-1):
            # 找到未排序部分的最小元素索引
            min_index = i
            for j in range(i+1, n):
                if arr[j] < arr[min_index]:
                    min_index = j
            
            # 将最小元素与当前位置交换
            arr[i], arr[min_index] = arr[min_index], arr[i]
        
        return arr
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    代码优化

    def selection_sort(arr):
        n = len(arr)
        
        for i in range(n-1):
            min_index = i
            for j in range(i+1, n):
                if arr[j] < arr[min_index]:
                    min_index = j
            
            if min_index != i:
                arr[i], arr[min_index] = arr[min_index], arr[i]
        
        return arr
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    在优化后的代码中,加入了一个判断,只有当找到的最小元素索引不等于当前位置时,才进行交换。这样可以避免不必要的交换操作,减少了一些冗余操作。

    算法分析

    • 时间复杂度:选择排序的时间复杂度为 O ( n 2 ) O(n^2) O(n2) ,其中n是待排序序列的长度。在选择排序的过程中,需要进行 n − 1 n-1 n1 次迭代,每次迭代都需要在未排序的部分中找到最小(或最大)的元素,这需要遍历未排序部分,时间复杂度为 O ( n ) O(n) O(n) 。因此,总的时间复杂度为 O ( n 2 ) O(n^2) O(n2)

    • 空间复杂度:选择排序的空间复杂度为 O ( 1 ) O(1) O(1) ,因为它只需要使用常数级别的额外空间来存储临时变量和交换元素。

    • 稳定性:选择排序是一种不稳定的排序算法,即相等元素的相对顺序可能会发生改变。

    3 插入排序

    插入排序(Insertion Sort)是一种简单直观的排序算法。它的基本思想是将待排序序列分成已排序区间和未排序区间,初始时已排序区间只有一个元素。然后,依次将未排序区间的元素插入到已排序区间的合适位置,直到全部元素都插入完毕。

    基本步骤

    插入排序的步骤如下:

    1. 将待排序序列的第一个元素视为已排序区间。
    2. 从第二个元素开始,依次将未排序区间的元素插入到已排序区间的合适位置。具体操作是,将待插入元素与已排序区间的元素从右到左依次比较,如果待插入元素小于已排序区间的某个元素,则将该元素后移一位,直到找到合适的位置插入。
    3. 重复上述步骤,直到未排序区间为空。

    插入排序的时间复杂度为 O ( n 2 ) O(n^2) O(n2) ,其中 n n n 是待排序序列的长度。虽然插入排序的时间复杂度较高,但是它的实现简单,对于小规模的数据排序是一种较为合适的选择。此外,插入排序是稳定的排序算法,即相等元素的相对顺序不会发生改变。

    代码实现

    def insertion_sort(arr):
        n = len(arr)
        
        for i in range(1, n):
            key = arr[i]
            j = i - 1
            
            # 将比key大的元素后移一位
            while j >= 0 and arr[j] > key:
                arr[j+1] = arr[j]
                j -= 1
            
            # 插入key到合适位置
            arr[j+1] = key
        
        return arr
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    算法分析

    • 时间复杂度:插入排序的时间复杂度为 O ( n 2 ) O(n^2) O(n2) ,其中n是待排序序列的长度。在插入排序的过程中,需要进行 n − 1 n-1 n1 次迭代,每次迭代都需要将当前元素插入到已排序部分的合适位置。在最坏情况下,每次迭代都需要将当前元素与已排序部分的所有元素比较,因此内循环的时间复杂度为 O ( i ) O(i) O(i) ,其中i是当前迭代的索引。所以总的时间复杂度为 O ( 1 + 2 + 3 + . . . + n − 1 ) O(1+2+3+...+n-1) O(1+2+3+...+n1) ,即 O ( n 2 ) O(n^2) O(n2)

    • 空间复杂度:插入排序的空间复杂度为 O ( 1 ) O(1) O(1) ,因为它只需要使用常数级别的额外空间来存储临时变量和交换元素。

    • 稳定性:插入排序是一种稳定的排序算法,即相等元素的相对顺序不会发生改变。

    4 归并排序

    归并排序(Merge Sort)是一种常见的排序算法,采用分治法(Divide and Conquer)的思想。它将待排序的序列分成两个子序列,分别对两个子序列进行排序,然后将两个已排序的子序列合并成一个有序序列。

    基本步骤

    具体步骤如下:

    1. 将待排序序列不断地二分,直到每个子序列只有一个元素。
    2. 将相邻的两个子序列合并,形成一个新的有序序列。
    3. 不断重复步骤2,直到所有子序列都合并成一个有序序列。

    归并排序的关键在于合并操作。合并操作的步骤如下:

    1. 创建一个临时数组,用于存放合并后的有序序列。
    2. 比较两个子序列的第一个元素,将较小的元素放入临时数组中,并将对应子序列的索引后移。
    3. 重复步骤2,直到其中一个子序列为空。
    4. 将另一个非空子序列的剩余元素全部放入临时数组中。
    5. 将临时数组中的元素复制回原序列的对应位置。

    代码实现

    def merge_sort(arr):
        if len(arr) <= 1:
            return arr
        
        # 将数组分成两半
        mid = len(arr) // 2
        left_half = arr[:mid]
        right_half = arr[mid:]
        
        # 递归地对左右两半进行排序
        left_half = merge_sort(left_half)
        right_half = merge_sort(right_half)
        
        # 合并左右两半
        return merge(left_half, right_half)
    
    def merge(left, right):
        merged = []
        left_index = 0
        right_index = 0
    
        # 依次比较左右两个数组的元素,将较小的元素添加到合并数组中
        while left_index < len(left) and right_index < len(right):
            if left[left_index] < right[right_index]:
                merged.append(left[left_index])
                left_index += 1
            else:
                merged.append(right[right_index])
                right_index += 1
        
        # 将剩余的元素添加到合并数组中
        while left_index < len(left):
            merged.append(left[left_index])
            left_index += 1
        while right_index < len(right):
            merged.append(right[right_index])
            right_index += 1
        
        return merged
    
    # 测试代码
    arr = [5, 2, 8, 6, 1, 9, 3, 7, 4]
    sorted_arr = merge_sort(arr)
    print(sorted_arr)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    输出结果为:[1, 2, 3, 4, 5, 6, 7, 8, 9]

    算法分析

    • 时间复杂度: O ( n × log ⁡ n ) O(n \times \log n) O(n×logn)。归并排序算法的时间复杂度等于归并趟数与每一趟归并的时间复杂度乘积。
    • 时间复杂度: O ( n ) O(n) O(n)。用到的空间大小与参加排序的数组一样。
    • 稳定性:稳定排序算法,相等元素的相对顺序在排序后不会发生改变。

    5 希尔排序

    希尔排序(Shell Sort)是一种基于插入排序的排序算法,它通过比较相距一定间隔的元素来对数据进行排序。希尔排序先将整个待排序的序列分割成若干个子序列,分别进行插入排序,然后逐步缩小间隔,最终使整个序列变为有序。

    基本步骤

    具体步骤如下:

    1. 选择一个增量序列,通常为n/2,n/4,n/8…直到增量为1。
    2. 根据增量将序列分成若干个子序列,对每个子序列进行插入排序。
    3. 不断缩小增量,重复第2步,直到增量为1时,进行最后一次插入排序。

    代码实现

    def shell_sort(arr):
        n = len(arr)
        gap = n // 2
    
        while gap > 0:
            for i in range(gap, n):
                temp = arr[i]
                j = i
    
                while j >= gap and arr[j - gap] > temp:
                    arr[j] = arr[j - gap]
                    j -= gap
    
                arr[j] = temp
    
            gap //= 2
    
        return arr
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    算法分析

    • 时间复杂度:希尔排序的时间复杂度是个开放问题,尚未得到完全确定的结论。这是因为希尔排序的性能取决于所选择的增量序列。一些增量序列可以使希尔排序的时间复杂度接近 O ( n ) O(n) O(n),而其他增量序列则可能导致时间复杂度接近 O ( n 2 ) O(n^2) O(n2)。最坏情况下,希尔排序的时间复杂度为 O ( n 2 ) O(n^2) O(n2)。这种情况发生在增量序列不好的情况下,例如使用常见的Hibbard增量序列( 2 k − 1 2^k - 1 2k1)。最好情况下,希尔排序的时间复杂度为 O ( n log ⁡ n ) O(n \log n) O(nlogn),这种情况发生在增量序列按照Knuth增量序列( 3 k − 1 3^k - 1 3k1)选择时。

    • 空间复杂度:希尔排序的空间复杂度为O(1),因为它只需要使用常数级别的额外空间来存储临时变量。

    • 稳定性:希尔排序是一种不稳定的排序算法,即相等元素的相对顺序可能在排序过程中发生改变。

    6 快速排序

    快速排序(Quick Sort)是一种常用的排序算法,它的基本思想是通过一趟排序将待排序的数据分割成独立的两部分,其中一部分的所有数据都比另一部分的所有数据小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以达到整个数据变成有序序列。

    基本步骤

    1. 选择基准值(pivot):从待排序的数组中选择一个元素作为基准值。一般情况下,可以选择数组的中间元素作为基准值。
    2. 划分操作:将数组中的其他元素与基准值比较,将小于基准值的元素放在基准值的左边,将大于基准值的元素放在基准值的右边。同时,基准值所在的位置也确定了。
    3. 递归排序:对基准值左边的子数组和右边的子数组分别进行快速排序。递归地对子数组进行划分和排序操作,直到子数组的长度为1或0,即无法再划分为更小的子数组。
    4. 合并结果:将排序好的子数组合并起来,得到最终的排序结果。

    代码实现

    def quick_sort(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 quick_sort(left) + middle + quick_sort(right)
    
    # 测试代码
    arr = [5, 2, 8, 3, 1, 6, 9, 4, 7]
    sorted_arr = quick_sort(arr)
    print(sorted_arr)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    算法分析

    • 时间复杂度:快速排序的平均时间复杂度为 O ( n log ⁡ n ) O(n\log n) O(nlogn),其中 n n n 为待排序数组的长度。在最坏情况下,快速排序的时间复杂度为 O ( n 2 ) O(n^2) O(n2) ,但这种情况发生的概率较低。

    • 空间复杂度:快速排序的空间复杂度为 O ( log ⁡ n ) O(\log n) O(logn) ,因为需要使用递归来实现划分和排序操作,递归调用的栈空间会占用一定的额外空间。

    • 稳定性:快速排序是一种不稳定的排序算法,即在排序过程中,相同元素的相对顺序可能会改变。这是因为在划分操作中,相同元素可能会被分到不同的子数组中。

    7 堆排序

    堆排序(Heap Sort)是一种基于堆数据结构的排序算法。它的基本思想是将待排序的数据构建成一个最大堆(或最小堆),然后反复从堆顶取出最大(或最小)元素,将其与堆的最后一个元素交换位置,并调整堆,使剩余元素重新构成一个最大(或最小)堆。通过不断重复这个过程,最终得到一个有序的数组。

    基本步骤

    堆排序的步骤如下:

    1. 构建最大堆:将待排序的数组看作一个完全二叉树,从最后一个非叶子节点开始,依次向上调整每个子树,使其满足最大堆的性质。最大堆的性质是父节点的值大于等于其左右子节点的值。

    2. 取出最大元素:将堆顶元素(最大值)与堆的最后一个元素交换位置,并将堆的大小减1。

    3. 调整堆:对交换后的堆顶元素进行下沉操作,将其与左右子节点中较大的节点交换位置,直到满足最大堆的性质。

    4. 重复步骤2和步骤3,直到堆的大小为1,即所有元素都已排序完毕。

    以下是一个示例,以数组 [5, 2, 8, 3, 1, 6, 9, 4, 7] 进行堆排序的步骤说明:

    1. 构建最大堆:将数组构建成最大堆,得到 [9, 7, 8, 4, 1, 6, 5, 3, 2] 。

    2. 取出最大元素:将堆顶元素 9 与堆的最后一个元素 2 交换位置,得到 [2, 7, 8, 4, 1, 6, 5, 3, 9] 。

    3. 调整堆:对交换后的堆顶元素 2 进行下沉操作,与左子节点 7 交换位置,得到 [7, 2, 8, 4, 1, 6, 5, 3, 9] 。

    4. 取出最大元素:将堆顶元素 7 与堆的最后一个元素 3 交换位置,得到 [3, 2, 8, 4, 1, 6, 5, 7, 9] 。

    5. 调整堆:对交换后的堆顶元素 3 进行下沉操作,与左子节点 8 交换位置,得到 [8, 2, 3, 4, 1, 6, 5, 7, 9] 。

    6. 重复步骤2和步骤3,直到堆的大小为1,即所有元素都已排序完毕。最终得到有序数组 [1, 2, 3, 4, 5, 6, 7, 8, 9] 。

    代码实现

    以下是使用Python语言实现堆排序的代码:

    def heapify(arr, n, i):
        largest = i
        left = 2 * i + 1
        right = 2 * i + 2
    
        if left < n and arr[left] > arr[largest]:
            largest = left
        
        if right < n and arr[right] > arr[largest]:
            largest = right
        
        if largest != i:
            arr[i], arr[largest] = arr[largest], arr[i]
            heapify(arr, n, largest)
    
    def heap_sort(arr):
        n = len(arr)
    
        # 构建最大堆
        for i in range(n // 2 - 1, -1, -1):
            heapify(arr, n, i)
        
        # 逐个取出最大元素并调整堆
        for i in range(n - 1, 0, -1):
            arr[0], arr[i] = arr[i], arr[0]
            heapify(arr, i, 0)
        
        return arr
    
    # 测试代码
    arr = [5, 2, 8, 3, 1, 6, 9, 4, 7]
    sorted_arr = heap_sort(arr)
    print(sorted_arr)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    算法分析

    • 时间复杂度:堆排序的时间复杂度为 O ( n log ⁡ n ) O(n \log n) O(nlogn),其中n为待排序数组的长度。这是因为构建最大堆的过程需要 O ( n ) O(n) O(n) 的时间复杂度,而对于每个节点进行下沉操作的时间复杂度为 O ( log ⁡ n ) O(\log n) O(logn) 。在最坏情况下,堆排序的时间复杂度也为 O ( n log ⁡ n ) O(n \log n) O(nlogn)

    • 空间复杂度:堆排序的空间复杂度为 O ( 1 ) O(1) O(1) ,因为排序过程中只需要使用常数级别的额外空间。

    • 稳定性:堆排序是一种不稳定的排序算法,即相同元素的相对顺序可能会改变。这是因为在构建最大堆和调整堆的过程中,元素的位置会发生改变。

    8 计数排序

    计数排序(Counting Sort)是一种线性时间复杂度的排序算法,它适用于待排序元素的范围较小的情况。计数排序的基本思想是统计待排序数组中每个元素出现的次数,然后根据统计结果将元素放置到正确的位置上,从而得到有序的数组。

    基本步骤

    计数排序的步骤如下:

    1. 统计元素出现次数:遍历待排序数组,统计每个元素出现的次数,并将统计结果存储在一个辅助数组中。

    2. 累加统计次数:对辅助数组进行累加操作,使得辅助数组中的每个元素表示小于等于该元素的元素个数。

    3. 生成有序数组:遍历待排序数组,根据元素的值在辅助数组中找到对应的位置,将元素放置到正确的位置上,并更新辅助数组中的统计次数。

    代码实现

    def counting_sort(arr):
        # 确定待排序数组的最大值和最小值
        min_val = min(arr)
        max_val = max(arr)
    
    # 统计元素出现的次数
    count = [0] * (max_val - min_val + 1)
    for num in arr:
        count[num - min_val] += 1
    
    # 累加统计次数
    for i in range(1, len(count)):
        count[i] += count[i - 1]
    
    # 生成有序数组
    sorted_arr = [0] * len(arr)
    for num in reversed(arr):
        index = count[num - min_val] - 1
        sorted_arr[index] = num
        count[num - min_val] -= 1
    
    return sorted_arr
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    算法分析

    • 时间复杂度:计数排序的时间复杂度为 O ( n + k ) O(n+k) O(n+k),其中 n n n 为待排序数组的长度, k k k 为待排序元素的范围。在最坏情况下,即待排序元素的范围很大且与待排序数组长度接近时,计数排序的时间复杂度接近 O ( n ) O(n) O(n)

    • 空间复杂度:计数排序的空间复杂度为 O ( n + k ) O(n+k) O(n+k) ,其中 n n n 为待排序数组的长度, k k k 为待排序元素的范围。这是因为需要使用一个辅助数组来存储统计结果和生成有序数组。

    • 稳定性:计数排序是一种稳定的排序算法,即相同元素的相对顺序在排序后保持不变。

    9 桶排序

    桶排序(Bucket Sort)是一种线性时间复杂度的排序算法,它适用于待排序元素服从均匀分布的情况。桶排序的基本思想是将待排序的元素分配到不同的桶中,每个桶内的元素进行排序,然后按照桶的顺序依次取出元素,得到有序的数组。

    基本步骤

    桶排序的步骤如下:

    1. 确定桶的数量和范围:根据待排序元素的范围和分布情况,确定桶的数量和每个桶的范围。

    2. 将元素分配到桶中:遍历待排序数组,将每个元素根据其值分配到对应的桶中。

    3. 桶内排序:对每个桶内的元素进行排序。可以使用其他排序算法,如插入排序、快速排序等。

    4. 合并结果:按照桶的顺序依次取出每个桶内的元素,将其合并到一个有序的数组中。

    代码实现

    def bucket_sort(arr):
        # 确定桶的数量和范围
        min_val = min(arr)
        max_val = max(arr)
        bucket_size = 5
    
        # 计算桶的数量
        bucket_count = (max_val - min_val) // bucket_size + 1
        
        # 创建桶
        buckets = [[] for _ in range(bucket_count)]
        
        # 将元素分配到桶中
        for num in arr:
            index = (num - min_val) // bucket_size
            buckets[index].append(num)
        
        # 桶内排序
        for bucket in buckets:
            bucket.sort()
        
        # 合并结果
        sorted_arr = []
        for bucket in buckets:
            sorted_arr.extend(bucket)
        
        return sorted_arr
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    算法分析

    • 时间复杂度:桶排序的时间复杂度为 O ( n + k ) O(n+k) O(n+k) ,其中 n n n 为待排序数组的长度, k k k 为桶的数量。
    • 空间复杂度:桶排序的空间复杂度为 O ( n + k ) O(n+k) O(n+k) ,因为需要额外的桶来存储元素。
    • 稳定性:桶排序是一种稳定的排序算法,即相同元素的相对顺序在排序后保持不变。桶排序适用于待排序元素服从均匀分布的情况。

    10 基数排序

    基数排序(Radix Sort)是一种非比较排序算法,它根据元素的每个位上的数值进行排序。基数排序的核心思想是将待排序的元素按照低位到高位的顺序依次进行排序,最终得到有序的结果。

    基本步骤

    基数排序的步骤如下:

    1. 找到待排序元素中最大值,并确定其位数(例如,最大值为3位数,则需要进行3次排序)。
    2. 从最低位开始,按照个位数的数值将元素分配到0-9的桶中。
    3. 将每个桶中的元素按照顺序合并成一个数组。
    4. 重复步骤2和步骤3,直到排序完成。

    代码实现

    以下是基数排序的Python代码实现:

    def radix_sort(arr):
        # 获取数组中的最大值,确定位数
        max_val = max(arr)
        max_digits = len(str(max_val))
    
        # 进行位数的循环排序
        for digit in range(max_digits):
            # 创建10个桶
            buckets = [[] for _ in range(10)]
        
            # 将元素按照当前位数的值放入对应的桶中
            for num in arr:
                digit_val = (num // (10 ** digit)) % 10
                buckets[digit_val].append(num)
        
            # 合并桶中的元素
            arr = [num for bucket in buckets for num in bucket]
        
        return arr
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    算法分析

    • 时间复杂度:基数排序的时间复杂度为 O ( d ∗ ( n + k ) ) O(d * (n + k)) O(d(n+k)),其中 d d d 为最大位数, n n n 为元素个数, k k k 为基数(例如,十进制中的k为10)。
    • 空间复杂度: O ( n + k ) O(n + k) O(n+k)
    • 稳定性:基数排序采用的桶排序是稳定的。基数排序是一种 稳定排序算法

    task05

    剑指 Offer 45. 把数组排成最小的数

    思路

    • x + y > y + x x+y>y+x x+y>y+x y y y 在前, x x x 在后
    • x + y > y + x x+y>y+x x+y>y+x x x x 在前, y y y 在后
    • 自定义排序

    代码

    import functools
    class Solution:
        def minNumber(self, nums: List[int]) -> str:
            def cmp(a, b):
                if a + b == b + a:
                    return 0
    
                elif a + b > b + a:
                    return 1
                
                else:
                    return -1
    
            num_s = list(map(str, nums))   
            num_s.sort(key=functools.cmp_to_key(cmp))
            return ''.join(num_s)            
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    在这里插入图片描述

    283. 移动零

    思路

    快慢指针,上分~

    代码

    class Solution:
        def moveZeroes(self, nums: List[int]) -> None:
            """
            Do not return anything, modify nums in-place instead.
            """
            slow = 0	# 快慢指针
            fast = 0
            while fast < len(nums):
                if nums[fast] != 0:
                    nums[slow], nums[fast] = nums[fast], nums[slow]
                    slow += 1
                fast += 1
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    在这里插入图片描述

    task06

    506. 相对名次

    思路

    • 先拷贝一份score,然后再对它进行希尔排序
    • 新建字典,对排序好的得分进行排名
    • 遍历score,在字典中找出对应的排名

    代码

    class Solution:
        def shellSort(self, nums: [int]) -> List[int]:  # 希尔排序
                size = len(nums)
                gap = size // 2
                while gap > 0:
                    for i in range(gap, size):
                        temp = nums[i]
                        j = i
                        while j >= gap and nums[j - gap] < temp:
                            nums[j] = nums[j - gap]
                            j -= gap
                        nums[j] = temp
                    gap = gap // 2
                return nums
    
        def findRelativeRanks(self, score: List[int]) -> List[str]:
            res = []
            nums = score.copy()
            nums = self.shellSort(nums)
            score_map = dict()  # 新建字典,存储序号
            for i in range(len(nums)):
                score_map[nums[i]] = i + 1
            
            for i in range(len(score)):     # 遍历score,找出前三名
                if score[i] == nums[0]:
                    res.append("Gold Medal")
                elif score[i] == nums[1]:
                    res.append("Silver Medal")
                elif score[i] == nums[2]:
                    res.append("Bronze Medal")
                else:
                    res.append(str(score_map[score[i]]))
            return res
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    在这里插入图片描述

    参考文献

    • [1] https://datawhalechina.github.io/leetcode-notes/#/

    —— END ——


    如果以上内容有任何错误或者不准确的地方,欢迎在下面 👇 留言。或者你有更好的想法,欢迎一起交流学习~~~

    更多精彩内容请前往 AXYZdong的博客

  • 相关阅读:
    redis的持久化
    羊了个羊的模式浅薄认知
    Skywalking全部
    阿里云/腾讯云国际站:私服服务器:什么是游戏虚拟服务器及用途讲解?
    【学习笔记】Public NOIP Round #3 简要题解
    ERC721标准与加密猫
    《快速掌握QML》第六章 动画
    MySql的初识感悟,以及sql语句中的DDL和DML和DQL的基本语法
    论文浅尝 | GPT-RE:基于大语言模型针对关系抽取的上下文学习
    软件测评的必要性,选择第三方软件测试机构的好处
  • 原文地址:https://blog.csdn.net/qq_43328313/article/details/132901998