一、冒泡排序(Bubble Sort)
1. 算法原理
冒泡排序,重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢"浮"到数列的顶端。
具体步骤为:
(1)比较相邻的元素。如果第一个比第二个大,就交换位置。
(2)重复步骤(1),比较每一对相邻元素,直至最后一对,末尾的元素即为最大的数。
(3)重复以上步骤一次,又可以确定倒数第二个元素。
(4)持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
2. 时间复杂度
(1)最优时间复杂度O(n)
(2)最坏时间复杂度为O(n^2)
3. 冒泡排序算法是稳定的
实例:
def bubble_sort(alist):
"""冒泡排序实现"""
n = len(alist)
for j in range(n-1): # j表示内循环需要执行的次数,共n-1次
count = 0 # 为了降低时间复杂度,引入计数变量count
for i in range(0, n-1-j): # 内循环每完成一次,需要比较的数就减少一个,第一遍n-1次,第二遍n-2次,。。。
if alist[i] > alist[i + 1]:
alist[i], alist[i + 1] = alist[i + 1], alist[i]
count += 1 # 如果发生了位置交换,count加1
if count == 0: # 如果count没有变化,则说明已经是顺序排列,直接返回
return
if __name__ == "__main__":
a = [45, 34, 25, 12, 20, 10]
print("原序列为:", a)
bubble_sort(a)
print("排序后的序列为:", a)
二、选择排序(Selection Sort)
1. 算法原理
每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完。
2. 时间复杂度
(1)最优时间复杂度O(n^2)
(2)最坏时间复杂度为O(n^2)
3. 选择排序算法是不稳定的
实例:
def selection_sort(alist):
"""选择排序法"""
for j in range(0, len(alist)-1): # j表示每次内循环的起始位置,0~n-2
min_index = j # 暂认为每次内循环起始位置元素都是最小的
for i in range(j+1, len(alist)): # 表示内循环中在起始位置之后,需要和起始元素比较大小的元素的位置
if alist[min_index] > alist[i]: # 找到每次内循环中最小的元素,记录其位置
min_index = i
alist[j], alist[min_index] = alist[min_index], alist[j] # 交换每次内循环中的最小元素和起始位置元素
if __name__ == "__main__":
a = [45, 34, 25, 12, 20, 10]
print("原序列为:", a)
selection_sort(a)
print("排序后的序列为:", a)
三、插入排序(Insertion Sort)
1. 算法原理
通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。插入排序在实现上,通常采用in-place排序(即只需用到的额外空间的排序),因而在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。
2. 时间复杂度
(1)最优时间复杂度O(n)
(2)最坏时间复杂度为O(n^2)
3. 插入排序算法是稳定的
实例:
def insertion_sort(alist):
"""插入排序法"""
for j in range(1, len(alist)): # j表示让第j个元素插入到其前面正确的位置,1~n-1
for i in range(j, 0, -1): # i表示j位置之前有序序列中元素的位置,即要比较的次数,从j开始直至1
if alist[i] < alist[i-1]: # 内层循环中,将第i个元素和它之前的所有元素比较并插入到正确的位置
alist[i-1], alist[i] = alist[i], alist[i-1]
else:
break # 降低时间复杂度
if __name__ == "__main__":
a = [45, 34, 25, 12, 20, 10]
print("原序列为:", a)
insertion_sort(a)
print("排序后的序列为:", a)
四、希尔排序(Shell Sort)
1. 算法原理
先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录"基本有序"时,再对全体记录进行依次直接插入排序。
2. 时间复杂度
(1)平均时间复杂度O(n^1.25)
(2)最坏时间复杂度为O(n^2)
3. 希尔排序算法是不稳定的
def shell_sort(alist):
"""希尔排序,与插入排序相似,当gap=1时就是插入排序"""
gap = int(input("请输入间距:"))
n = len(alist)
while gap > 0:
for j in range(gap, n): # j表示将第j个元素插入到正确的位置,gap~n-1
for i in range(j, 0, -gap): # i表示位置之前的有序序列中元素的位置,即要比较的次数,从j开始直至1
# 内层循环中,将第i个元素和它之前的所有元素比较并插入到正确的位置
if alist[i] < alist[i-gap]:
alist[i-gap], alist[i] = alist[i], alist[i-gap]
else:
break
gap //= 2 # 改变间距gap
if __name__ == "__main__":
a = [45, 34, 25, 12, 20, 10]
print("原序列为:", a)
shell_sort(a)
print("排序后的序列为:", a)
五、快速排序(Quick Sort)
1. 算法原理
快速排序使用分治法(Divide and conquer)策略来把一个序列(list)分为较小和较大的2个子序列,然后递归地排序两个子序列。
步骤为:
(1)挑选基准值:从数列中挑出一个元素,称为"基准"(pivot);
(2)分割:重新排序数列,所有比基准值小的元素摆放在基准前面,所有比基准值大的元素摆在基准后面(与基准值相等的数可以到任何一边)。在这个分割结束之后,对基准值的排序就已经完成;
(3)递归排序子序列:递归地将小于基准值元素的子序列和大于基准值元素的子序列排序。
注:递归到最底部的判断条件是数列的大小是零或一,此时该数列显然已经有序。选取基准值有数种具体方法,此选取方法对排序的时间性能有决定性影响。
2. 时间复杂度
(1)平均时间复杂度O(nlgn)
(2)最坏时间复杂度为O(n^2)
3. 快速排序算法是不稳定的
def quick_sort(alist, first, last):
"""快速排序"""
if first >= last: # 递归退出的条件
return
mid_value = alist[first] # 存储列表第一个值
low = first # 最左的游标
high = last # 最右的游标
while low < high: # low和high没有相遇时就一直循环
while low < high and alist[high] >= mid_value:
high -= 1 # 满足上述条件时,将high左移一个位置
alist[low] = alist[high] # 将high所指的元素赋值给low所指位置
while low < high and alist[low] < mid_value:
low += 1 # 满足上述条件时,将low右移一个位置
alist[high] = alist[low] # 将low所指的元素赋值给high所指位置
alist[low] = mid_value
# 递归调用quick_sort(),每一次递归,传入的列表是mid_value的左边和右边
quick_sort(alist, first, low-1)
quick_sort(alist, low+1, last)
if __name__ == "__main__":
a = [45, 34, 25, 12, 20, 10]
print("原序列为:", a)
quick_sort(a, 0, len(a)-1)
print("排序后的序列为:", a)
六、归并排序(Merge Sort)
1. 算法原理
归并排序是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并
2. 时间复杂度
(1)平均时间复杂度O(nlgn)
(2)最坏时间复杂度为O(nlgn)
3. 快速排序算法是稳定的
def merge_sort(alist):
"""归并排序"""
n = len(alist)
if n <= 1:
return alist
mid = n // 2 # 划分一半
left_list = merge_sort(alist[:mid]) # 归并排序后左边的新序列
right_list = merge_sort(alist[mid:]) # 归并排序后右边的新序列
left_pointer, right_pointer = 0, 0 # 分别给左右序列加一个起始游标
result = []
# 对左右序列合并排序
while left_pointer < len(left_list) and right_pointer < len(right_list):
if left_list[left_pointer] < right_list[right_pointer]:
result.append(left_list[left_pointer])
left_pointer += 1
else:
result.append(right_list[right_pointer])
right_pointer += 1
# 将左右序列剩下的元素全部添加到result中
result += left_list[left_pointer:]
result += right_list[right_pointer:]
return result # 返回一个新的序列
if __name__ == "__main__":
a = [45, 34, 25, 12, 20, 10]
print("原序列为:", a)
sorted_list = merge_sort(a)
print("排序后的序列为:", sorted_list)