冒泡法排序
冒泡法
属于交换排序 两两比较大小,交换位置。如同水泡咕嘟咕嘟往上冒 结果分为升序和降序排序 升序
n个数从左至右,编号从0开始到n-1,索引0和1的值比较,如果索引0大,则交换两者位置,如果索引1大,则不交换。继续比较索引1和2的值,将大值放在右侧。直至n-2和n-1比较完,第一轮比较完成。第二轮从索引0比较到 n-2,因为最右侧n-1位置上已经是最大值了。依次类推,每一轮都会减少最右侧的不参与比较,直至剩下最后2个数比较 降序
import random
lst = [ random.randint( 2,15) for i in range( 10) ]
print( lst)
length = len( lst)
for i in range( length) :
flag = False
for j in range( length-i-1) :
if lst[ j] > lst[ j+1] :
lst[ j] ,lst[ j+1] = lst[ j+1] ,lst[ j]
flag = True
if not flag:
break
print( lst)
冒泡法总结
冒泡法需要数据一轮轮比较 可以设定一个标记判断此轮是否有数据交换发生,如果没有发生交换,可以结束排序,如果发生交换,继续下一轮排序 最差的排序情况是,初始顺序与目标顺序完全相反,遍历次数1,……,n-1之和 n(n-1)/2 最好的排序情况是,初始顺序与目标序完全相同,遍历次数 n-1 时间复杂度 O(n^2)
简单选择排序
简单选择排序
属于选择排序 两两比较大小,找出极值(极大值或极小值)被放置在固定的位置,这个固定位置一般指的是某一端 结果分为升序和降序排列 降序
n 个数从左至右,索引从 0 开始到 n-1 ,两两依次比较,记录大值索引,此轮所有数比较完毕,将大数和索引1位置交换,如果它就在索引1位置则不交换。依次类推,每次左边都会固定下一个大数 升序
lst = [ 3, 9, 14, 8, 10, 13, 15, 2, 13, 9]
print( lst)
length = len( lst)
for i in range( length) :
maxindex = i
for j in range( i+1 , length) :
if lst[ j] > lst[ maxindex] :
maxindex = j
if i != maxindex:
lst[ i] ,lst[ maxindex] = lst[ maxindex] ,lst[ i]
print( lst)
lst = [ 3, 9, 14, 8, 10, 13, 15, 2, 13, 9]
print( lst)
length = len( lst)
for i in range( length//2) :
maxindex = i
minindex = -i-1
minorigin = length + minindex
for j in range( i+1 , length) :
if lst[ j] > lst[ maxindex] :
maxindex = j
if lst[ -j-1] < lst[ minindex] :
minindex = -j-1
else:
minindex = length + minindex
if i != maxindex:
lst[ i] ,lst[ maxindex] = lst[ maxindex] ,lst[ i]
if i == minindex:
minindex = maxindex
if minindex != minorigin:
lst[ minorigin] ,lst[ minindex] = lst[ minindex] ,lst[ minorigin]
print( lst)
优化实现
二元选择排序:同时固定左边最大值和右边最小值 优点:减少迭代元素的次数
length//2 整除,通过几次运算就可以发现规律 由于使用了负索引,为了计算方便,所以增加了转换为正索引
lst = [ 1,1,1,1,1,1,1,1,1,1,1,1]
print( lst)
length = len( lst)
count_iter = 0
count_swap = 0
for i in range( length//2) :
maxindex = i
minindex = -i-1
minorigin = length + minindex
for j in range( i+1 , length-1) :
count_iter += 1
if lst[ j] > lst[ maxindex] :
maxindex = j
if lst[ -j-1] < lst[ minindex] :
minindex = -j-1
if lst[ maxindex] == lst[ minindex] :break
minindex = length + minindex
if i != maxindex:
lst[ i] ,lst[ maxindex] = lst[ maxindex] ,lst[ i]
count_swap += 1
if i == minindex:
minindex = maxindex
if minindex != minorigin:
lst[ minorigin] ,lst[ minindex] = lst[ minindex] ,lst[ minorigin]
count_swap += 1
print( lst,count_iter,count_swap)
如果一轮比较后,极大值、极小值的值相等,说明比较的序列元素全部相等 改进实现,[1,1,1,1,1,1,1,1,2,1,1,1] 这种情况,找到的最小索引是 -2 ,最大索引8,上面的代码会交换两次,最小值两个1交换是无用功,所以,增加一个判断
lst = [ 1,1,1,1,1,1,1,1,2,1,1,1]
print( lst)
length = len( lst)
count_iter = 0
count_swap = 0
for i in range( length//2) :
maxindex = i
minindex = -i-1
minorigin = length + minindex
for j in range( i+1 , length-1) :
count_iter += 1
if lst[ j] > lst[ maxindex] :
maxindex = j
if lst[ -j-1] < lst[ minindex] :
minindex = -j-1
if lst[ maxindex] == lst[ minindex] :break
minindex = length + minindex
if i != maxindex:
lst[ i] ,lst[ maxindex] = lst[ maxindex] ,lst[ i]
count_swap += 1
if i == minindex:
minindex = maxindex
if minindex != minorigin and lst[ minorigin] != lst[ minindex] :
lst[ minorigin] ,lst[ minindex] = lst[ minindex] ,lst[ minorigin]
count_swap += 1
print( lst,count_iter,count_swap)
简单选择排序总结
简单选择排序需要数据一轮轮比较,并在每一轮中发现极值 没有办法知道当前轮是否已经达到排序的要求,但是可以知道极值是否在目标索引位置上 遍历次数 1,……,n-1之和n(n-1)/2 时间复杂度 O(n^2) 减少了交换次数,提高了效率,性能略好于冒泡法
直接插入排序
直接插入排序原理
在未排序序列中,构建一个子排序序列,直至全部数据排序完成 将待排序的数,插入到已经排序 的序列中合适的位置 增加一个哨兵,放入待比较值,让它和后面已经排好序的序列比较,找到合适的插入点
增加一个哨兵位,每趟将待比较数放入 哨兵依次和待比较数的前一个数据比较,大数靠右移动,找到哨兵中值的插入位置 每一轮结束后,得到一个从开始到待比较数位置的一个有序序列
lst = [ 9, 3, 14, 8, 10, 13, 15, 2, 13, 9]
lst = [ 0] +lst
print( lst)
length = len( lst)
for i in range( 2,length) :
lst[ 0] = lst[ i]
j = i - 1
if lst[ j] > lst[ 0] :
while lst[ j] > lst[ 0] :
lst[ j+1] = lst[ j]
j -= 1
lst[ j+1] = lst[ 0]
print( lst[ 1:] )
最好情况,正好是升序排序,比较迭代 n-1 次 最差情况,正好是降序排列,比较迭代 1,2,……,n-1 即n(n-1)/2,数据移动非常多 使用两层嵌套循环,时间复杂度O(n^2) 稳定排序算法
如果待排序序列R中两元素相等,即 Ri 等于 Rj ,且 i < j ,那么排序后这个先后顺序不变,这种排序算法就称为稳定排序 使用在小规模数据比较 优化,如果比较操作耗时大的话,可以采用二分查找来提高效率,即二分查找插入排序