排序与搜索
排序算法(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)#次序被改变
不稳定排序算法额能回在相等的键值中改变记录的相对次序,但是稳定排序算法从来不会改变其相对次序。不稳定排序算法可以被特别的地实现为稳定,作为这件事的一个方式是人工扩充键值的比较,如此在其他方面相同键值的两个对象之间比较,(比如上面的比较中加入第二个标准:第二个键值的大小)。然而,这种次序通常牵涉到额外的空间负担。
通俗的来讲,排序之后,子序列和原来相同的就是稳定的,不相同的就是不稳定的。
冒泡排序
冒泡排序(Bubble Sort)是一种简单的排序算法,它重复的遍历要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。遍历数列的工作是重复的进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。
冒泡排序算法的运作如下:
- 比较相邻的元素,如果第一个比第二个大(升序),就交换他们两个。
- 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这一步做完以后,最后的元素会是最大的数。
- 针对所有的元素重复以上的步骤,除了最后一个。
- 持续每次对越来越少的元素重复上述的步骤,直到没有任何一对数字需要比较。
冒泡排序的分析
交换过程图示(第一次):
每一次遍历,都会有一个元素到达它的最终位置。
那么我们需要进行n-1次冒泡过程,每次对应的比较次数如下图所示:
冒泡排序实现
分析:使用顺序表或者链表都可以表示冒泡排序,这里我们选择以顺序表为例,因为交换链表节点,操作会稍微复杂一些。
为了方便在操作时交换,我们不选择交换元素,而是交换元素的下标。判断两个元素的大小,前一个元素大于后一个元素,要交换两个元素的位置。
因为列表下标是从0开始的,所以range要从n-1开始去遍历比较。
动画演示冒泡排序过程:
#冒泡算法执行过程
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)
代码实现:
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]
if __name__=="__main__":
li=[54,26,93,17,77,31,44,55,20]
print(li)
bubble_sort(li)#调用冒泡算法
print(li)
冒泡算法中,外层循环控制循环多少次,内层循环控制从头走到尾
运行结果:
另外一种循环表示方式:
def bubble_sort(alist):
for j in range(len(alist)-1,0,-1):
# j表示每次遍历需要比较的次数,是逐渐减小的
for i in range(j):
if alist[i] > alist[i+1]:
alist[i], alist[i+1] = alist[i+1], alist[i]
时间复杂度
- 最优时间复杂度:O(n)(表示遍历一次发现没有需要交换的元素,排序结束)
- 最坏时间复杂度:O(n2)(最坏的情况,每一次遍历都要从头走到尾)
- 稳定性:稳定
优化冒泡算法:当在一次遍历后,没有出现元素的交换,说明已经有序,所以用一个count记录交换元素的次数。当count==0时,直接return,不需要再继续遍历。
def bubble_sort(alist):
"""冒泡排序"""
n=len(alist)
for j in range(n-1):
count=0
for i in range(0,n-1-j):
if alist[i]>alist[i+1]:
alist[i],alist[i+1]=alist[i+1],alist[i]
count+=1
if 0==count:
return
选择排序
选择排序(Selection sort)是一种简单直观的排序算法。它的工作原理如下。首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。
选择排序的主要优点与数据移动有关。如果某个元素位于正确的最终位置上,则它不会被移动。选择排序每次交换一对元素,他们当中至少有一个将被移到其最终位置上,因此对n个元素的表进行排序总共进行至多n-1次交换。在所有的完全依靠交换去移动与安素的排序方法中,选择排序属于非常好的一种。
算法分析
alist=[54,226,93,17,77,44,55,20]
alist=[17,226,93,54,77,44,55,20]
alist=[17,20,31, 54,77,44,55,226]
相当于在列表的左边是有序的,右边是还未排序的,不断的遍历去寻找和交换,始终都是从未排序的序列中找到最小值去放到前面去。这里我们也使用数据下标去表示相应的数据。
def select_sort(alist):
"""选择排序"""
n=len(alist)
for j in range(n-1):
min_index=j
for i in range(j+1,n):
if alist[min_index]>alist[i]:
alist[i],alist[min_index]=alist[min_index],alist[i]
if __name__=="__main__":
li=[54,26,93,17,77,31,44,55,20]
print(li)
select_sort(li)#调用选择算法
print(li)
算法执行过程:
遍历列表,默认第一个元素是最小元素,依次去进行比较。
动画演示选择排序过程:
时间复杂度
- 最优时间复杂度:O(n2)
- 最坏时间复杂度:O(n2)
- 稳定性:不稳定(考虑升序每次选择最大的情况)
插入排序
插入排序(Insertion Sort)是一种简单直观的排序算法,它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。插入排序在实现上,在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。
插入排序分析
动画演示插入排序过程:
通过插入排序和选择排序的比较可知,选择排序操作的是无序的一边,插入排序操作的是有序的一边。
def insertion_sort(alist):
"""插入排序"""
n=len(alist)
for j in range(1,n):#从右边无序序列中取出多少个元素执行这样的过程
#j=[1,2,3,...,n-1]
#i表示内层循环起始值
i=j
while i>0:#执行从右边的无序序列中取出第一个元素i位置的元素,然后将其插入到正确的位置
if alist[i]<alist[i-1]:
alist[i],alist[i-1]=alist[i-1],alist[i]
i-=1
else:
break
if __name__=="__main__":
li=[54,26,93,17,77,31,44,55,20]
print(li)
insertion_sort(li)#调用插入算法
print(li)
另一种循环表示方式:
def insert_sort(alist):
# 从第二个位置,即下标为1的元素开始向前插入
for i in range(1, len(alist)):
# 从第i个元素开始向前比较,如果小于前一个元素,交换位置
for j in range(i, 0, -1):
if alist[j] < alist[j-1]:
alist[j], alist[j-1] = alist[j-1], alist[j]
时间复杂度
- 最优时间复杂度:O(n)(升序排列,序列已经处于升序状态)
- 最坏时间复杂度:O(n2)
- 稳定性:稳定
动画演示插入排序过程: