总述
插入排序:直接插入排序;希尔排序;
选择排序:简单选择排序;堆排序;
交换排序:冒泡排序;快速排序;
归并排序;
桶排序/基数排序;
直接插入排序
选取未排序区域的第一个元素,从后至前逐个与已排序区域的元素两两比较,若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:return for i in range(length): cur_index = i while 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 -= 1 return arr
希尔排序-缩小增量排序
通过比较一定间隔的元素进行插入排序,同时不断缩小间隔(增量),直到比较相邻元素。当增量为0时,算法终止。
时间复杂度:依赖于gap的选择,平均复杂度O(n^1.5)
空间复杂度:O(1)--原地实现
稳定性:不稳定
def shell_sort(arr): n = len(arr) gap = n // 2 while 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 -= gap gap = gap // 2 return arr
选择排序
搜索未排序区域的最小值(最大值),将其交换到已排序区域的末尾,然后扩大已排序范围,直至得到升序(降序)排列;
时间复杂度:O(n^2)
空间复杂度:O(1)--原地实现
稳定性:不稳定
def select_sort(arr): length = len(arr) if length <= 1:return for i in range(length): min_index = i for j in range(i+1, length): if arr[min_index] > arr[j]: min_index = j if 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+2 if right < end: cur = left if nums[left]>nums[right] else right elif left < end: cur = left else: break if nums[cur]>nums[idx]: nums[cur], nums[idx] = nums[idx], nums[cur] idx = cur else:break return def 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:return for 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 = True if 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, right while i<j: while(i<j and nums[j]>=pivot): #从后往前查找,直到找到一个比pivot更小的数 j-=1 nums[i] = nums[j] #将更小的数放入左边 while(i<j and nums[i]<=pivot): #从前往后找,直到找到一个比pivot更大的数 i+=1 nums[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 arr mid = 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 = 0 while(i < len(left) and j < len(right)): if left[i] < right[j]: new.append(left[i]) i += 1 else: new.append(right[j]) j += 1 new = new + left[i:] + right[j:] return new
桶排序/基数排序
将数据按位进行分桶,按桶代表的数值进行排序,然后切换到下一个位;
def radix_sort(arr): max_num = max(arr) place = 1 while max_num >= 10**place: # 统计最大值的位数 place += 1 for i in range(place): buckets = [[] for _ in range(10)] for num in array: radix = int(num/(10**i) % 10) buckets[radix].append(num) j = 0 for k in range(10): for num in buckets[k]: arr[j] = num j += 1 return arr
备注:
冒泡排序和选择排序大体思路一致,区别在于冒泡是通过交换都得到未排序区域的最值,而选择只记录了对应下标;冒泡的有序区域在列表末尾,选择的有序区域在列表开头(这个也可换);冒泡比较出相对关系,可在满足要求时提前结束排序,而选择无法优化;