首页 > 编程语言 >python 入门九大排序:1冒泡排序2插入排序3选择排序4快速排序5归并排序6堆排序7计数排序8基数排序9希尔排序

python 入门九大排序:1冒泡排序2插入排序3选择排序4快速排序5归并排序6堆排序7计数排序8基数排序9希尔排序

时间:2024-10-29 22:45:28浏览次数:10  
标签:arr python 元素 list 冒泡排序 列表 数组 排序

1冒泡排序:

冒泡排序是一种简单的排序算法,它重复地遍历要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。

代码如下:

import numpy as np

def bubbling(arr):
    n = len(arr)
    for i in range(n-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

old_list = np.random.randint(100,size=6)
print(f"未排序的列表{old_list}")
new_list = bubbling(old_list)
print(f"排完序后的列表{[int(x) for x in new_list]}")

代码详解:

  1. bubbling函数:

    • 首先获取输入数组的长度n
    • 外层循环for i in range(n - 1)控制排序的轮数。每一轮都会将当前未排序部分中的最大元素 “冒泡” 到末尾。
    • 内层循环for j in range(n - i - 1)遍历未排序部分的元素,进行相邻元素的比较和交换。如果当前元素大于下一个元素,就交换它们的位置。
    • 最终返回排序后的数组。

下面是举实例:

假设有一个随机生成的numpy数组old_list = [45, 78, 22, 63, 30, 85]

  1. bubbling函数:

    • n = len(arr):这里n = 6,即数组的长度。
    • 外层循环for i in range(n - 1)
      • 第一轮(i = 0):
        • 内层循环for j in range(n - i - 1),此时相当于for j in range(5)
          • 第一次比较:j = 0,比较arr[0](45)和arr[1](78),因为 45 不大于 78,所以不交换。
          • 第二次比较:j = 1,比较arr[1](78)和arr[2](22),因为 78 大于 22,所以交换这两个元素,数组变为[45, 22, 78, 63, 30, 85]
          • 继续进行比较和交换操作,直到本轮结束。此时,本轮最大的元素 78 被 “冒泡” 到了当前未排序部分的末尾。
      • 第二轮(i = 1):
        • 内层循环范围变为for j in range(4)。重复类似的比较和交换操作,将第二大的元素放到合适的位置。
      • 以此类推,经过多轮循环,最终将数组排序。

 2插入排序:

插入排序是一种简单的排序算法,它将未排序的数据逐一插入到已排序的部分中,最终使整个数组有序。

代码如下:

import numpy as np

def insert(arr):
    n = len(arr)
    for i in range(1,n):
        j = i
        while arr[j] < arr[j-1] and j >=1 :
            arr[j], arr[j-1] = arr[j-1], arr[j]
            j -= 1
    return arr

old_list = np.random.randint(100,size=6)
print(f"未排序的列表{old_list}")
new_list = insert(old_list)
print(f"排完序后的列表{[int(x) for x in new_list]}")

代码详解:

  1. insert函数:

    • n = len(arr):获取输入数组的长度。
    • for i in range(1, n):从第二个元素开始遍历数组,因为第一个元素可以认为是已排序的部分。
    • j = i:设置一个指针j,初始值为当前要插入的元素的索引。
    • while arr[j] < arr[j - 1] and j >= 1:当当前元素小于前一个元素且索引大于等于 1 时,进行循环。这意味着只要当前元素在合适的位置之前,就不断地将其与前一个元素交换位置,以将其插入到已排序部分的正确位置。
    • arr[j], arr[j - 1] = arr[j - 1], arr[j]:交换当前元素和前一个元素的位置。
    • j -= 1:将指针向前移动一位,继续比较和交换,直到找到合适的位置插入当前元素。

下面是举实例:

假设我们有一个随机生成的numpy数组old_list = [45, 78, 22, 63, 30, 85]

  1. insert函数:

    • n = len(arr):这里n = 6,即数组的长度。
    • for i in range(1, n):从第二个元素开始遍历数组。
      • 第一轮(i = 1):
        • j = i,此时j = 1。比较arr[j](78)和arr[j - 1](45),因为 78 不小于 45,所以不进入循环,数组保持不变。
      • 第二轮(i = 2):
        • j = i,此时j = 2。比较arr[j](22)和arr[j - 1](78),因为 22 小于 78,进入循环。
        • arr[j], arr[j - 1] = arr[j - 1], arr[j],交换这两个元素,数组变为[45, 22, 78, 63, 30, 85]
        • j -= 1,此时j = 1。继续比较arr[j](22)和arr[j - 1](45),因为 22 小于 45,再次交换,数组变为[22, 45, 78, 63, 30, 85]
      • 第三轮(i = 3):
        • 类似地进行比较和交换操作,将合适的元素插入到已排序部分。
      • 以此类推,直到整个数组有序。

3选择排序:

选择排序是一种简单的排序算法,它每次从未排序的部分中找到最小的元素,然后将其与未排序部分的第一个元素交换位置,逐步将整个数组排序。

代码如下:

import numpy as np

def select(arr):
    n = len(arr)
    for i in range(n-1):
        min_val1 = i
        for j in range(i+1,n):
            if arr[j] < arr[min_val1]:
                min_val1= j
        arr[i], arr[min_val1] = arr[min_val1], arr[i]
    return arr

old_list = np.random.randint(100,size=6)
print(f"未排序的列表{old_list}")
new_list = select(old_list)
print(f"排完序后的列表{[int(x) for x in new_list]}")

代码详解:

  1. select函数:

    • n = len(arr):获取输入数组的长度。
    • for i in range(n - 1):外层循环控制已排序部分的边界。从第一个元素开始,逐步将最小的元素放到已排序部分的末尾。
    • min_val1 = i:初始化当前最小元素的索引为i
    • for j in range(i + 1, n):内层循环遍历未排序部分的元素,找到最小的元素。
    • if arr[j] < arr[min_val1]:如果当前元素小于当前认为的最小元素,则更新最小元素的索引。
    • arr[i], arr[min_val1] = arr[min_val1], arr[i]:将找到的最小元素与未排序部分的第一个元素交换位置。

下面是举实例:

假设我们有一个随机生成的numpy数组old_list = [45, 78, 22, 63, 30, 85]

  1. select函数:

    • n = len(arr):这里n = 6,即数组的长度。
    • 第一轮(i = 0):
      • min_val1 = i,此时min_val1 = 0,假设当前最小元素的索引为第一个元素的索引。
      • for j in range(i + 1, n),即for j in range(1, 6)
        • 第一次比较:j = 1,比较arr[1](78)和arr[0](45),因为 78 不小于 45,所以不更新最小元素索引。
        • 继续比较,当j = 2时,发现arr[2](22)小于当前认为的最小元素arr[0](45),更新min_val1 = 2
        • 继续遍历完未排序部分,确定当前最小元素的索引为 2。
      • arr[i], arr[min_val1] = arr[min_val1], arr[i],交换arr[0]arr[2],数组变为[22, 78, 45, 63, 30, 85]
    • 第二轮(i = 1):
      • 重复上述过程,在未排序部分(arr[1]arr[5])中找到最小元素,并与未排序部分的第一个元素交换位置。
    • 以此类推,直到整个数组有序。

4快速排序 

快速排序是一种高效的排序算法,它通过选择一个基准元素,将数组分为小于基准元素、等于基准元素和大于基准元素的三个部分,然后对这三个部分分别进行递归排序,最终得到一个有序的数组。

代码如下:

import numpy as np

def quick(arr):
    n = len(arr)
    if n <= 1 :
        return arr
    mid = arr[n//2]
    left = [x for x in arr if x < mid]
    middle = [x for x in arr if x== mid]
    right = [x for x in arr if x > mid]
    return quick(left) + middle + quick(right)

old_list = np.random.randint(100,size=6)
print(f"未排序的列表{old_list}")
new_list = quick(old_list)
print(f"排完序后的列表{[int(x) for x in new_list]}")

代码详解:

  1. quick函数:

    • n = len(arr):获取输入数组的长度。
    • if n <= 1::如果数组长度小于等于 1,则直接返回该数组,因为一个元素或空数组本身就是有序的。
    • mid = arr[n // 2]:选择数组的中间元素作为基准元素。
    • left = [x for x in arr if x < mid]:使用列表推导式创建一个新的列表,包含所有小于基准元素的元素。
    • middle = [x for x in arr if x == mid]:创建一个列表,包含所有等于基准元素的元素。
    • right = [x for x in arr if x > mid]:创建一个列表,包含所有大于基准元素的元素。
    • return quick(left) + middle + quick(right):对小于基准元素的部分和大于基准元素的部分分别进行递归排序,然后将三个部分合并起来返回。

下面是举实例:

假设我们有一个随机生成的numpy数组old_list = [45, 78, 22, 63, 30, 85]

  1. quick函数:

    • n = len(arr):这里n = 6,即数组的长度。

    • if n <= 1::如果数组长度小于等于 1,则直接返回该数组。

    • mid = arr[n // 2]:选择数组的中间元素作为基准元素,这里mid = arr[3] = 63

    • left = [x for x in arr if x < mid]:创建一个新的列表,包含所有小于基准元素的元素,即left = [45, 22, 30]

    • middle = [x for x in arr if x == mid]:创建一个列表,包含所有等于基准元素的元素,即middle = [63]

    • right = [x for x in arr if x > mid]:创建一个列表,包含所有大于基准元素的元素,即right = [78, 85]

    • return quick(left) + middle + quick(right):对小于基准元素的部分和大于基准元素的部分分别进行递归排序,然后将三个部分合并起来返回。

    • 对于left部分[45, 22, 30],再次进行快速排序:

      • 选择中间元素30作为新的基准元素。
      • 得到left = [22]middle = [30]right = [45]
      • 继续递归排序,直到子数组长度小于等于 1。
    • 对于right部分[78, 85],选择中间元素85作为基准元素,得到left = []middle = [85]right = [78]

    • 最后将排序后的子数组合并起来,得到[22, 30, 45] + [63] + [78, 85] = [22, 30, 45, 63, 78, 85]

5归并排序:

归并排序是一种分治算法,它将一个数组不断地分成两部分,分别对两部分进行排序,然后再将两个已排序的子数组合并成一个有序的数组。

代码如下:

import numpy as np

def merge_sort(arr):
    n = len(arr)
    if n <= 1:
        return arr
    mid = n//2
    left = merge_sort(arr[:mid])
    right = merge_sort(arr[mid:])
    return merge(left, right)

def merge(left, right):
    result = []
    i = j =0
    while i < len(left) and j < len(right):
        if left[i] < right[j]:
            result.append(left[i])
            i += 1
        else:
            result.append(right[j])
            j += 1
    if i < len(left):
        result.extend(left[i:])
    if j < len(right):
        result.extend(right[j:])
    return result

old_list = np.random.randint(100,size=6)
print(f"未排序的列表{old_list}")
new_list = merge_sort(old_list)
print(f"排完序后的列表{[int(x) for x in new_list]}")

 代码详解:

  1. merge_sort函数:

    • n = len(arr):获取输入数组的长度。
    • if n <= 1::如果数组长度小于等于 1,则直接返回该数组,因为一个元素或空数组本身就是有序的。
    • mid = n//2:计算数组的中间位置。
    • left = merge_sort(arr[:mid]):对数组的左半部分进行递归排序。
    • right = merge_sort(arr[mid:]):对数组的右半部分进行递归排序。
    • return merge(left, right):调用merge函数将两个已排序的子数组合并成一个有序的数组并返回。
  2. merge函数:

    • result = []:创建一个空列表用于存储合并后的结果。
    • i = j = 0:初始化两个指针,分别指向左子数组和右子数组的开头。
    • while i < len(left) and j < len(right):当两个子数组都还有元素时,进行循环。
    • if left[i] < right[j]:如果左子数组当前元素小于右子数组当前元素,则将左子数组当前元素添加到结果列表中,并将左指针向后移动一位。
    • 否则,将右子数组当前元素添加到结果列表中,并将右指针向后移动一位。
    • if i < len(left):如果左子数组还有剩余元素,将其全部添加到结果列表中。
    • if j < len(right):如果右子数组还有剩余元素,将其全部添加到结果列表中。
    • return result:返回合并后的结果列表。

下面是举例:

假设我们有一个随机生成的numpy数组old_list = [45, 78, 22, 63, 30, 85]

  1. merge_sort函数:

    • n = len(arr):这里n = 6,即数组的长度。
    • if n <= 1::如果数组长度小于等于 1,则直接返回该数组。
    • mid = n//2:计算中间位置,这里mid = 3
    • left = merge_sort(arr[:mid]):对左半部分[45, 78, 22]进行递归排序。
      • 再次进入merge_sort函数,对[45, 78]进行分割和排序。
        • 继续分割为[45][78],因为长度为 1,直接返回。
        • 调用merge函数合并[45][78],得到[45, 78]
      • [22]直接返回。
      • 再次调用merge函数合并[45, 78][22],得到[22, 45, 78]
    • right = merge_sort(arr[mid:]):对右半部分[63, 30, 85]进行递归排序。
      • 类似地进行分割和排序,最终得到[30, 63, 85]
    • return merge(left, right):调用merge函数将左半部分和右半部分合并。
  2. merge函数:

    • result = []:创建一个空列表用于存储合并后的结果。

    • i = j = 0:初始化两个指针,分别指向左子数组和右子数组的开头。

    • while i < len(left) and j < len(right):当两个子数组都还有元素时,进行循环。

      • 比较左子数组和右子数组的当前元素,将较小的元素添加到结果列表中,并移动相应的指针。
    • if i < len(left):如果左子数组还有剩余元素,将其全部添加到结果列表中。

    • if j < len(right):如果右子数组还有剩余元素,将其全部添加到结果列表中。

    • return result:返回合并后的结果列表。

    • 对于上面的例子,合并[22, 45, 78][30, 63, 85],首先比较2230,将22添加到结果列表中,然后比较4530,将30添加到结果列表中,以此类推,最终得到[22, 30, 45, 63, 78, 85]

 6堆排序

堆排序是一种利用堆这种数据结构进行排序的算法,它分为两个主要阶段:构建最大堆和进行排序。

代码如下:

import numpy as np

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]:
        largerst = right
    if largest != i:
        arr[i], arr[largest] = arr[largest], arr[i]
        heapify(arr,n,largest)

def heap(arr):
   n = len(arr)
   for i in range(n//2 , -1,-1):
       heapify(arr,n,i)
   for i in range(n-1,0,-1):
        arr[i], arr[0] = arr[0], arr[i]
        heapify(arr,i,0)
   return arr

old_list = np.random.randint(100,size=6)
print(f"未排序的列表{old_list}")
new_list = heap(old_list)
print(f"排完序后的列表{[int(x) for x in new_list]}")

代码详解:

  1. heapify函数:

    • 这个函数用于维护最大堆的性质。它接受一个数组arr、数组长度n和一个索引i
    • largest = i首先假设当前索引i对应的元素是最大的。
    • left = 2*i + 1right = 2*i + 2计算当前节点的左子节点和右子节点的索引。
    • 如果左子节点存在且大于当前最大元素,更新largest为左子节点的索引。
    • 如果右子节点存在且大于当前最大元素,更新largest为右子节点的索引。
    • 如果largest不等于i,说明需要调整堆,交换arr[i]arr[largest],然后递归地调用heapify函数对新的largest位置进行调整。
  2. heap函数:

    • n = len(arr)获取数组的长度。
    • 第一个循环用于构建最大堆。从最后一个非叶子节点n//2 - 1开始,逐步向前遍历,对每个节点调用heapify函数来确保最大堆的性质。
    • 第二个循环用于进行排序。从最后一个元素开始,每次将堆顶元素(当前最大元素)与最后一个未排序的元素交换,然后对新的堆顶元素调用heapify函数,缩小堆的范围,继续进行下一次交换和调整,直到整个数组有序。

下面是举例:

假设我们有一个随机生成的数组old_list = [33, 55, 11, 44, 22, 66]

  1. 构建最大堆过程:

    • 首先计算非叶子节点的索引。这里有 6 个元素,非叶子节点是索引为 0、1、2 的元素。
    • 处理索引为 2 的元素,即数字 11。它的左子节点是 5(索引为 5 的元素 66),右子节点不存在。因为 66 大于 11,所以交换这两个元素,得到[33, 55, 66, 44, 22, 11]
    • 接着处理索引为 1 的元素,即数字 55。它的左子节点是 3(索引为 3 的元素 44),右子节点是 4(索引为 4 的元素 22)。比较后无需调整。
    • 最后处理索引为 0 的元素,即数字 33。它的左子节点是 1(索引为 1 的元素 55),右子节点是 2(索引为 2 的元素 66)。比较后发现最大元素是 66,交换arr[0]arr[2],得到[66, 55, 33, 44, 22, 11]。此时最大堆构建完成。
  2. 进行排序过程:

    • 交换堆顶元素和最后一个未排序的元素,即交换arr[0]arr[5],得到[11, 55, 33, 44, 22, 66],然后对新的堆顶元素调用heapify函数,此时堆的大小为 5。
    • 继续交换堆顶元素和当前最后一个未排序的元素,重复这个过程。
    • 第二次交换后得到[22, 55, 33, 44, 11, 66],对新的堆顶调用heapify,堆的大小为 4。
    • 第三次交换后得到[33, 55, 22, 44, 11, 66],对新的堆顶调用heapify,堆的大小为 3。
    • 第四次交换后得到[44, 55, 22, 33, 11, 66],对新的堆顶调用heapify,堆的大小为 2。
    • 第五次交换后得到[55, 44, 22, 33, 11, 66],对新的堆顶调用heapify,堆的大小为 1。
    • 最终得到排完序后的列表[11, 22, 33, 44, 55, 66]

 7计数排序

计数排序是一种非比较型整数排序算法,它通过统计每个元素在序列中出现的次数,然后根据出现次数确定每个元素在排序后的序列中的位置。

代码如下:


import numpy as np

def count(arr):
    if len(arr) == 0:
        return arr
    min_val1 = min(arr)
    max_val1 = max(arr)
    range_val1 = max_val1 - min_val1 + 1
    count_list = [0] * range_val1
    output_list = [0] * len(arr)
    for num in arr:
        count_list[num - min_val1] += 1
    for i in range(1,len(count_list)):
        count_list[i] += count_list[i - 1]
    for num in reversed(arr):
        index = count_list[num - min_val1] - 1
        output_list[index] = num
        count_list[num - min_val1] -= 1
    return output_list

old_list = np.random.randint(100,size=6)
print(f"未排序的列表{old_list}")
new_list = count(old_list)
print(f"排完序后的列表{[int(x) for x in new_list]}")

代码详解:

  1. count函数:

    • if len(arr) == 0::如果输入数组为空,则直接返回空数组。
    • min_val1 = min(arr)max_val1 = max(arr):找到输入数组中的最小值和最大值。
    • range_val1 = max_val1 - min_val1 + 1:计算数据的取值范围。
    • count_list = [0] * range_val1:创建一个计数列表,用于统计每个值出现的次数。
    • output_list = [0] * len(arr):创建一个输出列表,用于存储排序后的结果。
    • 第一个循环遍历输入数组,将每个元素的值减去最小值作为计数列表的索引,将对应位置的计数加一。
    • 第二个循环对计数列表进行累加操作,使得计数列表中的每个位置的值表示小于或等于该位置索引对应的元素在输出数组中的最终位置。
    • 第三个循环逆序遍历输入数组,根据每个元素在计数列表中的位置信息,将其放置在输出数组的正确位置,并更新计数列表中的值。
  2. 主程序部分:

    • old_list = np.random.randint(100, size = 6):生成一个包含 6 个随机整数(范围在 0 到 99 之间)的numpy数组。
    • print(f"未排序的列表{old_list}"):打印未排序的列表。
    • new_list = count(old_list):调用count函数对old_list进行排序。
    • print(f"排完序后的列表{[int(x) for x in new_list]}"):打印排完序后的列表,使用列表推导式将numpy数组中的元素转换为整数类型后输出。

下面是举例:

假设我们有一个随机生成的数组old_list = [37, 52, 28, 46, 33, 60]

  1. 确定最小值和最大值:

    • min_val1 = min(arr),在这个例子中,min_val1 = 28
    • max_val1 = max(arr),这里max_val1 = 60
  2. 计算取值范围:

    • range_val1 = max_val1 - min_val1 + 1,即60 - 28 + 1 = 33
  3. 创建计数列表和输出列表:

    • count_list = [0] * range_val1,创建一个长度为 33 的计数列表,初始值都为 0。
    • output_list = [0] * len(arr),创建一个长度为 6 的输出列表,初始值也为 0。
  4. 统计每个元素出现的次数:

    • 遍历输入数组,对于元素 37,在计数列表中的索引为37 - 28 = 9,将count_list[9]的值加一。
    • 继续遍历,对于元素 52,索引为52 - 28 = 24,将count_list[24]的值加一。
    • 以此类推,遍历完整个输入数组后,计数列表反映了每个值在输入数组中出现的次数。
  5. 累加计数列表:

    • 初始时,count_list[0]保持不变。
    • count_list[1] = count_list[1] + count_list[0]
    • 继续进行累加操作,这样,计数列表中的每个位置的值表示小于或等于该位置索引对应的元素在输出数组中的最终位置。
  6. 确定输出列表中的元素位置:

    • 逆序遍历输入数组,对于元素 60,在计数列表中的索引为60 - 28 = 32,此时count_list[32]的值表示元素 60 在输出列表中的位置索引。将 60 放置在输出列表的这个位置,并将count_list[32]的值减一。
    • 对于下一个元素继续这个过程,直到遍历完整个输入数组。

最终,输出列表就是排完序后的列表,即[28, 33, 37, 46, 52, 60]

 8基数排序

基数排序是一种非比较型整数排序算法,它通过对数字的各个位进行分别排序来实现对整个数组的排序。

代码如下:

import numpy as np

def redix(arr):
    max_num = max(arr)
    div = 1
    while max_num // div > 0:
        bucket = [[] for _ in range(10)]
        for num in arr:
            digit = (num //div) % 10
            bucket[digit].append(num)
        arr = [x for x in bucket for x in x]
        div *=10
    return arr

old_list = np.random.randint(100,size=6)
print(f"未排序的列表{old_list}")
new_list = redix(old_list)
print(f"排完序后的列表{[int(x) for x in new_list]}")

 代码详解:

  1. redix函数:

    • max_num = max(arr):找到输入数组中的最大数。
    • div = 1:初始化用于确定当前要排序的位数的除数。
    • while max_num // div > 0:当最大数除以当前除数大于 0 时,说明还有位数需要排序。
    • bucket = [[] for _ in range(10)]:创建 10 个空桶,用于存储不同位数的值。
    • for num in arr::遍历输入数组中的每个数字。
      • digit = (num // div) % 10:确定当前数字对应于当前位数的数字(例如,对于个位,div = 1;对于十位,div = 10 等)。
      • bucket[digit].append(num):将数字放入相应的桶中。
    • arr = [x for x in bucket for x in x]:将桶中的数字按顺序重新组合成一个新的数组。
    • div *= 10:将除数乘以 10,以便在下一次循环中处理下一位。

下面是举列:

假设我们有一个随机生成的数组old_list = [73, 49, 81, 26, 57, 64]

  1. 确定最大数和初始除数:

    • max_num = max(arr),这里max_num = 81
    • div = 1
  2. 第一次循环(个位排序):

    • 创建 10 个空桶:bucket = [[] for _ in range(10)]
    • 遍历输入数组中的每个数字:
      • 对于数字 73,digit = (73 // div) % 10 = 3,将 73 放入第 3 个桶中。
      • 对于数字 49,digit = (49 // div) % 10 = 9,将 49 放入第 9 个桶中。
      • 以此类推,将所有数字根据个位数字放入相应的桶中。
    • 重新组合数组:arr = [x for x in bucket for x in x]。此时可能得到[81, 26, 49, 57, 73, 64](如果个位数字没有重复,顺序可能不同)。
  3. 第二次循环(十位排序):

    • div *= 10,现在div = 10
    • 再次创建 10 个空桶。
    • 遍历重新组合后的数组:
      • 对于数字 81,digit = (81 // div) % 10 = 8,将 81 放入第 8 个桶中。
      • 对于数字 26,digit = (26 // div) % 10 = 2,将 26 放入第 2 个桶中。
      • 以此类推,将所有数字根据十位数字放入相应的桶中。
    • 重新组合数组。此时得到排序后的结果[26, 49, 57, 64, 73, 81]

 9希尔排序

希尔排序也称为 “缩小增量排序”,它是对插入排序的一种改进,通过将原始数据分成多个子序列,分别进行插入排序,逐步减少子序列的间隔,最终实现对整个序列的排序。

代码如下:

import numpy as np

def shell(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

old_list = np.random.randint(100,size=6)
print(f"未排序的列表{old_list}")
new_list = shell(old_list)
print(f"排完序后的列表{[int(x) for x in new_list]}")

 代码详解:

  1. shell函数:

    • n = len(arr):获取输入数组的长度。
    • gap = n // 2:初始化间隔为数组长度的一半。
    • while gap > 0:当间隔大于 0 时,进行循环。
    • for i in range(gap, n):从间隔位置开始遍历数组。
    • temp = arr[i]:保存当前要插入的元素。
    • j = i:设置一个指针j,初始值为当前要插入的元素的索引。
    • while j >= gap and arr[j - gap] > temp:当j大于等于间隔且当前位置的元素大于要插入的元素时,进行循环。
    • arr[j] = arr[j - gap]:将较大的元素向后移动间隔的位置。
    • j -= gap:更新指针j,继续向前比较。
    • arr[j] = temp:将保存的要插入的元素放置在正确的位置。
    • gap //= 2:每次循环结束后,将间隔缩小一半。

下面是举例:

假设我们有一个随机生成的数组old_list = [58, 33, 72, 46, 29, 67]

  1. 初始设置:

    • n = 6,即数组长度为 6。
    • gap = n // 2 = 3
  2. 第一次以间隔为 3 进行排序:

    • 子序列为[58, 46][33, 29][72, 67]
    • 对于[58, 46],58 大于 46,交换得到[46, 58]
    • 对于[33, 29],33 大于 29,交换得到[29, 33]
    • 对于[72, 67],72 大于 67,交换得到[67, 72]
    • 此时数组变为[46, 29, 67, 58, 33, 72]
  3. 缩小间隔:

    • gap //= 2 = 1
  4. 以间隔为 1 进行插入排序:

    • 从第二个元素开始,逐个插入到已排序的部分中。
    • 对于 29,与 46 比较,29 小于 46,交换得到[29, 46, 67, 58, 33, 72]
    • 对于 67,无需交换。
    • 对于 58,无需交换。
    • 对于 33,与 58 比较,33 小于 58,交换;继续与 46 比较,33 小于 46,交换;继续与 29 比较,无需交换,得到[29, 33, 46, 58, 67, 72]

最终得到排序后的数组[29, 33, 46, 58, 67, 72]

标签:arr,python,元素,list,冒泡排序,列表,数组,排序
From: https://blog.csdn.net/2401_88040640/article/details/143351557

相关文章

  • Python中的*args和**kwargs
    在Python编程中,函数的参数处理是一个非常重要的特性,它让代码更加灵活和强大。特别是在处理不确定数量的参数时,Python提供了两个非常有用的工具:*args和**kwargs。这两个特殊的参数使得函数能够接收任意数量的位置参数或关键字参数,从而极大地增加了函数的通用性和灵活性。*args用于......
  • D50【python 接口自动化学习】- python基础之类
    day50init方法学习日期:20241027学习目标:类--64init方法:如何为对象传递参数?学习笔记:魔术方法init方法classKlass(object):#定义初始化方法,类实例化时自动进行初始化def__init__(self,name,age):self.name=nameself.age=agede......
  • D51【python 接口自动化学习】- python基础之模块与标准库
    day51模块的导入学习日期:20241028学习目标:模块与标准库--66模块的导入:如何使用其他人编写好的代码功能?学习笔记模块的作用导入模块的方法#导入模块#方式一importos#获取当前的位置print(os.getcwd())#方式二fromosimportgetcwd#获取当前的位置pr......
  • 拓扑排序学习笔记
    图论系列:前言:ぽつり夕立を食らった此処に帰る傘はないんだふたりで嵐を待ったどこへ行こうか探してんだ相关题单:戳我一.图论基本定义其实可以查oiwiki的。1.图图:图是一个二元组\(G=(V(G),E(G))\),其中\(V(G)\)是非空点集,由图的各个顶点组成,\(E(G)\)是各点之间的......
  • 基于Python+Django的花卉销售商城|花店系统设计与实现
    ......
  • [分享]Python学习你看这一篇就够了!(四)
    本文你将看到四.面向对象编程8:面向对象编程基础类和对象的概念类(Class):类是一种抽象的数据类型,它是对具有相同属性和行为的一组对象的描述。可以将类看作是创建对象的蓝图或模板。例如,“汽车”是一个类,它定义了汽车共有的属性(如品牌、颜色、车轮数量等)和行为(如启动、......
  • Python输入位置的坐标(即经纬度),计算两点的距离结果保留两位
    可以使用haversine公式来计算两个坐标点之间的距离。以下是一个示例代码:importmathdefcalculate_distance(lat1,lon1,lat2,lon2):#将经纬度转换为弧度lon1=math.radians(lon1)lat1=math.radians(lat1)lon2=math.radians(lon2)lat2=m......
  • Python高手进阶:重写vs重载,你真的懂了吗?
    在面向对象编程中,“重写”与“重载”是两个核心概念,它们分别对应着如何扩展或改变现有功能的不同方式。尽管Python作为一种动态语言,对于这两种概念的支持与C++、Java等强类型静态语言有所不同,但这并不意味着它们在Python中不存在。01/ Python中的重写(Overwriting)重写,又......
  • 猴子请来的补丁——Python中的Monkey Patching
    猴子补丁(MonkeyPatching)在Python中是一种允许在运行时修改对象行为的技术。这种技术可以在不直接修改原始源代码的情况下,动态地改变或扩展程序的行为。猴子补丁的原理猴子补丁的核心原理是利用Python的动态特性,即在运行时修改对象(如函数、方法或类)的属性或行为。在Python......
  • Python从0到100(六十八):Python OpenCV-图像边缘检测及图像融合
    前言:零基础学Python:Python从0到100最新最全教程。想做这件事情很久了,这次我更新了自己所写过的所有博客,汇集成了Python从0到100,共一百节课,帮助大家一个月时间里从零基础到学习Python基础语法、Python爬虫、Web开发、计算机视觉、机器学习、神经网络以及人工智能相关知......