排序算法
一种能将一串数据依照特定顺序进行排列的算法。排序算法在排序后能位置相等键值得原有秩序则称算法稳定。
1.冒泡排序——稳定
冒泡排序(Bubble Sort)算法重复遍历待排序序列,每次比较两个元素,如果它们的顺序错误就将它们进行交换。重复进行此过程,直到没有需要交换再的元素。之所以叫冒泡排序是因为随着排序算法的进行,较大元素会像一个个水中的气泡一样浮向序列末端。
算法具体流程(以开始左端,按升序排列为例):
- 从左端第一个元素开始,比较相邻元素大小,若第一个元素大于第二个元素就交换他们
- 对序列中的每一对元素重复上一步的工作,从第一对直到到最后一对,做完这一步最大元素会是序列最右端的元素,此时完成一次交换过程
- 按上述步骤,每一次交换过程会完成一个数的排序,重复n-1次交换过程即可完成整个排序
排序算法图解分析(一次交换过程):
def bubble_sort(alist): # 最坏时间复杂度:O(n^2) 最优时间复杂度:O(n)
"""冒泡排序"""
for j in range(0,len(alist)-1):
# 一次交换过程
exchange = False
for i in range(0,len(alist)-1-j):
if alist[i] > alist[i+1]: # 如果前一个元素大于后一个元素就交换他们,否则什么也不做
alist[i],alist[i+1] = alist[i+1],alist[i]
exchange = True
if exchange is False: # 优化:如果遍历过程没有发生交换则序列已经有序,退出排序算法
break
2.选择排序——不稳定
选择排序(Selection Sort),首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置。然后,再从剩余未排序元素中继续寻找最小(大)元素,放到已排序序列的末尾。以此类推,直到所有元素排序完毕。
排序过程图解(以升序,选择最大值为例):
def select_sort(alist):
"""选择排序"""
n = len(alist)
for j in range(n-1,0,-1):
max_index = j
for i in range(0,j): # i [0,1,...,n-2]
if alist[max_index] < alist[i]:
max_index = i
alist[max_index],alist[j] = alist[j],alist[max_index]
3.插入排序——稳定
插入排序(Insert Sort),通过构建有序序列,对于未排序数据,在已排序数据中从后向前扫描,找到相应的插入位置并插入。
#---------------------插入排序实现-----------------------#
def insert_sort(alist): # 最坏时间复杂度:O(n^2) 最优时间复杂度:O(n)
"""插入排序"""
n = len(alist)
for i in range(1,n):
while i>0:
if alist[i] < alist[i-1]:
alist[i],alist[i-1] = alist[i-1],alist[i]
i -= 1
else:
break
4.希尔排序
希尔排序(Shell Sort)是插入排序的一种,其排序的基本思想是将数组列在一个表中,并对列分别进行插入排序,重复这个过程,但每次用更长的列来进行。最后整个表就只有一列。将数组转换至表是为了更好的理解这一算法,算法本身还是使用数组进行排序。
希尔排序示意图:
#---------------------希尔排序实现-------------------------#
def shell_sort(alist): # 最坏时间复杂度:O(n^2) 最优时间复杂度:由
"""希尔排序"""
n = len(alist)
gap = n // 2 # 此处gap采用折半方式
while gap > 0:
for i in range(gap,n):
while i >= gap:
if alist[i] < alist[i-gap]:
alist[i],alist[i-gap] = alist[i-gap],alist[i]
i -= gap
else:
break
gap //= 2
5.快速排序——不稳定
快速排序(Qucik Sort)又称划分交换排序,通过一趟排序将序列数据分割为独立的两部分,其中一部分的所有数据都比另一部分的数据要小,然后再按此方法对这两部分数据分别进行快速排序,整个过程可以递归进行,以此达到整个数据变成有序序列。
步骤:(以升序排列,基准值为左边第一个元素为例)
- 选取序列第一个元素为基准值(mid_value)
- 定义两个游标分别为序列第一个元素索引(low)和最后一个元素索引(high),从high游标开始比较与基准值得关系,若high游标的元素大于等于基准值则游标向前移动一步,直到不符合前述条件就将high游标的元素赋值给low游标,然后将low游标的元素与基准值比较,若low游标的元素小于基准值,则将游标向后移动一步,直到不符合前述条件就将low游标的元素赋值给high游标,然后再比较high游标,如此往复直到两个游标重合,重合之处便是基准值在序列中的最后位置,此时基准值左侧的元素比基准值小,右侧元素比基准值大或相等。
- 递归的将小于基准值的子序列和大于基准值的子序列进行排序。当递归的子序列长度为1时便可结束递归。
具体实现代码如下:
#----------------------快速排序实现----------------------#
def quick_sort(alist,start,end): # 最坏时间复杂度:O(n^2) 最优时间复杂度:O(nlogn)
"""快速排序"""
if start >= end:
return
low = start
high = end
mid_value = alist[start]
while low < high:
while low < high and alist[high] >= mid_value:
high -= 1
alist[low] = alist[high]
while low < high and alist[low] < mid_value:
low += 1
alist[high] = alist[low]
alist[low] = mid_value
quick_sort(alist,start,low-1)
quick_sort(alist,low+1,end)
6.归并排序——稳定
归并排序的思想是先递归分解序列,再合并序列。将序列分解到最小也就是只有一个元素时,合并两个有序序列。合并的基本思路是比较两个数组的最前面的数,谁小就先取谁,取了后相应的位置往后移动一步,然后再比较,直到一个序列为空,最后把另一个序列的全部剩余元素直接追加到后面。
具体实现代码如下:(图片为递归过程函数调用示意图)
#----------------------归并排序实现--------------------------#
def merge_sort(alist): # 最坏时间复杂度=最优时间复杂度:O(nlogn)
"""归并排序"""
if len(alist) == 1:
return alist
n = len(alist)
num = n // 2
left = merge_sort(alist[:num])
right = merge_sort(alist[num:])
return merge(left,right)
def merge(left,right):
"""合并序列"""
result = []
l_index,r_index = 0,0
while l_index < len(left) and r_index < len(right):
if left[l_index] <= right[r_index]:
result.append(left[l_index])
l_index += 1
else:
result.append(right[r_index])
r_index += 1
result.extend(left[l_index:])
result.extend(right[r_index:])
return result