n*logn
快速排序

# 复杂度 O(nlogn) # 从小到大 1、取一个元素p(第一个元素),使元素p归位 2、列表被p分成两部分,左边都比p小,右边都比p大 3、递归完成排序 def partition(li,left,right): tmp = li[left] while left < right: while left < right and tmp <= li[right]: right -= 1 li[left] = li[right] # 找到右边第一个<=tmp的 while left < right and tmp >= li[left]: left += 1 li[right]=li[left] li[left] = tmp def quick_sort(li,left,right): if len(li)>=2: mid = partition(li,left,right) quick_sort(li,mid+1,right) quick_sort(li,left,mid-1) li = [5,7,4,6,3,1,2,9,8] quick_sort(li,0,len(li)-1) print(li) 装饰器装饰递归函数,装饰器每次递归都会执行 # 涉及到的问题 # 最坏情况:当列表正好是从大到小,复杂度升级为O(n^2) # 递归:递归深度sys.setrecursionlimit(226) # 设置深度
堆排序

树的深度 节点的度:(分了几个叉) 树的度:(最大的节点的度) 二叉树:节点的度不超过2 满二叉树:一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树 完全二叉树:叶节点只能出现在最下层和次下层,并且最下面一层的结点都集中在该层最左边的若干位置的二叉树 二叉树的存储方式: [9,8,7,6,5,0,1,2,4,3] 0,1,2,3,4,5,6,7,8,9 父节点->左孩子 i-->2i+1 父节点->右孩子 i-->2i+2 堆:特殊的完全二叉树 大根堆:一棵完全二叉树,满足任意节点都比其孩子结点大 小根堆:一棵完全二叉树,满足任意节点都比其孩子结点小 堆的向下调整顺序 当根节点的左右子树都是堆时,可以通过一次向下的调整来将其变换成一个堆 堆排序过程: 1、建立堆 2、得到堆顶元素,为最大元素 3、去掉堆顶、将堆最后一个元素放到堆顶,此时可通过一次调整重新使堆有序 4、堆顶元素为第二大元素 5、重复步骤3、直到堆变空 def sift(data,low,high): # low 堆的根节点位置;high堆得最后一个节点位置 i = low # 最开始指向根节点 j = 2*i+1 # j开始是左孩子 tmp = data[low] # 把堆顶存起来 while j <= high: # 只要j位置有数 if j+1 <=high and data[j+1] >data[j]: # 如果右孩子有并且比较大 j+=1 # j指向右孩子 if data[j] > tmp: data[i] = data[j] i = j # 往下看一层 j = 2 * i + 1 else: # tmp更大,把tmp放到i位置上 data[i] = tmp # 把tmp放到某一级领导位置上 break else: data[i] = tmp # 把tmp放到叶子节点上 def heap_sort(li): n = len(li) for i in range((n-2)//2,-1,-1): # i表示建堆的时候调整的部分的根的下标 sift(li,i,n-1) # 建堆完成后 for i in range(n-1, -1, -1): # i指向当前堆得最后一个元素 li[0],li[i] = li[i],li[0] sift(li,0,i-1) # i-1是新的high li = [i for i in range(1000)] import random random.shuffle(li) print(li) heap_sort(li) print(li) 内置模块----------heapq 实现堆排序 常用函数 heapify(x) # 建堆(小根堆) heappush(heap,item) heappop(heap) # 弹出最小的元素 应用场景: 现有n个数,设计算法得到前k大的数 解决思路: 排序后切片:O(nlogn) + k 冒泡排序,排k次,O(kn)就出来了,比上边快 插入排序,维护一个长度为k的列表,O(kn) 选择排序;和冒泡类似 堆排序: O(nlogk) 1、取列表前k个元素建立一个小根堆,堆顶就是目前第k大的数 2、依次向后遍历原列表,对于列表中的元素,如果小于堆顶,则忽略该元素; 如果大于堆顶,则将堆顶更换为该元素,并且对堆进行一次调整 3、遍历列表所有元素后,倒序弹出堆顶 def sift(data,low,high): # low 堆的根节点位置;high堆得最后一个节点位置 i = low # 最开始指向根节点 j = 2*i+1 # j开始是左孩子 tmp = data[low] # 把堆顶存起来 while j <= high: # 只要j位置有数 if j+1 <=high and data[j+1] >data[j]: # 如果右孩子有并且比较大 j+=1 # j指向右孩子 if data[j] > tmp: data[i] = data[j] i = j # 往下看一层 j = 2 * i + 1 else: # tmp更大,把tmp放到i位置上 data[i] = tmp # 把tmp放到某一级领导位置上 break else: data[i] = tmp # 把tmp放到叶子节点上 def topk(li,k): heap = li[0:k] for i in range((k-2)//2,-1,-1): sift(heap,i,k-1) for i in range(k,len(li)-1): if li[i] > heap[0]: heap[0]=li[i] sift(heap,0,k-1) for i in range(k-1,-1,-1): heap[0],heap[i]=heap[i],heap[0] sift(heap,0,i-1) return heap import random li = list(range(1000)) random.shuffle(li) print(topk(li,10))
归并排序

# 空间复杂度O(n) ltmp = [] # 时间复杂度O(nlogn) def merge(li,low,mid,high): i = low j = mid + 1 ltmp = [] while i<=mid and j<=high: if li[i] < li[j]: ltmp.append(li[i]) i+=1 else: ltmp.append(li[j]) j+=1 while i<=mid: ltmp.append(li[i]) i+=1 while j<=high: ltmp.append(li[j]) j+=1 li[low:high+1]=ltmp def merge_sort(li,low,high): if low<high: # 至少有2元素,递归 mid = (low + high)//2 merge_sort(li,low,mid) merge_sort(li,mid+1,high) merge(li,low,mid,high) li = list(range(1000)) import random random.shuffle(li) print(li) merge_sort(li,0,len(li)-1) print(li) 稳定性:当两个元素相同时,保证相对位置不变;(挨着换的都稳定)