目录
一、复杂度
1-1 空间复杂度 - 用于评估算法内存占用大小
1-2 时间复杂度 - 用来估计算法运行时间的
一般来说,时间复杂度高的算法比复杂度低的算法慢。
循环减半的过程 → O(logn);循环n次,则复杂度为O(n^n)
- 常见的时间复杂度(按效率排序)
- O(1) < O(logn) < O(n) < O(nlogn) < O(n2) < O(n2logn) < O(n3)
- 不常见的时间复杂度
- O(n!) O(2n) O(nn) …
二、列表排序
2-1 基础排序
2-1-1 冒泡排序
排序原理:列表每两个相邻的数,如果前边的比后边的大,那么交换这两个数
def bubble_sort(li): # 10个数表示外层循环9次,每一次外层循环结束,表示一个最大数归为 for i in range(len(li)-1): # 内层循环两两进行对比,循环总数-已循环次数 for j in range(len(li)-1-i): if li[j] > li[j+1]: li[j], li[j+1] = li[j+1], li[j] ''' value: 7 5 4 6 3 8 2 9 1 index: 0 1 2 3 4 5 6 7 8 时间复杂度:O(n^2) 空间复杂度:O(1) 内层循环: 当 i = 0 内层循环len(li)=1=i次 j[0] 和 j[1] 进行比较,若后者数大,则二者交换位置 j[1] j[2] j[2] j[3] . . . j[len(li)=2=i] j[len(li)=1=i] 第一次循环结束,最大的数一定到了列表尾部 第二次外层循环从i=1,循环长度-1,即 len(li)-1-1 第三次外层循环从i=2,循环长度-1,即 len(li)-1-2 '''
基础冒泡排序优化 - 使用内置自定义flag
# 冒泡排序优化,使用flag变量判断是否为有序列表 # 若列表有序,只需要执行一次外层循环,当flag为false立马返回 # 当列表有序时,第一次循环都不会走入内层循环的判断语句内,则flag始终为False def bubble_sort_0(li): for i in range(len(li)-1): flag = False for j in range(len(li)-i-1): if li[j] > li[j+1]: li[j], li[j+1] = li[j+1], li[j] flag = True if not flag: return
2-1-2 选择排序
排序原理:取出最小值,每次内层循环将剩余值一一与最小值进行两两比对,进行是否重置最小值操作
def select_sort(li): # 10个数循环9次,每次外层循环结束,最小值归位 for i in range(len(li) - 1): # 初始化最小值,第一次内层循环即将当前最小位的数当做最小值,min的索引值不动 min_loc = i for j in range(i+1, len(li)): if li[j] < li[min_loc]: li[j], li[min_loc] = li[min_loc], li[j] ''' value: 7 5 4 6 3 8 2 9 1 index: 0 1 2 3 4 5 6 7 8 时间复杂度:O(n^2) 空间复杂度:O(1) 内层循环: 当i=0 最小值为 li[0] = li[min] 内层从li[1]开始取到li[len(li)-1],即 range(1,len(li)) li[min] 依次和 li[j]进行对比,若li[j]小,则将li[min]和li[j]位置互换 仍然保持li[0]为最小值 '''
2-1-3 插入排序
排序原理:列表被分为有序区和无序区两个部分。最初有序区只有一个元素。每次从无序区选择一个元素,插入到有序区的位置,直到无序区变空。
def insert_sort(li): for i in range(1, len(li)): # tmp为取出的数 tmp = li[i] # j为取出数的前一个索引值 j = i - 1 # 若取出的数前存在数,并且,取出的数小于前一个数,则将tmp的数覆盖为前一个数值 # 比较索引向前走一位继续判断循环 while j >= 0 and tmp < li[j]: li[j + 1] = li[j] j -= 1 # 因为循环执行的是覆盖操作,则结束循环,一定有两个索引为值相同。修改前置的索引值。 # 若结束循环后,j=-1,则将取出值置最前 # 若结束循环后,j != -1,则将取出值放置在上一次循环比较前的位置,抵消j -= 1操作 li[j + 1] = tmp ''' value: 7 5 4 6 3 8 2 9 1 index: 0 1 2 3 4 5 6 7 8 时间复杂度:O(n^2) 空间复杂度:O(1) '''
2-2 高级排序
2-2-1 快速排序
- 取一个元素p(第一个元素),使元素p归位;
- 列表被p分成两部分,左边都比p小,右边都比p大;
- 递归完成排序。
- 、
def quick_sort(data, left, right): ''' data - 数据列表 left - 最左索引 right - 最右索引 quick(li, 0, len(li)-1) ''' if left < right: mid = partition(data, left, right) # 迭代分左右部分归位p元素 quick_sort(data, left, mid - 1) quick_sort(data, mid + 1, right) def partition(data, left, right): ''' 归位取出的第一个元素 data:列表 left:最左索引 right:最右索引 ''' # 提取出需要归位的元素 tmp = data[left] # 当左索引和右索引未合并或者交换,则表示元素未归位 while left < right: # 移动右指针,若右指针元素大于tmp,则指针继续移动 while left < right and data[right] >= tmp: right -= 1 # 右指针元素小于tmp,跳出循环,将右指针值覆盖在左指针位 data[left] = data[right] # 移动左指针,若左指针元素小于tmp,则指针继续移动 while left < right and data[left] <= tmp: left += 1 # 左指针元素大于tmp,跳出循环,将左指针值覆盖在右指针位置 data[right] = data[left] # 当左索引和右索引未合并或者交换,跳出指针移动循环,并将需归位元素赋予其位 data[left] = tmp return left ''' value: 7 5 4 6 3 8 2 9 1 index: 0 1 2 3 4 5 6 7 8 时间复杂度:O(nlogn) - partition函数为n,quick_sort为logn 空间复杂度:O(1) '''
2-2-2 堆排序
2-2-3 归并排序
- 分解:将列表越分越小,直至分成一个元素。一个元素即是有序的。
- 合并:将两个有序列表归并,列表越来越大。
- 一次归并:分解成单个元素后的比较。
def merge(li, left, mid, right): ''' li - 数据列表 letf - 最左索引 mid - 中间索引 right - 最右索引 ''' i = left j = mid + 1 # 新列表,用于将取出值有序排列 ltmp = [] # 取出最左元素和中间元素进行对比,将较小元素放入列表,对应指针右移 while i <= mid and j <= right: 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 <= right: ltmp.append(li[j]) j += 1 # 将新列表覆盖在原列表内,通过切片(取前不取后,因此右索引+1) li[left:right + 1] = ltmp def merge_sort(li, left, right): if left < right: # 找到中间位置索引,取最大整除数 mid = (left + right) // 2 '''先迭代拆分,后迭代合并''' # 分解操作 merge_sort(li, left, mid) merge_sort(li, mid + 1, right) # 合并操作 merge(li, left, mid, right) ''' value: 7 5 4 6 3 8 2 9 1 index: 0 1 2 3 4 5 6 7 8 时间复杂度:O(nlogn) 空间复杂度:O(n) '''
2-3 非常用排序
2-3-1 希尔排序
- 希尔排序是一种分组插入排序算法。
- 首先取一个整数d1=n/2,将元素分为d1个组,每组相邻量元素之间距离为d1,在各组内进行直接插入排序;
- 取第二个整数d2=d1/2,重复上述分组排序过程,直到di=1,即所有元素在同一组内进行直接插入排序。
- 希尔排序每趟并不使某些元素有序,而是使整体数据越来越接近有序;最后一趟排序使得所有数据有序。
def shell_sort(li): gap = len(li) // 2 while gap > 0: for i in range(gap, len(li)): tmp = li[i] j = i - gap while j >= 0 and tmp < li[j]: li[j + gap] = li[j] j -= gap li[j + gap] = tmp gap /= 2 ''' 时间复杂度:O((1+τ)n) 或 O(1.3n) '''
2-3-2 基数排序
2-3-3 桶排序
2-4 常见排序总结
2-5 其他排序
2-5-1 计数排序 - 利用index顺序进行匹配计数
- vlaue: 10 9 8 7 9 6 2 4 5 2 9 1
- index: 0 1 2 3 4 5 6 7 8 9 10
- count: 0 1 2 0 1 1 1 1 1 3 1
- list : 1 2 2 4 5 6 7 8 9 9 9 10
def count_sort(li): # 初始化一个全为0组成的11个数的列表,表示从0开始计数 ltmp = [0 for i in range(11)] # 循环数据列表,value与index匹配,1对应li[1]内value值计数+1 for x in li: ltmp[x] += 1 # 清空原有数据列表 li.clear() # 更具ltmp计数列表,循环列表内值次数,并将其索引值添加到原列表中。 for index, val in enumerate(ltmp): for v in range(val): li.append(index)
三、二分查找
# 普通二分查找 def bin_search(data_set, value): ''' 在指定有序集合内查找索引 :param data_set: 有序集合 :param value: 查找值 :return: 查找指定值的索引 ''' low = 0 high = len(data_set) - 1 while low <= high: mid = (low + high) // 2 if data_set[mid] == value: return mid elif data_set[mid] > value: high = mid - 1 else: low = mid + 1 # 基于迭代的二分查找 def bin_search_rec(data_set, value, low, high): ''' 指定范围你,对有序集合查找value的索引 :param data_set: 有序集合 :param value: 查找值 :param low:最大值 :param high:最小值 :return:查找指定值的索引 ''' if low <= high: mid = (low + high) // 2 if data_set[mid] == value: return mid elif data_set[mid] > value: return bin_search_rec(data_set, value, low, high) else: return bin_search_rec(data_set, value, low, high) else: return