排序是编程学习中的“必修课”,在各种数据处理场景中应用广泛。今天我就带大家用 Python 实现三种经典排序算法——冒泡排序、快速排序、插入排序,并配合详细注释和讲解,帮你真正理解它们的底层原理。
1. 冒泡排序(Bubble Sort)
原理说明:
冒泡排序的核心思想是:相邻元素两两比较,把大的“冒”到后面。每完成一轮,最大的元素就被“冒”到最右边。
思维过程:
-
对整个列表进行多轮遍历;
-
每一轮遍历时,两两相邻元素比较,如果顺序不对就交换;
-
每一轮结束,最大的一个数“沉底”到正确位置(这是外层循环的作用)。
-
它的第二次循环最大范围取值的时间复杂度是 O(1) 。
为了方便大家理解代码运行的过程,在每一次循环结束之后展现本次排序结束等提示。可以复制代码到python中运行查看。
def B_sort(s):
k=len(s)
for i in range(k):
j=i-1
for j in range(0,k-i-1):# 把本次循环最大的一位数放在列表最后一个位置。并且下次的范围从后缩小一位
if s[j] >s[j+1]:
print("本次找到第",j+1,"位为[",s[j],"] 放到第",j+2,"位")
print("排序后",s)
s[j],s[j+1]=s[j+1],s[j]# 前后位置互换
print("排序后",s)
print()
print("---------第",i+1,"次排序结束--------")
return s
print(B_sort([5, 3, 8, 4, 2]))
下方是部分运行结果的呈现,将每一轮循环改变的数字位置都打印出来,观察数字的变化轨迹可以帮助更好的掌握对算法的理解。

