从零开始的蓝桥杯打怪升级-冒泡排序

20 阅读3分钟

冒泡序列

从零开始遍历整个列表,一次比较两个元素,如果它们的顺序错误就把它们交换过来。遍历数列的工作是重复地进行直到没有再需要交换的元素,这意味着数列已经排序完成。

时间复杂度

最坏的时间复杂度:O(n²)
最好的时间复杂度:O(n)
平均时间复杂度:O (n²)

空间复杂度

冒泡排序即是原地排序
空间复杂度:O(1)
原地排序(In-place Sort):排序算法在执行过程中,不需要额外开辟和原数组规模相当的辅助空间,仅使用常数级(O (1))的额外空间来完成排序,所有操作都在原数组 / 数列上直接进行。

冒泡序列的特点

  • 优点:实现简单,稳定排序(相等元素的相对顺序不会改变)。

  • 缺点:效率低,适合小规模数据或基本有序的数据。

接下来我们进入python来实践一下

这部分是前置代码

import time
import tracemalloc
from typing import Callable, List
import random


class RandomList():
    def __init__(self):
        self.random_list = []

    def append(self, size, space):
        size = random.randint(1, 99)
        self.random_list.clear()
        for i in range(size):
            self.random_list.append(random.randint(0, space))
# 设置随机数列

class BaseSort():
    def __init__(self):
        self.compare_count = 0
        self.swap_count = 0
        self.start_time = 0.0
        self.end_time = 0.0
        self.cost_time = 0.0
        self.peak = 0
        self.peak_kb = 0

    def compare(self, a: int, b: int) -> int:
        self.compare_count += 1
        return a - b
    #     回归一个a - b值 通过判断大于零或者是小于零来进行处理

    def swap(self, arr: List, i: int, j: int):
        self.swap_count += 1
        arr[i] , arr[j] = arr[j] , arr[i]

    def time_it(self, func: Callable, *args, **kwargs) -> tuple:
        self.start_time = time.perf_counter()
        result = func(*args, **kwargs)
        self.end_time = time.perf_counter()
        self.cost_time = self.end_time - self.start_time
        return result, self.cost_time
        # 计算时间复杂度

    def space_it(self, func: Callable, *args, **kwargs) -> tuple:
        tracemalloc.start()
        result = func(*args, **kwargs)
        current, self.peak = tracemalloc.get_traced_memory()
        tracemalloc.stop()
        return result, self.peak / 1024
        # 计算空间复杂度

    def time_space_it(self, func: Callable, *args, **kwargs) -> tuple:

        self.start_time = time.perf_counter()
        tracemalloc.start()

        result = func(*args, **kwargs)

        self.end_time = time.perf_counter()
        current, self.peak = tracemalloc.get_traced_memory()
        tracemalloc.stop()
        # 计算结果
        self.cost_time = self.end_time - self.start_time
        self.peak_kb = self.peak / 1024
        return result, self.cost_time, self.peak_kb
    # 同时计算时间复杂度和空间复杂度

    def reset(self):
        self.compare_count = 0
        self.swap_count = 0
        self.start_time = 0.0
        self.end_time = 0.0
        self.cost_time = 0.0
        self.peak = 0
        self.peak_kb = 0

    def print_out(self):

        # 格式化输出
        print(f"数列经过{self.compare_count}次比较,经过了{self.swap_count}次交换")
        print(f"计算时间为{abs(self.cost_time):.6f}")
        print(f"空间复杂度为{self.peak_kb:.2f} KB")

基本的冒泡排序

def BubbleSort(bs: BaseSort, rl1: RandomList):
    print(f"原数列为:{rl1.random_list}")
    rl_size = len(rl1.random_list)
    for i in range(rl_size):
        for j in range(rl_size - i - 1):
        # 内层循环为rl_size - i - 1 ,防止数组越界到rl_size - 1之内
            if bs.compare(rl1.random_list[j], rl1.random_list[j+1]) > 0:
                bs.swap(rl1.random_list, j, j+1)
    print(f"经过冒泡排序后数列为:{rl1.random_list}")

通过两个for循环对列表进行遍历
再经过if判断compare(rl.random_list[j])和rl1.random_list[j+1]的大小

  • 问题:无论是否已经完成排序,均要完成遍历后才能完成排序

基本冒泡排序.png

优化后的冒泡序列

def BubbleSort_New(bs: BaseSort, rl2: RandomList):
    print(f"原数列为:{rl2.random_list}")
    rl_size = len(rl2.random_list)
    for i in range(rl_size):
        is_sorted = True
        for j in range(rl_size - i - 1):
            # 内层循环为rl_size - i - 1 ,防止数组越界到rl_size - 1之内
            if bs.compare(rl2.random_list[j], rl2.random_list[j + 1]) > 0:
                bs.swap(rl2.random_list, j, j + 1)
                is_sorted = False
        if is_sorted:
            break
    print(f"经过冒泡排序后数列为:{rl2.random_list}")

优化后的冒泡排序增加了一个判断
if is_sorted = Ture则代表该列表已排序完成
则break

优化冒泡序列.png

总结

优化冒泡排序和基本冒泡排序主要比较次数
交换次数则不变
因为是原地排序则内存占用基本不变
运行时间则明显缩短

希望我的文章可以对你有所帮助QwQ