基于Python实现快速排序、冒泡排序、选择排序
快速排序
复杂度:快速排序是不稳定的排序算法,最坏的时间复杂度是 O(n2),最好的时间复杂度是(nlogn)
方法一
代码:
import random
def quick_sort(lst):
if len(lst) < 2: # 递归的基例或出口,即列表长度为1时返回自身
return lst
else:
pivot = lst[0] # 以第一个元素作为参考
less = [x for x in lst[1:] if x <= pivot] # 得到小于等于参考元素的列表
greater = [x for x in lst[1:] if x > pivot] # 得到大于参考元素的列表
return quick_sort(less) + [pivot] + quick_sort(greater) # 对于上面得到的两个列表继续递归排序
if __name__ == '__main__':
list1 = list(range(10))
random.shuffle(list1)
print("排序前: ", list1)
print("排序后: ", quick_sort(list1))
结果:
排序前: [7, 6, 0, 9, 8, 5, 2, 1, 3, 4]
排序后: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
方法二
讲道理这个方法比较难以理解,实在理解不了可以忽略不管。
代码:
import random
def quick_sort(lst, start, end):
"""
找到基准值, 将小于基准值的元素放前面, 大于基准值的元素放后面, 两头向中间收缩, 重叠处就是基准值的实际位置
"""
if start >= end:
return
pivot = lst[start] # 基准值, 用于比对
low = start
high = end
while low < high:
# high不断向左收缩,从右往左找到第一个小于基准值的元素
while low < high and lst[high] >= pivot:
high -= 1
lst[low] = lst[high] # 将上述找到的元素与low互换位置,将比pivot小的放到前面去
# 然后low不断向右收缩,找到第一个大于基准值的元素
while low < high and lst[low] < pivot:
low += 1
lst[high] = lst[low] # 将上述找到的元素与high互换位置,将比pivot大的放到后面去
# 根据while条件,循环结束后,low与high收缩到同一个位置,而这个位置就是pivot的真实位置
lst[low] = pivot
# 至此,我们为pivot找到了正确的位置,同时pivot左侧的元素都比pivot小,右侧都比pivot大,但两侧还不是有序的
# 对基准值左侧排序
quick_sort(lst, start, low - 1)
# 对基准值右侧排序
quick_sort(lst, low + 1, end)
if __name__ == '__main__':
list1 = list(range(10))
random.shuffle(list1)
print("排序前: ", list1)
quick_sort(list1, 0, len(list1) - 1)
print("排序后: ", list1)
结果:
排序前: [8, 3, 2, 0, 9, 4, 6, 1, 7, 5]
排序后: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
方法三
这是在菜鸟上看到的方法,同样是原地排序,思想跟方法二其实类似,但实现方法略有不同
代码:
def partition(arr, low, high):
"""在arr列表的low-high索引范围内,把比pivot小的元素放到pivot前面,同时给pivot(这里是拿high对应的元素)找到正确的位置,那么比pivot大的元素就在pivot后面了"""
i = low # 预置pivot的正确位置
pivot = arr[high]
for j in range(low, high):
# 遍历low-high范围,找出有多少个元素比pivot小,同时把这些元素交换位置,放到前面,那么i也就对应着pivot实际的位置
if arr[j] <= pivot:
arr[i], arr[j] = arr[j], arr[i]
i = i + 1
# 到这里i也就对应着pivot实际的位置,然后将pivot(high对应的元素)与i互换
arr[i], arr[high] = arr[high], arr[i]
return i
# 快速排序函数
def quickSort(arr, low, high):
if low < high:
pi = partition(arr, low, high) # pi是原来arr里high元素对应的正确位置,执行完后已经交换位置
# 经过这一步,arr已经经过一次粗略排序:pi之前的都是小于arr[pi]的元素,pi之后都是大于arr[pi]的元素,但pi前后的元素还未排序
quickSort(arr, low, pi - 1) # 针对pi之前的元素再次执行快排
quickSort(arr, pi + 1, high) # 针对pi之后的元素再次执行快排
arr = [10, 7, 8, 9, 1, 5]
n = len(arr)
quickSort(arr, 0, n - 1)
print("排序后的数组:")
for i in range(n):
print("%d" % arr[i]),
冒泡排序
复杂度:时间复杂度为 O(n^2),且排序是稳定的
代码:
import random
def bubble_sort(lst):
"""
每次遍历列表,都将最大的数往后移动
:param lst:
:return:
"""
for i in range(len(lst)-1, 0, -1):
for j in range(i): # 遍历列表前n个元素,根据i依次减小
if lst[j] > lst[j+1]:
lst[j], lst[j+1] = lst[j+1], lst[j] # 如果相邻两个元素的顺序不正确,则替换它们的位置
if __name__ == '__main__':
list1 = list(range(10))
random.shuffle(list1)
print("排序前: ", list1)
bubble_sort(list1)
print("排序后: ",list1)
结果:
排序前: [3, 9, 2, 6, 1, 4, 8, 0, 5, 7]
排序后: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
选择排序
复杂度:时间复杂度为 O(n^2),且排序是稳定的
代码:
import random
def select_sort(lst):
"""
每次遍历列表,都选择列表中最小的元素,将其移到列表最前面
:param lst:
:return:
"""
for i in range(len(lst)):
smallest_index = i # 每次遍历列表的起始位置为i,同时假定i就是最小的元素索引
for j in range(i, len(lst)): # 以i为起始值,即忽略前一次遍历选择出来的最小元素
if lst[smallest_index] > lst[j]:
smallest_index = j # 如果列表中有比smallest_index对应元素更小的元素,则将这个更小元素的索引赋值给smallest_index
# 遍历后的smallest_index对应的元素就是最小的元素,将其移动到本次遍历的起始位置
lst[i], lst[smallest_index] = lst[smallest_index], lst[i]
if __name__ == '__main__':
list1 = list(range(10))
random.shuffle(list1)
print("排序前: ", list1)
select_sort(list1)
print("排序后: ", list1)
结果:
排序前: [3, 7, 6, 4, 0, 9, 2, 8, 5, 1]
排序后: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
桶排序
桶排序的基本思想是将一个数据表分割成许多 buckets,然后每个 bucket 各自排序。也是典型的 divide-and-conquer 分而治之的策略。
桶排序是稳定的,而且桶排序非常快,但是同时也非常耗空间,基本上是最耗空间的一种排序算法
代码:
import random
def bucket_sort(lst):
max_num = max(lst) # 获得最大的元素
bucket = [0] * (max_num+1) # 根据最大元素建立n个桶
for i in lst:
bucket[i] += 1 # 遍历列表每一个元素,在对应位置的桶中增加计数
sorted_list = [] # 创建结果列表
for j in range(len(bucket)):
if bucket[j] != 0:
for k in range(bucket[j]):
sorted_list.append(j) # 按顺序遍历每个桶,从桶中取数放到结果列表中
return sorted_list
if __name__ == '__main__':
lst1 = [i for i in range(10)]
random.shuffle(lst1)
print("排序前: ", lst1)
print("排序后: ", bucket_sort(lst1))
结果:
排序前: [9, 2, 8, 4, 3, 6, 0, 1, 7, 5]
排序后: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
插入排序
插入排序的基本思想是本质是从第二个开始取数,跟前面的比较并插入正确的位置
插入排序是稳定的,时间复杂度为 O(n)到O(n^2)
代码:
import random
def insert_sort(l):
for i in range(1, len(l)): # 从头开始
for j in range(i, 0, -1):
if l[j-1] > l[j]: # 一点一点往前插
l[j-1], l[j] = l[j], l[j-1]
else:
break # 排序从头开始,如果它没有前一个数大,那它就不需要改变位置
return l
if __name__ == '__main__':
lst1 = [i for i in range(10)]
random.shuffle(lst1)
print("排序前: ", lst1)
print("排序后: ", insert_sort(lst1))
结果:
排序前: [9, 2, 8, 4, 3, 6, 0, 1, 7, 5]
排序后: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]