总述
插入排序:直接插入排序;希尔排序;
选择排序:简单选择排序;堆排序;
交换排序:冒泡排序;快速排序;
归并排序;
桶排序/基数排序;
直接插入排序
选取未排序区域的第一个元素,从后至前逐个与已排序区域的元素两两比较,若arr[i-1]>arr[i](前者大),交换arr[i-1]和arr[i],i-=1;需要保证已排序区域再加入这个元素后仍然有序——打扑克;
时间复杂度:最优O(n)--满足排序要求 最坏O(n^2)--与排序要求相反
空间复杂度:O(1)--原地实现
稳定性:稳定
def insertion_sort(arr):length = len(arr)if length <= 1:returnfor i in range(length):cur_index = iwhile cur_index-1 >= 0 and arr[cur_index-1] > arr[cur_index]:arr[cur_index], arr[cur_index-1] = arr[cur_index-1], arr[cur_index]cur_index -= 1return arr
希尔排序-缩小增量排序
通过比较一定间隔的元素进行插入排序,同时不断缩小间隔(增量),直到比较相邻元素。当增量为0时,算法终止。
时间复杂度:依赖于gap的选择,平均复杂度O(n^1.5)
空间复杂度:O(1)--原地实现
稳定性:不稳定
def shell_sort(arr):n = len(arr) gap = n // 2while gap>0:for i in range(gap, n):while i-gap >= 0 and arr[i-gap]>arr[i]:arr[i], arr[i-gap] = arr[i-gap], arr[i]i -= gapgap = gap // 2return arr
选择排序
搜索未排序区域的最小值(最大值),将其交换到已排序区域的末尾,然后扩大已排序范围,直至得到升序(降序)排列;
时间复杂度:O(n^2)
空间复杂度:O(1)--原地实现
稳定性:不稳定
def select_sort(arr):length = len(arr)if length <= 1:returnfor i in range(length):min_index = ifor j in range(i+1, length):if arr[min_index] > arr[j]:min_index = jif min_index != i:arr[min_index], arr[i] = arr[i], arr[min_index]
堆排序
通过数据上浮构造初始大根堆,然后将最大值与最后一个数交换,通过数据下沉恢复除最后一个节点的大根堆,实现倒序构造升序数组;
时间复杂度:O(nlogn)
空间复杂度:O(1)--原地实现
稳定性:不稳定
def heapify(arr, index, end):while True:left, right = 2*idx+1, 2*idx+2if right < end: cur = left if nums[left]>nums[right] else rightelif left < end: cur = leftelse: breakif nums[cur]>nums[idx]:nums[cur], nums[idx] = nums[idx], nums[cur]idx = curelse:breakreturndef heap_sort(arr):n = len(arr)for i in range(n//2, -1, -1):heapify(arr, i, n)for i in range(n-1, 0, -1):arr[0], arr[i] = arr[i], arr[0]heapify(arr, 0, i) # 调整边界return arr
冒泡排序
对未排序区域的元素两两比较,将较大值(较小值)向后交换,然后缩小未排序范围,直至未排序范围清空时得到升序(降序)排列;
时间复杂度:最优O(n)--满足排序要求 最坏O(n^2)--与排序要求相反
空间复杂度:O(1)--原地实现
稳定性:稳定
def bubble_sort(arr):length = len(arr)if length <= 1:returnfor i in range(length):is_made_swap = False # 设置标志位for j in range(length - i - 1):if arr[j] > arr[j + 1]:arr[j], arr[j + 1] = arr[j + 1], arr[j]is_made_swap = Trueif not is_made_swap: break # 无交换代表已经有序
快速排序
step1.从数列中挑出一个元素,称为"基准"(pivot),
step2.重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区结束之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。
step3.递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。时间复杂度:最优O(nlogn)--每次接近二分 最坏O(n^2)--每次只分出一个
空间复杂度:O(logn)
稳定性:不稳定
def partition(nums, left, right):pivot = nums[left] #初始化一个待比较数据i,j = left, rightwhile i<j:while(i<j and nums[j]>=pivot): #从后往前查找,直到找到一个比pivot更小的数j-=1nums[i] = nums[j] #将更小的数放入左边while(i<j and nums[i]<=pivot): #从前往后找,直到找到一个比pivot更大的数i+=1nums[j] = nums[i] #将更大的数放入右边nums[i] = pivot #待比较数据放入最终位置 return i #返回待比较数据最终位置def quicksort(nums, left, right):if left < right:index = partition(nums, left, right)quicksort(nums, left, index-1)quicksort(nums, index+1, right)
归并排序
把长度为n的输入序列分成两个长度为n/2的子序列,在子序列中也采用归并排序;当子序列中排序完成后,将其合并;
时间复杂度:最优O(n) 最坏O(nlogn)
空间复杂度:O(n)
稳定性:不稳定
def merge_sort(arr):if len(arr) <= 1:return arrmid = len(arr) // 2 left = merge_sort(arr[:mid])right = merge_sort(arr[mid:])return merge(left, right) def merge(left, right):new = []i = j = k = 0while(i < len(left) and j < len(right)):if left[i] < right[j]:new.append(left[i])i += 1else:new.append(right[j])j += 1new = new + left[i:] + right[j:]return new
桶排序/基数排序
将数据按位进行分桶,按桶代表的数值进行排序,然后切换到下一个位;
def radix_sort(arr):max_num = max(arr)place = 1while max_num >= 10**place: # 统计最大值的位数place += 1for i in range(place):buckets = [[] for _ in range(10)]for num in array:radix = int(num/(10**i) % 10)buckets[radix].append(num)j = 0for k in range(10):for num in buckets[k]:arr[j] = numj += 1return arr
备注:
冒泡排序和选择排序大体思路一致,区别在于冒泡是通过交换都得到未排序区域的最值,而选择只记录了对应下标;冒泡的有序区域在列表末尾,选择的有序区域在列表开头(这个也可换);冒泡比较出相对关系,可在满足要求时提前结束排序,而选择无法优化;