冒泡序列
从零开始遍历整个列表,一次比较两个元素,如果它们的顺序错误就把它们交换过来。遍历数列的工作是重复地进行直到没有再需要交换的元素,这意味着数列已经排序完成。
时间复杂度
最坏的时间复杂度: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]的大小
- 问题:无论是否已经完成排序,均要完成遍历后才能完成排序
优化后的冒泡序列
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
总结
优化冒泡排序和基本冒泡排序主要比较次数上
交换次数则不变
因为是原地排序则内存占用基本不变
运行时间则明显缩短