#coding=utf-8
#具有稳定性的算法:冒泡排序、插入排序、基数排序、归并排序
#堆排序、归并排序:平均、最好、最坏均为O(nlogn);选择排序:平均、最好、最坏均为O(n^2)
#快排:平均、最好为O(nlogn),最坏为O(n^2)
#冒泡、插入排序:最好O(n),最坏和平均为O(n^2)
#希尔排序:最坏O(n^1.3),最好O(n^2)
#基数排序:O(d(n+k)),其中d为数字位数,n为排序数字个数,k为基数
#冒泡排序,具有稳定性,O(n^2)
def bubble_sort(num):
for i in range(len(num)):
# print(i)
change = False
for j in range(len(num)-1,i,-1): #从后往前,每次最小的冒到最前面(也可以从前往后,每次最大的冒到最后面)
if num[j]<num[j-1]:
num[j],num[j-1] = num[j-1],num[j]
change = True
if not change:
break #最好情况为轮询一遍不需要交换,O(n)
return num
#插入排序,具有稳定性,O(n^2)
def insert_sort(num):
for i in range(len(num)):
for j in range(i,0,-1):
if num[j]<num[j-1]:
num[j],num[j-1] = num[j-1],num[j]
else:
break #最好情况为都break不需要操作,O(n)
return num
#选择排序,不具有稳定性,O(n^2)
def select_sort(num):
for i in range(len(num)):
min_index = i
for j in range(i+1,len(num)): #找出最小值,与num[i]交换
if num[min_index]>num[j]:
min_index = j
num[i],num[min_index] = num[min_index],num[i] #交换导致不稳定
return num
#---------------------------------------------------------------------------------------
#归并排序,具有稳定性,O(nlogn),其中n为排序耗时,logn为二分耗时
def merge(num,left,mid,right):
temp = []
i,j = left,mid+1
while (i<=mid and j<=right):
if (num[i]<=num[j]):
temp.append(num[i])
i+=1
else:
temp.append(num[j])
j+=1
while (i<=mid): #长度不等时剩下的,不是左边剩就是右边剩
temp.append(num[i])
i += 1
while (j<=right):
temp.append(num[j])
j += 1
num[left:right+1] = temp
#index[0,1,2,3,4,5]->[0,1,2][3,4,5]->[0,1][2,2][3,4,5]->[0,0][1,1][2,2][3,4,5]
#->merge([0,0][1,1])->merge([0,1][2,2])->(0,1,2)[3,4][5,5]->(0,1,2)[3,3][4,4][5,5]
#->merge([3,3][4,4])->merge([3,4][5,5])->merge([0,1,2][3,4,5])
def merge_recursion_sort(num,left,right): #递归归并排序,自顶向下
if left==right:
return
mid = (left+right)//2
merge_recursion_sort(num,left,mid) #左区间不断对半分
merge_recursion_sort(num,mid+1,right) #右区间不断对半分
merge(num,left,mid,right) #左右
# print('merge',num,left,mid,right)
return num
#merge([0,0][1,1])([2,2][3,3])([4,4][5,5])->[0,1][2,3][4,5]->merge([0,1][2,3])
#->[0,3][4,5]->merge([0,3][4,5])->[0,5]
def merge_iteration_sort(num): #非递归归并排序,自底向上
n = len(num)-1
i = 1
while (i<=n): #子序列的长度初始为1,每轮merge后长度乘2
left = 0
while (left+i<=n): #如果后面还有子序列,就再加i个长度之后
mid = left+i-1 #每个子区间长度为i,merge([left,left+i-1][left+i,right])
if (mid+i)>n: #如果right超过数组长度,就将right设置为len-1
right = n
else:
right = mid + i
merge(num,left,mid,right)
# print('merge',num,left,mid,right)
left = right + 1
i *= 2
return num
#-------------------------------------------------------------------------------------------
#堆排序,不具有稳定性,O(nlogn),其中新堆顶向下调整为logn,有n个元素
def heap_sort(num):
n = len(num)
build_heap(num,n-1)
while (n>1): #堆元素还有两个或两个以上就还需要交换位置然后调整堆
# print('顶点放最末前:',num)
num[0],num[n-1] = num[n-1],num[0]
# print('顶点放最末后:',num)
heapify(num,0,n-2)
n -= 1
return num
#堆顶从0开始,和树结构类存储不一样,用数组存储堆是层次存储。父节点为i,则左孩子为2i+1,右孩子为2i+2
#最末尾的非叶子节点为(n-1)//2,其中n=堆元素个数-1
#数组建堆
def build_heap(num,n):
i = (n-1)//2 #最末尾的非叶节点
while (i>=0):
heapify(num,i,n)
i -= 1
#堆自顶向下调整
def heapify(num,i,n):
maxx = i #根和左右子树中的最大值的index
if (2*i+1<=n) and (num[2*i+1]>num[maxx]): #存在左孩子且左孩子更大
maxx = 2*i+1
if (2*i+2<=n) and (num[2*i+2]>num[maxx]): #存在右孩子且右孩子更大
maxx = 2*i+2
if (maxx != i): #此处根若最大没有继续往下是因为build_heap的时候是从最末尾非叶开始调
num[maxx],num[i] = num[i],num[maxx] #把最大放到i节点位置
heapify(num,maxx,n) #交换位置后,再去调整因交换而可能改变的下面的堆结构
#---------------------------------------------------------------------------------------
def heap_sort2(array):
def heap_adjust(parent):
child = 2 * parent + 1 # left child
while child < len(heap):
if child + 1 < len(heap):
if heap[child + 1] > heap[child]:
child += 1 # right child
if heap[parent] >= heap[child]:
break
heap[parent], heap[child] = \
heap[child], heap[parent]
parent, child = child, 2 * child + 1
heap, array = array.copy(), []
for i in range(len(heap) // 2, -1, -1):
heap_adjust(i)
while len(heap) != 0:
heap[0], heap[-1] = heap[-1], heap[0]
array.insert(0, heap.pop())
heap_adjust(0)
return array
#---------------------------------------------------------------------------------------
#快速排序,不具有稳定性,O(nlogn)
def quick_sort(num,left,right):
if left>=right:
# print('left>=right')
return
pivot_index = partition(num,left,right)
# print(pivot_index)
quick_sort(num, left, pivot_index-1)
# print(num)
quick_sort(num, pivot_index+1, right)
# print(num)
return num
def partition(num, left, right):
pivot = num[right] #每次选最右边的元素为基准元素
tail = left - 1 #左边均小于基准,右边均大于基准,tail记录左边区间最后一位的下标
for i in range(left,right): #最后一位是pivot单独处理
if num[i]<pivot:
tail += 1
num[tail],num[i] = num[i],num[tail]
num[tail+1],num[right] = num[right],num[tail+1]
return tail+1
#-------------------------------------------------------------------------------
def quick_sort2(array):
def recursive(begin, end):
if begin > end:
return
l, r = begin, end
pivot = array[l]
while l < r:
while l < r and array[r] > pivot:
r -= 1
while l < r and array[l] <= pivot:
l += 1
array[l], array[r] = array[r], array[l]
array[l], array[begin] = pivot, array[l]
recursive(begin, l - 1)
recursive(r + 1, end)
recursive(0, len(array) - 1)
return array
#--------------------------------------------------------------------
#基数排序,具有稳定性,O(d*(n+r)),其中d为位数,n为数组元素个数,r为基数(如十进制则为10)
#首先是遍历各个位将数字放入桶中(共d*n次操作,n个数,d次遍历),再是遍历桶,得到最终排列(共r*d次操作,r个桶,d次遍历)
def radix_sort(num):
n = len(num)
digit = 0 #用来得到各个位的数
bucket = [[]]
while len(bucket[0])!=n: #最后最高位均为0,都跑到标号为0的桶中
bucket = [[] for i in range(10)] #每次循环初始就清空桶
for i in num:
number = i//(10**digit)%10 #获取各个位上的数
bucket[number].append(i)
# print('bucket',bucket)
num.clear() #用完num之后清空,获取新排序的num
for i in bucket: #此处为从小到大排列,若从大到小则为bucket[::-1]
num += i
# print('num',num)
digit += 1 #下一位需要多除以一位10
return num
#-----------------------------------------------------------------------
#希尔排序,不具有稳定性,O(n^1.3-n^2),改进版插入排序
def shell_sort2(num): #自己写的,循环太多
n = len(num)
gap = n//2
while gap>0:
for i in range(gap):
for ii in range(i,n,gap): #从此处开始就是插入排序
for jj in range(ii,0,-1):
if num[jj]<num[jj-1]:
num[jj],num[jj-1] = num[jj-1],num[jj]
else:
break
gap = gap//2
return num
#第一次要比的区间们gap=5:[0,5][1,6][2,7][3,8][4,9]
#第二次要比的区间们gap=2:[0,2,4,6,8][1,3,5,7,9]
def shell_sort(num):
n = len(num)
gap = n//2
while gap>0:
for i in range(gap,n):
ii = i
while (ii>=gap and num[ii]<num[ii-gap]): #几个区间同时比
num[ii],num[ii-gap] = num[ii-gap],num[ii]
ii -= gap
gap = gap//2
return num
a = [4,3,2,1,0]
# a = [5,4,10,3,8,3,2,2,1]
# print(bubble_sort(a))
# print(insert_sort(a))
# print(select_sort(a))
# print(merge_recursion_sort(a,0,len(a)-1))
# print(merge_iteration_sort(a))
# print(heap_sort(a))
# print(heap_sort2(a))
# print(quick_sort(a,0,len(a)-1))
# print(quick_sort2(a))
# print(radix_sort(a))
# print(shell_sort(a))