解析:
for i in range(0,k-i-1)其中的k-i-1 ,那我们就来详细解读一下它:
- 我们要比较的是
arr[j]和arr[j + 1],为了避免索引越界,j + 1必须小于数组长度k,也就是j <= k - 2。 J的最大取值的时间复杂度是O(-(n+1)) - 但每一轮末尾第
i个及后面的元素是排好序的,不需要再比较,因此有效比较范围应缩小为j <= k - i - 2,即range(0, k - i - 1)。
Python实现:
def B_sort(input_list):
k=len(input_list)
for i in range(k): #以列表的长度为内层循环的次数
for j in range(0,k-i-1): #主要是为了限制j的上限以及相邻的元素进行比较
if input_list[j] > input_list[j+1]:
input_list[j], input_list[j+1] =input_list[j+1],input_list[j]
return input_list
print(B_sort([5,4,3,2,1]))
2. 快速排序(Quick Sort)
原理说明:
快速排序采用的是:选定一个“基准数”,将比它小的放左边,大的放右边,再分别对左右两边递归排序。
思维过程:
-
选一个基准数
pivot -
通过一次
while划分数组:-
把小的放左边
-
把大的放右边
-
-
然后:
-
对左边再进行“同样的排序” (也就是递归调用。在函数里面“调用自己”,避免写重复的while语句)
-
对右边也进行“同样的排序”
-
Python实现:
def quick_sort(arr,start,end):
if start >= end:
return
pivot=arr[start] # 把最左边的第一个数当成基准数(拿出)
low =start #low是数组最小下标
high=end #high是数组arr的最大下标
# 最左边的数被拿走了,那么就从右边找一个小于基准的数放上
while low <high: #外部循环是用来控制一次划分基准左右两类的总循环,只要high ==low就进行下一次
while low <high and arr[high] >= pivot: #只要 arr[high] 的值比 pivot 大或相等,我们就继续往左走(high -= 1),跳过它。
high -=1
arr[low]=arr[high] #如果一次没有小于pivot的数就进行从右往左找,找到后,再从左往右找
while low < high and arr[low] <= pivot:
low +=1
arr[high] =arr[low]
arr[low]=pivot
quick_sort(arr,0,high-1) #左区间递归
quick_sort(arr,low+1, end) #右区间递归
li=[8,2,46,7,3,4,9,1]
quick_sort(li,0,len(li)-1)
print(li)
解析:
while low < high and arr[high] >= pivot: high -= 1 arr[low] = arr[high]
这部分的意思是:如果满足low < high and arr[high] >= pivot,那么high = high -1 进行执行while,不满足while条件时,就输出arr[low] = arr[high]。下面的while语句也是类似的意思。
li = [8,2,46,7,3,4,9,1]
quick_sort(li,0,len(li)-1)
print(li)
3.插入排序(Insertion Sort)
原理说明:
每次从未排序部分拿一个数插入到前面已排好序的部分中。
核心思想:
把整个数组看作“已排序”和“未排序”两部分,每次从未排序区取出一个元素,向前比较并插入到合适的位置,使前半部分始终保持有序。
Python实现
# 插入排序:将数字不断插入左边有序的部分中
def i_sort(nums):
li = len(nums)
for i in range(1, li): # 第一个数字默认有序,所以从第二个数字开始(下标为1)
word = nums[i] # 当前要插入的值
j = i - 1 # j是指针,从当前元素前一个位置开始向左寻找插入位置(反向循环)
while j >= 0 and nums[j] > word: # word 反向循环比较,如果前面的数比当前值大,就向后移动一位,为插入留出空位
nums[j + 1] = nums[j] # 将前一位的值赋值到后一位
j -= 1 # j 反向循环,向前继续比较前面的数
nums[j + 1] = word # 放在比word小的数后
return nums
s = [23, 22, 44, 11, 66, 12, 13, 16]
i_sort(s)
print(s)
解析:
1.for i in range(1,li)
外层循环查询列表的范围下标是递增的, 但是内部while语句中数值的比较是反向循环依次比较的。
4.补充内容
时间复杂度:衡量算法运行时间随输入规模增长的计算次数,反映程序处理大数据时的效率。
| 时间复杂度 | 含义 | 举个例子 |
|---|---|---|
| O(1) | 常数时间,不随输入变化 | 访问数组某个下标、判断奇偶等 |
| O(n) | 线性时间,输入越大时间越长 | 遍历一个列表 |
| O(n log n) | 近线性时间,比 n 慢但比 n² 快 | 快速排序 |
| O(n²) | 平方时间,数据多时急剧增长 | 冒泡排序、选择排序 |
空间复杂度:衡量算法在运行过程中额外占用的内存大小,不包括输入数据本身
原地排序:不使用额外数组。
稳定性:相同元素排序后相对位置是否改变。
举例:
[(小藕, 80), (李同, 80), (王谋, 90)]
按照成绩排序:
-
如果是稳定排序 ➜
[(小藕, 80), (李同, 80), (王谋, 90)] -
如果是不稳定排序 ➜
[(李同, 80), (小藕, 80), (王谋, 90)]
不稳定排序中小藕和李同的相同分数先后顺序就发生了改变。
| 排序算法 | 平均时间复杂度 | 空间复杂度 | 是否原地排序 | 是否稳定排序 |
|---|---|---|---|---|
| 冒泡排序 | O(n²) | O(1) | 是 | 是 |
| 插入排序 | O(n²) | O(1) | 是 | 是 |
| 快速排序 | O(n log n) | O(log n) | 是 | 否 |
总览:
小结
冒泡排序逻辑简单但效率低,更多用于算法入门练习,插入排序在小规模或近乎有序的数据中表现稳定,可作为其他排序算法的优化补充,而快速排序凭借较高的平均效率,成为处理大规模无序数据的常用选择。根据不同场景灵活选择排序算法,才能在实际开发中兼顾性能与效果。
如果你觉得这篇文章对你有帮助,欢迎点赞、收藏、转发~ 比心
有任何排序算法相关问题,欢迎留言交流!
18万+

被折叠的 条评论
为什么被折叠?



