Python基础排序算法
排序算法的稳定性
假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳定的;否则称为不稳定的。
我们举一个例子就好理解了:
假设有如下的元组:(4, 1) (3, 1) (3, 7) (5, 6)
按照每个元组的第一个位置的元素进行排序
排法1:(3, 1) (3, 7) (4, 1) (5, 6) 稳定,维持次序(3, 1)和(3, 7)的次序
排法2:(3, 7) (3, 1) (4, 1) (5, 6) 不稳定,次序被改变
冒泡排序
较为简单的排序算法之一,它重复地走访过要排序的元素列,依次比较两个相邻的元素,如果顺序(如从大到小、首字母从Z到A)错误就把他们交换过来。走访元素的工作是重复地进行,直到没有相邻元素需要交换,也就是说该元素列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端(升序或降序排列),就如同碳酸饮料中二氧化碳的气泡最终会上浮到顶端一样,故名“冒泡排序”。
冒泡排序是稳定的排序算法。
def bubble_sort(alist):
"""
冒泡排序
稳定的
最优时间复杂度:O(n^2)
最坏时间复杂度:O(n^2)
"""
n = len(alist)
for j in range(n-1):
for i in range(n-1-j):
if alist[i] > alist[i+1]:
alist[i], alist[i+1] = alist[i+1], alist[i]
if __name__ == '__main__':
alist=[1,23,5,1,2,3,45]
bubble_sort(alist)
print(alist)
选择排序
一种简单直观的排序算法。它的工作原理是:第一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,然后再从剩余的未排序元素中寻找到最小(大)元素,然后放到已排序的序列的末尾。以此类推,直到全部待排序的数据元素的个数为零。
选择排序是不稳定的排序算法。
def selection_sort(alist):
"""
选择排序
不稳定
最优时间复杂度:O(n^2)
最坏时间复杂度:O(n^2)
"""
n = len(alist)
for i in range(n):
index = 0
for j in range(1, n-i):
if alist[j] > alist[index]:
index = j
alist[index], alist[n-1-i] = alist[n-1-i], alist[index]
if __name__ == '__main__':
ls=[]
selection_sort(ls)
print(ls)
插入排序
插入排序,一般也被称为直接插入排序。对于少量元素的排序,它是一个有效的算法。插入排序是一种最简单的排序方法,它的基本思想是将一个记录插入到已经排好序的有序表中,从而一个新的、记录数增1的有序表。在其实现过程使用双层循环,外层循环对除了第一个元素之外的所有元素,内层循环对当前元素前面有序表进行待插入位置查找,并进行移动。
插入排序是稳定的排序算法。
第一种实现想法:
def insert_sort(alist):
"""
插入排序
稳定的
最优时间复杂度:O(n)
最坏时间复杂度:O(n^2)
平均情况:O(n^2)
插入排序:首先把数列第一个位置的的元素看成一个已排好序的数列,
然后分别遍历后面的元素,依次插入前面抽象出来的已排好序的数列即可。
"""
n = len(alist)
for i in range(1, n):
index = i
for j in range(i-1,-1,-1):
if alist[j] > alist[index]:
alist[index], alist[j] = alist[j], alist[index]
index = j
if __name__ == '__main__':
ls = [23,45,213,4,1,2,4]
insert_sort(ls)
print(ls)
另一种实现想法:
def insert_sort_2(alist):
"""
插入排序2
"""
n = len(alist)
for i in range(1,n):
while i-1 >= 0:
if alist[i-1] > alist[i]:
alist[i-1], alist[i] = alist[i], alist[i-1]
i -= 1
else:
break
if __name__ == '__main__':
ls = [2,3,4,345,35,1,32]
insert_sort_2(ls)
print(ls)
希尔排序
希尔排序(Shell’s Sort)是插入排序的一种又称“缩小增量排序”(Diminishing Increment Sort),是直接插入排序算法的一种更高效的改进版本。希尔排序是非稳定排序算法。该方法因 D.L.Shell 于 1959 年提出而得名。
希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至 1 时,整个文件恰被分成一组,算法便终止。
希尔排序是不稳定的排序算法。
def shell_sort(alist):
"""
希尔排序
最优时间复杂度:根据步长序列(gap)的不同而不同 (O(n^1.3) 根据数学计算得来)
最坏时间复杂度:O(n^2)
不稳定
平均情况:O(nlogn)~O(n^2)
"""
n = len(alist)
gap = n // 2
while gap:
for j in range(gap, n):
i = j
while i - gap >= 0:
if alist[i] < alist[i - gap]:
alist[i], alist[i - gap] = alist[i - gap], alist[i]
i -= gap
else:
break
gap //= 2
if __name__ == "__main__":
ls=[3,3,5,23,5,45,6,10]
shell_sort(ls)
print(ls)
快速排序
快速排序,计算机科学词汇,适用领域Python,c++等语言,是对冒泡排序算法的一种改进。
快速排序算法通过多次比较和交换来实现排序,其排序流程如下:
(1)首先设定一个分界值,通过该分界值将数组分成左右两部分。
(2)将大于或等于分界值的数据集中到数组右边,小于分界值的数据集中到数组的左边。此时,左边部分中各元素都小于分界值,而右边部分中各元素都大于或等于分界值。
(3)然后,左边和右边的数据可以独立排序。对于左侧的数组数据,又可以取一个分界值,将该部分数据分成左右两部分,同样在左边放置较小值,右边放置较大值。右侧的数组数据也可以做类似处理。
(4)重复上述过程,可以看出,这是一个递归定义。通过递归将左侧部分排好序后,再递归排好右侧部分的顺序。当左、右两个部分各数据排序完成后,整个数组的排序也就完成了。
快速排序是不稳定的排序算法。
def quick_sort(alist, first = 0, last = None):
"""
快速排序
最优时间复杂度:O(nlogn)
最坏时间复杂度:O(n^2)
不稳定
"""
if last == None:
last = len(ls) - 1
if first >= last:
return
mid_value = alist[first]
low = first
high = last
while high > low:
while high > low and alist[high] >= mid_value:
high -= 1
alist[low] = alist[high]
while high > low and alist[low] < mid_value:
low += 1
alist[high] = alist[low]
#当循环退出时,low = high
alist[high] = mid_value
quick_sort(alist, first, low - 1)
quick_sort(alist, low + 1, last)
if __name__ == '__main__':
ls = [23,4,1,2,2,34,53,65]
quick_sort(ls)
print(ls)
归并排序
归并排序是建立在归并操作上的一种有效,稳定的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。
归并操作的工作原理如下:
第一步:申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
第二步:设定两个指针,最初位置分别为两个已经排序序列的起始位置
第三步:比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
重复步骤3直到某一指针超出序列尾
将另一序列剩下的所有元素直接复制到合并序列尾
归并排序是稳定的排序算法。
def merge_sort(alist):
"""
归并排序
最优时间复杂度:O(nlogn)
最坏时间复杂度:O(nlogn)
稳定的
"""
n = len(alist)
if n <= 1:
return alist
mid = n // 2
left = merge_sort(alist[:mid])
right = merge_sort(alist[mid:])
return merge(left, right)
def merge(llist, rlist):
"""
合并
"""
ls = []
left = 0
right = 0
while left < len(llist) and right < len(rlist):
if llist[left] <= rlist[right]:
ls.append(llist[left])
left += 1
else:
ls.append(rlist[right])
right += 1
ls.extend(llist[left:])
ls.extend(rlist[right:])
return ls
if __name__ == '__main__':
ls = [2,4,5,6,23,3,5,7,7,2,1]
sorted_ls = merge_sort(ls)
print(sorted_ls)