蓝桥杯Python赛道备赛——Day2:排序算法(一)(冒泡排序、选择排序、快速排序)

   蓝桥杯中涉及的排序算法笔者计划用两期博客进行归纳。本博客包括:冒泡排序、选择排序和快速排序。每一个算法都在给出概念解释的同时,给出了示例代码,以供低年级师弟师妹们学习和练习。

   前序知识:
(1)Python基础语法
(2)Python基础算法


一、冒泡排序

1. 原理:
   冒泡排序是一种简单的排序算法,它通过重复遍历待排序的数列,比较相邻元素并交换位置,将较大的元素逐渐“冒泡”到数列的末尾。

2. 基本流程:

  • 从数列的第一个元素开始,依次比较相邻的两个元素。
  • 如果第一个元素大于第二个元素,就交换这两个元素。
  • 遍历完一次后,最大的元素已经被“冒泡”到了数列的最后一个位置。
  • 然后,忽略已经排好序的部分,对剩余的部分重复上述过程。
  • 每次遍历后,未排序的部分越来越少,直到所有元素都排好序。

3. 实时演示:
在这里插入图片描述

4. 性能分析:
(1)时间复杂度:

  • 时间复杂度较高,效率较低,不适用于大规模数据。
  • 最好情况:O(n)(当数组已经有序时,冒泡排序会进行一趟扫描即可退出)
  • 最坏情况:O(n^2)(当数组完全倒序时)
  • 平均情况:O(n^2)

(2)空间复杂度:

  • O(1),因为它是原地排序,不需要额外的存储空间。

5. 代码(包含上述实时演示):

# 冒泡排序
import numpy as np
import matplotlib.pyplot as plt
from IPython.display import display, clear_output

def bubble_sort_jupyter(arr):
    fig, ax = plt.subplots(figsize=(8,4))
    n = len(arr)
    
    for i in range(n):
        for j in range(n-i-1):

            #------------------------------这一部分仅仅用于实时可视化--------------------------------
            # 创建颜色数组
            colors = ['skyblue']*n
            colors[j] = 'red'
            colors[j+1] = 'lime'
            
            # 更新图表
            ax.clear()
            ax.bar(range(n), arr, color=colors)
            ax.set_title(f'冒泡排序 | 轮数: {i*n+j} | 当前比较: {arr[j]} vs {arr[j+1]}')
            
            # 显示并清除输出
            clear_output(wait=True)
            display(fig)
            plt.close(fig)  # 防止内存泄漏
            
            #------------------------------以上代码仅仅用于实时可视化--------------------------------

            #------------------------------以下代码是实际的冒泡排序逻辑------------------------------
            # 比较交换逻辑
            if arr[j] > arr[j+1]:
                arr[j], arr[j+1] = arr[j+1], arr[j]
            
            plt.pause(0.5)  # 暂停以更新显示
                
    return arr

# 使用示例
arr = np.random.randint(1, 100, 10)
result = bubble_sort_jupyter(arr.copy())
print("原始数组:", arr)
print("排序后数组:", result)

二、选择排序

1. 原理:
   选择排序是一种简单的排序算法。它每次从未排序的部分选择最小(或最大)元素,将其与未排序部分的第一个元素交换位置,逐步将数据分成已排序和未排序两部分,直到整个数组有序。

2. 基本流程:

  • 从未排序部分选择最小的元素。 将该元素与未排序部分的第一个元素交换。
  • 将已排序部分的边界向右移动一位。
  • 重复以上过程,直到所有元素都排好序。

3. 实时演示:
在这里插入图片描述

4. 性能分析:
(1)时间复杂度:

  • 时间复杂度较高,尤其是对大规模数据。
  • 最好情况:O(n^2)
  • 最坏情况:O(n^2)
  • 平均情况:O(n^2)

(2)空间复杂度:

  • O(1),同样是原地排序。

5. 代码(包含上述实时演示):

