排序算法
排序算法的稳定性
排序算法(Sorting algorithm)是一种能将一串数据依照特定顺序进行排列的一种算法。
稳定性:稳定排序算法会让原本有相等键值的纪录维持相对次序。也就是如果一个排序算法是稳定的,当有两个相等键值的纪录R和S,且在原本的列表中R出现在S之前,在排序过的列表中R也将会是在S之前。
当相等的元素是无法分辨的,比如像是整数,稳定性并不是一个问题。然而,假设以下的数对将要以他们的第一个数字来排序。
(4, 1) (3, 1) (3, 7)(5, 6)
在这个状况下,有可能产生两种不同的结果,一个是让相等键值的纪录维持相对的次序,而另外一个则没有:
(3, 1) (3, 7) (4, 1) (5, 6) #(维持次序)
(3, 7) (3, 1) (4, 1) (5, 6) #(次序被改变)
不稳定排序算法可能会在相等的键值中改变纪录的相对次序,但是稳定排序算法从来不会如此。不稳定排序算法可以被特别地实现为稳定。作这件事情的一个方式是人工扩充键值的比较,如此在其他方面相同键值的两个对象间之比较,(比如上面的比较中加入第二个标准:第二个键值的大小)就会被决定使用在原先数据次序中的条目,当作一个同分决赛。然而,要记住这种次序通常牵涉到额外的空间负担。
一、冒泡排序
游标从头走到尾,走一次,选出最大值93,第二次走,走到n-1,选出最大值77,以此类推
1.1 代码实现:
def bubble_sort(alist):
"""冒泡排序"""
n = len(alist)
for j in range(n-1):
for i in range(0,n-1-j):
if alist[i] > alist[i+1]:
alist[i],alist[i+1] = alist[i+1],alist[i]
# 我写的过程:先写内层循环(从头走到尾),再写外层循环(内层循环经历多少遍)
# i和j关系的确定:
# 第一次:i 0~n-2 (从第一个走到倒数第二个) range(0,n-1) j=0
# 第二次:i 0~n-3 (从第一个走到倒数第三个) range(0,n-1-1) j=1
# 第三次:i 0~n-4 (从第一个走到倒数第四个) range(0,n-1-2) j=2
# j=n i range(0,n-1-j)
if __name__ == "__main__":
li = [54,26,93,17,77,31,44,55,20]
print(li)
bubble_sort(li)
print(li)
1.2 时间复杂度
最优时间复杂度:O(n) (表示遍历一次发现没有任何可以交换的元素,排序结束。)
最坏时间复杂度:O(n^2)
稳定性:稳定
二、选择排序
选择排序(Selection sort)是一种简单直观的排序算法。它的工作原理如下。首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。
选择排序的主要优点与数据移动有关。如果某个元素位于正确的最终位置上,则它不会被移动。选择排序每次交换一对元素,它们当中至少有一个将被移到其最终位置上,因此对n个元素的表进行排序总共进行至多n-1次交换。在所有的完全依靠交换去移动元素的排序方法中,选择排序属于非常好的一种。
2.1 代码实现
# 举个例子,然后再写
# alist = [54,226,93,17,77,31,44,55,20]
# 0 1 2 3 4 5 6 7 8
# 54后面的所有数里挑一个最小的和54比,min = alist[3]; alist[0],alist[3] = alist[3],alist[0]
# alist = [17,226,93,54,77,31,44,55,20]
# 17后面的所有数里挑一个最小的和17比,min = alist[8],alist[1],alist[8] = alist[8],alist[1]
# alist = [17,20,93,54,77,31,44,55,226]
# …………
def select_sort(alist):
"""选择排序"""
#详细手把手写一下:
#第一步,写一次的代码
n = len(alist)
min_index = 0
for i in range(1,n):
if alist[min_index] > alist[i]:
min_index = i
alist[0],alist[min_index] = alist[min_index],alist[0]
#第二步,加上外置循环
n = len(alist)
for j in range(0,n-1)
min_index = j
for i in range(j+1,n):
if alist[min_index] > alist[i]:
min_index = i
alist[0],alist[min_index] = alist[min_index],alist[0]
2.2 时间复杂度
最优时间复杂度:O(n^2)
最坏时间复杂度:O(n^2)
稳定性:不稳定(考虑升序每次选择最大的情况)
三、插入排序
插入排序(Insertion Sort)是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。插入排序在实现上,在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。
3.1 和选择排序的异同
同:都是假设左边是排序的,右边是无序的
异:选择排序是第一次在所有里挑一个最小的,放最左边;第二次在除了刚才挑的,其他的数里再挑一个最小的,放第二个,以此类推(操作无序这边)
插入排序是取出第一个数,构成有序序列,然后再看第二个数,按大小插入到有序序列中,再看第三个数,按大小插入到有序序列中,以此类推(操作有序这边)
3.2 代码实现
def insert_sort(alist)
i = 1
while i > 0:#元素到头时停止
if alist[i] < alist[i-1]:
alist[i],alist[i-1] = alist[i-1],alist[i]
i -= 1 #因为往前移了一个位置
else: #因为如果比前一个元素大,那肯定比前面所有元素都大,就直接跳出循环即可
break
#增加外置循环
for j in range(1,n) #从右边的无序序列中取出多少个元素执行这样的过程
i = j
#执行从右边的无序序列中取出第一个元素,即i位置的元素,然后将其插入到前面正确位置
while i > 0:#元素到头时停止
if alist[i] < alist[i-1]:
alist[i],alist[i-1] = alist[i-1],alist[i]
i -= 1 #因为往前移了一个位置
else: #因为如果比前一个元素大,那肯定比前面所有元素都大,就直接跳出循环即可
break
3.3 时间复杂度
最优时间复杂度:O(n) (升序排列,序列已经处于升序状态)
最坏时间复杂度:O(n^2)
稳定性:稳定
四、希尔排序
希尔排序(Shell Sort)是插入排序的一种。也称缩小增量排序,是直接插入排序算法的一种更高效的改进版本。希尔排序是非稳定排序算法。该方法因DL.Shell于1959年提出而得名。 希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。
上图中,按照gap=4,分成了54,77,20 ; 26,31 ; 93,44 ; 17,55 四个序列,每个序列按照插入排序来操作
一次操作之后,gap变成2,再来一次;
gap变成1,再来一次,完成
4.1 代码实现
def shell_sort(alist):
n = len(alist)
# 初始步长
gap = n / 2
while gap > 0:
# 按步长进行插入排序
for i in range(gap, n):
j = i
# 插入排序
while j>=gap and alist[j-gap] > alist[j]:
alist[j-gap], alist[j] = alist[j], alist[j-gap]
j -= gap
# 得到新的步长
gap = gap / 2
alist = [54,26,93,17,77,31,44,55,20]
shell_sort(alist)
print(alist)
4.2 时间复杂度
最优时间复杂度:根据步长序列的不同而不同
最坏时间复杂度:O(n2)
稳定性:不稳定