排序算法 [ 基于Python语言 ] 冒泡排序 快速排序 堆排序 计数排序 桶排序

欢迎白嫖 但也殷切希望大家 关注+收藏+点赞 继续加油!!!

本文主要介绍了数据结构中常见的排序算法:冒泡排序(鸡尾酒排序) 快速排序(分治法) 堆排序(大顶堆) 计数排序 桶排序 (使用的是Python语言);以及各个算法之间的比较(稳定性 时间复杂度 空间复杂度等)

目录

本文主要介绍了数据结构中常见的排序算法:冒泡排序(鸡尾酒排序) 快速排序(分治法) 堆排序(大顶堆) 计数排序 桶排序 (使用的是Python语言);以及各个算法之间的比较(稳定性 时间复杂度 空间复杂度等)

4.1 分类

1.时间复杂度为O(n^2)

2.O(nlogn)

3.时间复杂度为线性

4.2 冒泡排序及其改进(鸡尾酒排序)

4.2.1 冒泡排序

4.2.2 鸡尾酒排序

4.3 快速排序

4.3.1 分治法 基准元素的选择

4.3.3 元素的交换

1.双边循环法

2.单边循环法

4.4 堆排序(不稳定排序)

4.4.1 堆排序认识

大顶堆:每一个树的孩子节点值均小于父节点

4.4.2 堆排序的代码实现

4.5 计数排序和桶排序

4.5.1 计数排序

介绍 文心一言

​编辑

代码表示

适用范围

4.5.2 桶排序

代码表示

4.6 小结 排序算法归纳


4.1 分类

根据时间复杂度的不同,主流的排序算法可以分为3大类:

1.时间复杂度为O(n^2)

冒泡排序

选择排序

插入排序

希尔排序(它的性能略优于O(n^2),O(nlogn),姑且把它归为此类)

2.O(nlogn)

快速排序

归并排序

堆排序

3.时间复杂度为线性

基数排序

归并排序

基数排序

冒泡排序

4.2 冒泡排序及其改进(鸡尾酒排序)

4.2.1 冒泡排序
`numpy` 是 Python 中一个非常重要的库,它提供了大量的数学函数操作,特别是针对数组的操作。
# 冒泡排序
# 生成一个随机列表
import numpy as np
list_pao = np.random.randint(100,size = 6)
print('随机生成的列表为:',list_pao)
count = len(list_pao)
# i表示一共进行了多少轮,j表示每一轮比较了多少次(每一轮比较的元素)
for i in range(count-1):
    for j in range(count-i-1):
        if list_pao[j] > list_pao[j+1]:
            list_pao[j],list_pao[j+1] = list_pao[j+1],list_pao[j]
print('排列后的列表为:',list_pao)
4.2.2 鸡尾酒排序

冒泡算法的每一轮都是从左到右一次比较元素,单项进行,

鸡尾酒排序是像摆钟一样,左右循环进行(感觉有点像指针),没有元素进行位置交换,排序结束。

文心一言

# 鸡尾酒排序算法
def cocktail_sort(arr):
    n = len(arr)
    swapped = True
    start = 0
    end = n - 1
​
    while swapped:
        # 重置swapped标志,如果这轮遍历中没有发生交换,则数组已经排序完成
        swapped = False
​
        # 从左向右遍历数组
        for i in range(start, end):
            if arr[i] > arr[i + 1]:
                # 交换元素
                arr[i], arr[i + 1] = arr[i + 1], arr[i]
                swapped = True
​
                # 如果这一轮没有发生交换,则数组已经排序完成
        if not swapped:
            break
​
            # 否则,重置swapped标志,并准备从右向左遍历
        swapped = False
​
        # 从右向左遍历数组,注意此时不需要再比较最后一个元素(因为它已经在上一轮与倒数第二个元素比较过了)
        end -= 1
        for i in range(end - 1, start - 1, -1):
            if arr[i] > arr[i + 1]:
                # 交换元素
                arr[i], arr[i + 1] = arr[i + 1], arr[i]
                swapped = True
​
                # 下一轮从左向右遍历时,由于最小的元素已经被移到了最左边,所以从start+1开始
        start += 1
​
# 测试代码
arr = [7, 3, 5, 8, 2, 9, 4, 1, 6]
cocktail_sort(arr)
print("Sorted array is:", arr)

4.3 快速排序

4.3.1 分治法 基准元素的选择

在每一轮挑选一个基准元素,并让其他比他大的元素移动到数列的一边,比它小的元素移动到数列的另一边,从而把数列拆解成两个部分。这种思想叫做分治法

时间复杂度为O(nlogn),最坏的情况下是O(n^2)

基准元素的选择:

一般情况下选取的是第一个元素,特殊情况,随机选择一个元素作为基准元素.

