参考资料:Problem Solving with Algorithms and Data Structures
附此文高清PDF版本Github地址:PDF版本
简单介绍一下六种常用的排序算法以及Python实现:
冒泡排序(Bubble Sort)、选择排序(Selection Sort)、插入排序(Insert Sort)、希尔排序(Shell Sort)、归并排序(Merge Sort)、快速排序(Quick Sort)。
一、 冒泡排序(Bubble Sort)
1、原理图解
2、Python实现
1) 算法实现
def bubble_sort(a_list):
for pass_num in range(len(a_list)-1,0,-1):
for i in range(pass_num):
if a_list[i] > a_list[i+1]:
temp = a_list[i]
a_list[i] =a_list[i+1]
a_list[i+1] = temp
2)测试程序
a_list = [54,26,93,17,77,31]
bubble_sort(a_list)
print(a_list)
3)测试结果
[17, 26, 31, 54, 77, 93]
3、算法复杂度
4、算法改进
思路:每次循环时,进行检查,该循环是否有交换发生。如果没有交换发生,说明排序已经完成,无须进行后续的循环。
def short_bubble_sort(a_list):
exchanges = True
pass_num = len(a_list)-1
while pass_num > 0 and exchanges:
# 如果此次循环未发生互换,则exchanges = False
exchanges = False
for i in range(pass_num):
if a_list[i] > a_list[i+1]:
exchanges = True
temp = a_list[i]
a_list[i] = a_list[i+1]
a_list[i+1] = temp
pass_num = pass_num - 1
# 查看代码在第几次循环停止
print(len(a_list)-1-pass_num)
a_list = [20,30,40,90,50,60,70,80,100,110]
short_bubble_sort(a_list)
print(a_list)
二、 选择排序(Selection Sort)
1、原理图解
2、Python实现
1) 算法实现
def selection_sort(a_list):
for fill_slot in range(len(a_list)-1, 0,-1):
pos_of_max = 0
for location in range(1,fill_slot+1):
if a_list[location] > a_list[pos_of_max]:
pos_of_max = location
temp = a_list[fill_slot]
a_list[fill_slot] = a_list[pos_of_max]
a_list[pos_of_max] = temp
2)测试程序
a_list = [54,26,93,17,77,31]
selection_sort(a_list)
print(a_list)
3)测试结果
[17, 26, 31, 54, 77, 93]
3、算法复杂度
选择算法的时间复杂度仍为 O(n^2 ),但是与冒泡法相比,少了交换的步骤。
三、 插入排序(Insert Sort)
1、原理图解
2、Python实现
1) 算法实现
def insertion_sort(a_list):
for index in range(1,len(a_list)):
current_value = a_list[index]
position = index
# 寻找插入的位置
while position > 0 and a_list[position - 1] > current_value:
a_list[position] = a_list[position - 1]
position = position - 1
a_list[position] = current_value
2)测试程序
a_list = [54,26,93,17,77,31]
insertion_sort(a_list)
print(a_list)
3)测试结果
[17, 26, 31, 54, 77, 93]
3、算法复杂度
算法复杂度仍为 O(n^2 ),但是在最理想的情况下,只需要进行n-1次对比,n为列表中元素的个数。同理,在一个排序性比较明显的原列表中,使用插入排序法更加快捷。
四、 希尔排序(Shell Sort)
1、原理图解
2、Python实现
1)算法实现
def shell_sort(a_lsit):
# 分为 n//2个子列表,分别进行插入排序
sublist_count = len(a_lsit) // 2
while sublist_count > 0 :
for start_positon in range(sublist_count):
gap_insertion_sort(a_lsit,start_positon,sublist_count)
print("After increments of size",sublist_count,"The list is", a_lsit)
# 将 n//个子列表组合成 n//4,n//8,n//16...1个列表进行插入排序
sublist_count = sublist_count // 2
def gap_insertion_sort(a_list, start, gap):
for i in range(start+gap,len(a_list),gap):
current_value = a_list[i]
position = i
while position >= gap and a_list[position-gap] > current_value:
a_list[position] = a_list[position-gap]
position = position - gap
a_list[position] = current_value
2)测试程序
a_list = [54,26,93,17,77,31,44,55,20]
shell_sort(a_list)
print(a_list)
3)测试结果
After increments of size 4 The list is [20, 26, 44, 17, 54, 31, 93, 55, 77]
After increments of size 2 The list is [20, 17, 44, 26, 54, 31, 77, 55, 93]
After increments of size 1 The list is [17, 20, 26, 31, 44, 54, 55, 77, 93]
[17, 20, 26, 31, 44, 54, 55, 77, 93]
3、算法复杂度
直觉上来看希尔排序法似乎不优于插入排序法,因为希尔排序法频繁使用了插入排序法。但实际上,希尔排序法的每一次插入排序都比较简单,因为子列表比较短。另外,前面的步骤生成了一个排序性比较好的新列表,减少了插入排序的计算次数。这使得最后一步插入排序十分高效。
与前面的排序方法相比,Shell的算法复杂度在O(n )与O(n^2)之间,与分组方式有关,数学计算不设讨论。
五、 归并排序(Merge Sort)
1、原理图解
2、Python实现
1)算法实现
def merge_sort(a_list):
print("Splitting ",a_list)
if len(a_list) > 1 :
mid = len(a_list)//2
left_half = a_list[:mid]
right_half = a_list[mid:]
# 递归
merge_sort(left_half)
merge_sort(right_half)
i = 0
j = 0
k = 0
# 组内排序
while i < len(left_half) and j < len(right_half):
if left_half[i] < right_half[j]:
a_list[k] = left_half[i]
i = i + 1
else:
a_list[k] = right_half[j]
j = j + 1
k = k + 1
while i < len(left_half):
a_list[k] = left_half[i]
i = i + 1
k = k + 1
while j < len(right_half):
a_list[k] = right_half[j]
j = j + 1
k = k + 1
print("Merging ",a_list)
2)测试程序
a_list = [54,26,93,17,77,31,44,55,20]
merge_sort(a_list)
print(a_list)
3)测试结果
Splitting [54, 26, 93, 17, 77, 31, 44, 55, 20]
Splitting [54, 26, 93, 17]
Splitting [54, 26]
Splitting [54]
Merging [54]
Splitting [26]
Merging [26]
Merging [26, 54]
Splitting [93, 17]
Splitting [93]
Merging [93]
Splitting [17]
Merging [17]
Merging [17, 93]
Merging [17, 26, 54, 93]
Splitting [77, 31, 44, 55, 20]
Splitting [77, 31]
Splitting [77]
Merging [77]
Splitting [31]
Merging [31]
Merging [31, 77]
Splitting [44, 55, 20]
Splitting [44]
Merging [44]
Splitting [55, 20]
Splitting [55]
Merging [55]
Splitting [20]
Merging [20]
Merging [20, 55]
Merging [20, 44, 55]
Merging [20, 31, 44, 55, 77]
Merging [17, 20, 26, 31, 44, 54, 55, 77, 93]
[17, 20, 26, 31, 44, 54, 55, 77, 93]
3、算法复杂度
归并排序的时间复杂度由拆分+合并两部分组成。拆分为二分法,时间复杂度为O(log n);合并每层代价为n,共有log n层,的时间复杂度为O(n log n)。相加的时间复杂度仍为O(n log n)。
六、快速排序(Quick Sort)
1、原理图解
2、Python实现
1)算法实现
def quick_sort(a_lsit):
quick_sort_helper(a_lsit,0,len(a_lsit)-1)
def quick_sort_helper(a_list,first,last):
# 递归2分、4分、8分...
if first < last:
split_point = partition(a_list,first,last)
quick_sort_helper(a_list,first,split_point - 1)
quick_sort_helper(a_list,split_point + 1, last)
def partition(a_list,first,last):
pivot_value = a_list[first]
left_mark = first + 1
right_mark = last
done = False
while not done:
# 寻找左边标记暂停点
while left_mark <= right_mark and a_list[left_mark] <= pivot_value:
left_mark = left_mark + 1
# 寻找右边标记暂停点
while a_list[right_mark] >=pivot_value and right_mark >= left_mark:
right_mark = right_mark - 1
# 右边标记超过左边标记,停止
if right_mark < left_mark:
done = True
# 交换左右标记暂停点的值
else:
temp = a_list[left_mark]
a_list[left_mark] = a_list[right_mark]
a_list[right_mark] = temp
# 将右边标记的值与分离点的值交换
temp = a_list[first]
a_list[first] = a_list[right_mark]
a_list[right_mark] = temp
return right_mark
2)测试程序
a_list = [54,26,93,17,77,31,44,55,20]
quick_sort(a_list)
print(a_list)
3)测试结果
[17, 20, 26, 31, 44, 54, 55, 77, 93]
3、算法复杂度
一般来说,分离点的数值为列表中的中值,此时类似于二分法,一共需要共有log n层,每层代价为n,时间复杂度为O(n log n)。不过在最糟糕的情况下,分离点的数值处于列表的最左边或者最右边,此时时间复杂度为O(n^2 )。
七、总结