排序算法
直接插入排序
基本思想:
每次将待排序元素插入到已经排序过的序列的合适位置
平均时间复杂度为O(n^2)、空间复杂度为O(1)的解决方案
def insert_sort_2(array):
n=len(array)
for i in range(n):
current_num = array[i]
j=i-1
while j>=0 and array[j]>current_num: #j>=0用于防止数组越界,要放在循环条件的前边
array[j+1]=array[j]
j-=1
array[j+1]=current_num
return array
冒泡排序
基本思想:
逐次比较并交换位置,每轮过后,将最大的元素放在数组末尾
平均时间复杂度O(n^2)、空间复杂度为O(1)的解决方案
#*****************************************************************两层循环实现*****************************************************************
def bubble_sort(array):
n=len(array)
for i in range(n):
for j in range(n-i-1):
if array[j]>array[j+1]:
temp=array[j+1]
array[j+1]=array[j]
array[j]=temp
return array
#*****************************************************************递归实现*****************************************************************
def bubble_sort_2(array,n):
if n<=2:
return array
for i in range(n-1):
if array[i]>array[i+1]:
temp=array[i]
array[i]=array[i+1]
array[i+1]=temp
bubble_sort_2(array,n-1)
return array
选择排序
基本思想:
逐次比较,但并非每次都交换位置,每轮排序过后,该轮最小的元素与首个元素交换位置
平均时间复杂度O(n^2)、空间复杂度为O(1)的解决方案
#*****************************************************************基于循环*****************************************************************
def selection_sort(array,n):
for i in range(n):
min_index = i
for j in range(i,n):
if array[j]<array[min_index]:
min_index = j
temp = array[i]
array[i]=array[min_index]
array[min_index]=temp
return array
#*****************************************************************基于递归*****************************************************************
def selection_sort_2(array,start):
if start==len(array)-1:
return array
min_index = start
for i in range(start,len(array)):
if array[i]<array[min_index]:
min_index = i
temp = array[start]
array[start]=array[min_index]
array[min_index]=temp
selection_sort_2(array,start+1)
return array
快速排序
基本思想:
函数每次将序列array分为小于pivot的左子序列和大于pivot的右子序列,其中左子序列和右子序列使用递归的方式进行同样的处理
平均时间、空间复杂度为nlogn的解决方案
def qik_sort(array):
if len(array)<=1:
return array #递归返回的边界条件
pivot=array[0]
left=[num for num in array[1:] if num<=pivot]
right=[num for num in array[1:] if num>=pivot]
return quik_sort(left)+[pivot]+quik_sort(right) #递归分治
注意:
使用递归一定要注意递归的边界条件,否则递归函数无法退出回调。此处当传入函数的数组长度为1或者为0时候进行返回,因此条件是 len(array)<=1(left和right可能是空)
复杂度分析: 平均情况:递归深度为logn,每次开辟空间n,因此空间复杂度为nlogn,每次循环2n,时间复杂度为nlogn 最差情况:递归深度为n,空间退化为n^2,时间退化为n^2
平均时间复杂度为O(nlogn),空间复杂度为O(n)的双指针解决方案
def quik_sork(array,left,right):
if left>=right:
return array #递归的边界停止条件
pivot = array[0]
i = left
j = right
while(i<j):
while(array[j]>=pivot and j>i):#一定要先从右边开始找
j-=1
while(array[i]<=pivot and j>i):
i+=1
if i<j:
temp=array[i]
array[j]=array[i]
array[i]=temp
temp=array[i]
array[i]=pivot
array[left]=temp
quik_array(array,left,i-1)
quik_array(array,i+1,right)#递归处理
return array
注意:
递归的边界条件是left>=right,这里加上>避免递归无法退出陷入死循环
为何要先从右边开始找?这是因为双指针的while循环在满足两个条件中的一个的时候停止,即找到符合大小关系的数或者或者指针相遇,在指针相遇的情况下我们要与pivot进行交换,此时当然是希望找到的数<pivot,因此从右边开始找。此外循环条件加上=,是为了避免死循环
复杂度分析: 平均情况:递归深度为logn,每次循环n,时间复杂度为nlogn,原位操作,因此空间复杂度为n 最差情况:递归深度为n,时间退化为n^2,空间退化为n^2