4.3.3 元素的交换
1.双边循环法
# 双边循环实现快速排序
# 递归
def quicksort(arr):
    if len(arr) <= 1:
        return arr
    pivot = arr[len(arr) // 2]  # 选择中间元素作为基准
    left, right = 0, len(arr) - 1
​
    # 类似于荷兰国旗问题的三向切分
    lt, gt = left, right  # lt是小于pivot的区域的右边界,gt是大于pivot的区域的左边界
    i = left  # 从左向右遍历数组
​
    while i <= gt:
        if arr[i] < pivot:
            arr[i], arr[lt] = arr[lt], arr[i]  # 交换,将小于pivot的元素移到左边
            lt += 1
            i += 1
        elif arr[i] > pivot:
            arr[i], arr[gt] = arr[gt], arr[i]  # 交换,将大于pivot的元素移到右边
            gt -= 1
        else:
            i += 1  # 当前元素等于pivot,不做交换,继续向右移动
​
    # 递归排序小于和大于pivot的部分
    quicksort(arr[:lt])
    quicksort(arr[gt + 1:])
​
    # 注意:这里的递归方式没有直接修改原数组,而是对切片进行递归。
    # 若要直接修改原数组,可以考虑使用索引而非切片进行递归,或使用辅助函数。
​
​
# 示例
arr = [10, 7, 8, 9, 1, 5]
quicksort(arr)
print(arr)  # 注意:由于原数组被直接修改,所以打印的是排序后的数组
​
​
# 这里的问题在于,由于切片操作,原数组arr并未被直接修改。
# 若要修改原数组,请采用索引进行递归或调整函数逻辑。
​
# 修正为直接修改原数组的版本(使用索引递归)
def quicksort_inplace(arr, low, high):
    if low < high:
        pi = partition(arr, low, high)
        quicksort_inplace(arr, low, pi - 1)
        quicksort_inplace(arr, pi + 1, high)
​
​
def partition(arr, low, high):
    pivot = arr[high]  # 也可以选择其他位置的元素作为pivot
    lt, gt = low, high - 1
    i = low
    while i <= gt:
        if arr[i] < pivot:
            arr[i], arr[lt] = arr[lt], arr[i]
            lt += 1
            i += 1
        elif arr[i] > pivot:
            arr[i], arr[gt] = arr[gt], arr[i]
            gt -= 1
        else:
            i += 1
    arr[lt], arr[high] = arr[high], arr[lt]
    return lt
​
​
# 使用修正后的版本
arr_inplace = [10, 7, 8, 9, 1, 5]
quicksort_inplace(arr_inplace, 0, len(arr_inplace) - 1)
print(arr_inplace)  # 现在直接打印修改后的原数组
2.单边循环法
# 单边排序
def quick_sort(arr):
    if len(arr) <= 1:
        return arr
    else:
        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)
​
    # 如果你想要一个单边循环的版本(即直接在原数组上进行操作的版本),下面是一个例子:
​
​
def quick_sort_inplace(arr, low, high):
    if low < high:
        # partition_index 是分区后基准元素的正确位置
        partition_index = partition(arr, low, high)
​
        # 递归地对基准元素左边和右边的子数组进行排序
        quick_sort_inplace(arr, low, partition_index - 1)
        quick_sort_inplace(arr, partition_index + 1, high)
​
​
def partition(arr, low, high):
    pivot = arr[high]  # 选择最右边的元素作为基准
    i = low - 1  # 小于基准的元素的最后一个索引
​
    for j in range(low, high):
        # 如果当前元素小于或等于基准
        if arr[j] <= pivot:
            i += 1
            arr[i], arr[j] = arr[j], arr[i]  # 交换
​
    arr[i + 1], arr[high] = arr[high], arr[i + 1]  # 把基准元素放到中间
    return i + 1
​
​
# 示例
arr = [10, 7, 8, 9, 1, 5]
quick_sort_inplace(arr, 0, len(arr) - 1)
print("Sorted array is:", arr)

4.4 堆排序(不稳定排序)

4.4.1 堆排序认识
  1. 最大堆的堆顶是整个堆中最大元素

  2. 最小堆的堆顶是整个堆中最小元素

    堆排序算法的步骤:

    1. 把无序数组构建成二叉堆。(时间复杂度是O(n))需要从小到大进行排序,则构建成最大堆;需要从大到小进行排序,则构成最小堆。

    2. 循环删除堆顶元素,替换到二叉堆的末尾,调整堆产生新的堆顶。(时间复杂度为O(nlogn))

大顶堆:每一个树的孩子节点值均小于父节点

4.4.2 堆排序的代码实现

堆排序的空间复杂度是O(1),时间复杂度为O(nlogn)