# 选择排序
import numpy as np
import matplotlib.pyplot as plt
from IPython.display import display, clear_output

def selection_sort_jupyter(arr):
    fig, ax = plt.subplots(figsize=(8,4))
    n = len(arr)
    
    for i in range(n):
        min_idx = i
        ax.clear()
        colors = ['skyblue']*n
        colors[i] = 'red'
        
        for j in range(i+1, n):
            #------------------------------这一部分仅仅用于实时可视化--------------------------------
            # 更新比较颜色
            colors[j] = 'lime'
            ax.bar(range(n), arr, color=colors)
            ax.set_title(f'选择排序 | 扫描中... 当前最小值: {arr[min_idx]}')
            
            clear_output(wait=True)
            display(fig)
            plt.close(fig)
            
            colors[j] = 'skyblue'  # 恢复颜色

            #------------------------------以上代码仅仅用于实时可视化--------------------------------

            #------------------------------以下代码是实际的快速排序逻辑------------------------------
            if arr[j] < arr[min_idx]:
                min_idx = j

            plt.pause(0.5)  # 暂停以更新显示
            
        # 显示最终交换
        colors[i], colors[min_idx] = 'red', 'lime'
        ax.bar(range(n), arr, color=colors)
        ax.set_title('将最小值调换到头部!')
        
        clear_output(wait=True)
        display(fig)
        plt.close(fig)
        
        arr[i], arr[min_idx] = arr[min_idx], arr[i]

        plt.pause(0.5)  # 暂停以更新显示
    
    return arr

# 使用示例
arr = np.random.randint(1, 100, 10)
result = selection_sort_jupyter(arr.copy())
print("原始数组:", arr)
print("排序后数组:", result)

三、快速排序

1. 原理:
   快速排序是一种“分治法”策略的排序算法。它通过一个枢轴(pivot)将数组分为两个子数组,其中一个子数组的元素都比枢轴小,另一个子数组的元素都比枢轴大。然后递归地对这两个子数组进行快速排序,最终整个数组排好序。

2. 基本流程:

  • 选择一个元素作为枢轴,通常选择第一个元素、最后一个元素或中间元素。
  • 将数组分成两个子数组:一个包含比枢轴小的元素,另一个包含比枢轴大的元素。
  • 对这两个子数组分别递归执行快速排序。
  • 当子数组的大小为1或0时,递归结束。

3. 实时演示:
在这里插入图片描述

4. 性能分析:
(1)时间复杂度:

  • 快速,尤其对于大规模数据,平均情况下时间复杂度较低。
  • 最好情况:O(n log n),当枢轴总是能平衡分割数组时。
  • 最坏情况:退化为O(n^2),当选择的枢轴总是数组的最大或最小元素时(比如已经排序好的数组)。
  • 平均情况:O(n log n)。

(2)空间复杂度:

  • O(log n),因为递归过程中栈的深度是O(log n)。

5. 代码(包含递归演示):

import numpy as np

def quick_sort(arr, depth=0):
    # 如果数组长度小于等于1,直接返回数组(已经排好序)
    if len(arr) <= 1:
        return arr
    else:
        # 选择基准元素,通常选择中间元素
        pivot = arr[len(arr) // 2]
        
        # 使用numpy的布尔索引来划分出比基准小、比基准大的两个子数组
        left = arr[arr < pivot]
        middle = arr[arr == pivot]
        right = arr[arr > pivot]
        
        # 打印当前递归的深度和数组的状态
        print(f"{'  ' * depth}[{' '.join(map(str, arr))}] -> [{ ' '.join(map(str, left)) }] [{ ' '.join(map(str, middle)) }] [{ ' '.join(map(str, right)) }]")

        # 递归对左边和右边进行排序
        return np.concatenate([quick_sort(left, depth + 1), middle, quick_sort(right, depth + 1)])

# 使用示例
arr = np.random.randint(1, 100, 10)
print("原始数组:", arr)
result = quick_sort(arr.copy())
print("排序后数组:", result)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值