def heapify(arr, n, i):
    """
    将索引i处的元素调整为堆顶
    :param arr: 列表
    :param n: 列表的长度
    :param i: 当前调整元素的索引
    """
    largest = i  # 初始化最大值为根
    l = 2 * i + 1  # 左子节点
    r = 2 * i + 2  # 右子节点
​
    # 如果左子节点大于根
    if l < n and arr[l] > arr[largest]:
        largest = l
​
        # 如果右子节点大于目前的最大值
    if r < n and arr[r] > arr[largest]:
        largest = r
​
        # 如果最大值不是根 说明进行了索引值交换
    if largest != i:
        arr[i], arr[largest] = arr[largest], arr[i]  # 交换
​
        # 递归地调整受影响的子树
        heapify(arr, n, largest)
​
​
def heapSort(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[i], arr[0] = arr[0], arr[i]  # 交换
        heapify(arr, i, 0)
​
    # 测试代码
​
​
if __name__ == "__main__":
    arr = [12, 11, 13, 5, 6, 7]
    heapSort(arr)
    n = len(arr)
    print("排序后的数组是")
    for i in range(n):
        print("%d" % arr[i], end=" ")

4.5 计数排序和桶排序

计数排序;利用数组下标来确定元素的正确位置。它适用于一定范围内的整数排序。在取值范围不是很大的情况下,它的性能甚至快过那些时间复杂度为O(nlogn)的排序。

4.5.1 计数排序
介绍 文心一言

原始数组:arr

计数数组:count 生成一个长度为原始数组中最大值的数组,记录原始数组中相应元素出现的次数

累计数组:count 计数数组的值 + 累计数组上一个的值。例:1+0=1 2+1=3...

最终结果:output 在累计数组中找到原始数组元素,将累计数组-1(累计数组也要-1)得到最终结果的排序

代码表示
# 计数排序
def counting_sort(arr):
    # 找到数组中的最大值和最小值
    max_val = max(arr)
    min_val = min(arr)
​
    # 数组的长度
    range_of_elements = max_val - min_val + 1
​
    # 初始化计数数组,长度为范围+1,并全部填充为0
    count_arr = [0] * range_of_elements
​
    # 计算每个元素的出现次数
    for num in arr:
        count_arr[num - min_val] += 1
​
        # 根据计数数组重构原数组
    index = 0
    for i in range(range_of_elements):
        while count_arr[i] > 0:
            arr[index] = i + min_val
            index += 1
            count_arr[i] -= 1
​
    return arr
​
​
# 测试代码
if __name__ == "__main__":
    arr = [4, 2, 2, 8, 3, 3, 1]
    sorted_arr = counting_sort(arr)
    print("Sorted array is:", sorted_arr)
适用范围
  1. 当数列的最大和最小值差距过大时,并不适用于计数排序(例:给出20个随机数,范围在0~1亿之间,这时如果使用计数排序,需要构建长度为1亿的数组,严重浪费空间,时间复杂度也会随之变高)

  2. 当数列不是整数时,也不适用计数排序(例:如果数列中的元素都是小数,显然无法进行计数排序)

  3. 对于这些局限性,另一种限行时间排序算法做出弥补,这种排序叫做桶排序。

4.5.2 桶排序

桶排序需要创建若干个桶来协助排序。

代码表示

文心一言

# 桶排序
def bucket_sort(arr):
    if len(arr) <= 1:
        return arr
​
        # 1. 找到数组中的最大值和最小值  
    max_val = max(arr)
    min_val = min(arr)
​
    # 2. 计算桶的数量和桶的边界  
    bucket_range = max_val - min_val + 1
    bucket_count = len(arr) if len(arr) < bucket_range else bucket_range
​
    # 3. 初始化桶  
    buckets = [[] for _ in range(bucket_count)]
​
    # 4. 将数组中的元素分配到各个桶中  
    for i in arr:
        index = int((i - min_val) * bucket_count / bucket_range)
        buckets[index].append(i)
​
        # 5. 对每个桶进行排序,这里简单使用Python内置的排序  
    # 对于每个非空桶,可以使用更高效的排序算法,比如插入排序,  
    # 因为桶内数据量不大时,插入排序的效率可能更高  
    for i in range(bucket_count):
        buckets[i].sort()
​
        # 6. 合并桶中的数据  
    sorted_arr = []
    for bucket in buckets:
        sorted_arr.extend(bucket)
​
    return sorted_arr
​
​
# 示例  
arr = [0.78, 0.17, 0.39, 0.26, 0.72, 0.94, 0.21, 0.12, 0.23, 0.68]
sorted_arr = bucket_sort(arr)
print("Sorted array:", sorted_arr)

4.6 小结 排序算法归纳

根据算法的时间复杂度、空间复杂度、是否稳定等多维度来做一个归纳。

  • 19
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值