时间复杂度:o(1) < o(logn) < o(n) < o(nlogn) < o(n^2) < o(n^2logn) < o(n^3)
一、递归
1. 汉诺塔:
移动次数:h(x)=2*h(x-1)+1
def hanoi(n,a,b,c): # n个圆盘,abc三个桩
if n>0:
hanoi(n-1,a,c,b) # 由a经过c到b
print("moving from %s to %s" %(a,c))
hanoi(n-1,b,a,c) # 由b经过a到c
二、 查找:index()
1. 顺序查找/线性查找:从头到尾,列表可无序
时间复杂度:o(n)
def linear_search(li,val): # li为列表,val为待查找的元素
for ind, v in enumerate(li): # enumerate用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,同时列出数据和数据下标,一般用在 for 循环当中
if v==val:
return ind
else:
return none
2. 二分查找/折半查找:与中间值比较,列表需有序
时间复杂度:o(logn)
def binary_search(li,val):
left=0
right=len(li)-1
while left<=right: # 候选区有值
mid=(left+right)//2 # 找到中间值的下标
if li[mid]==val:
return mid
elif li[mid]>val: # 待查找值在mid左侧
right=mid-1 # 把右指针移到mid左侧
else: # li[mid]<val 待查找值在mid右侧
left=mid+1
else:
return none
三、 排序:sort()
1. 冒泡排序:相邻比较,前>后:交换,共n-1趟
时间复杂度:o(n^2)
def bubble_sort(li):
for i in range(len(li)-1): # 第i趟
exchange=False
for j in rang(len(li)-i-1): # 指针位置
if li[j]>li[j+1]: # 前>后:升序,前<后:降序
li[j],li[j+1]=li[j+1],li[j] # 相邻值交换
exchange=True
if not exchange: # 如果没有顺序变化,提前结束排序
return
2. 选择排序:每次遍历列表中最小值,放在第一个位置
时间复杂度:o(n^2)
def select_sort(li):
for i in range(len(li)-1): # i是第几趟
min_loc=i # 存储最小值位置
for j in range(i+1,len(li)): # j是指针,li[j]是遍历的数,i+1是无需与自己比较
if li[j]<li[min_loc]:
min_loc=j # 找到最小值
li[i],li[min_loc]=li[min_loc],li[i] # 最小值与无序区的值进行交换
3. 插入排序:将无序区的牌依次插入有序区
时间复杂度:o(n^2)
def insert_sort(li):
for i in range(1,len(li)): # 遍历多少趟,i是摸到的牌的下标
tmp=li[i]
j=i-1 # j是手里的牌的下标
while j>=0 and li[j]>tmp: # 手里的牌比摸到的牌大
li[j+1]=li[j] # j手里的牌移动到右侧
j-=1
li[j+1]=tmp # 如果手里的牌比摸到的牌小,摸到的牌放到手里的牌右侧
4. 快速排序:二分值作为中间值将列表分为两部分,分别递归排序
时间复杂度:o(nlogn)
最坏情况时间复杂度: o(n^2)
def quick_sort(li,left,right):
if left<righ:
mid=partition(li,left,right) # 归位函数找到中间值的下标位置
# 将列表分为两部分,分别递归
quick_sort(li,left,mid-1)
quick_sort(li,mid+1,right)
# partition函数原理:取出列表左侧第一个元素放入一个变量中,列表其它元素依次与该元素进行比较,比该元素小的放入左侧,大的放入右侧,当left=right时,将该元素放回列表中
def partition(li, left, right):
tmp = li[left] # 取出列表左侧第一个元素放入一个变量中
while left < right: # 从列表右侧(因为左侧有空位)找到比该元素小的值放入该元素位置
while left < right and li[right] >= tmp: # 依次查找右侧元素,直到比该元素小
right -= 1
li[left] = li[right] # 把右边的值写到左边空位上
while left < right and li[left] <=tmp:
left += 1
li[right] = li[left] # 把左边的值写到右边空位上
li[left] = tmp # 当left=right时,将该元素放回列表中
return left # 此时left和right是同一个值
5. 堆排序:特殊的完全二叉树,大根堆(降序),小根堆(升序)
堆的向下调整:向下依次寻找每个大于父节点的子节点,找出最大值
挨个出数:为了确保该树始终为完全二叉树,每次提取出最大值之后将最下层子节点的最右节点作为新的父节点,再进行向下调整找出最大值
构造堆的过程:先调整最下层的非叶子节点的子树,再依次扩大需调整的树
堆排序过程:1.建立堆 --> 2.得到堆顶元素为最大元素 --> 3.去掉堆顶,将堆最后一个元素放到堆顶,通过一次调整重新使堆有序 --> 4.堆顶元素为第二大元素 --> 5.重复步骤3,直到堆为空
内置模块:heapq
常用函数:heapify(x),heappush(heap, item),heappop(heap)
时间复杂度:o(nlogn)
'''
二叉树顺序存储方式的(列表)原理:i是层数
通过父节点找子节点:
父节点和左子节点的下标之间的关系:i --> 2i+1
父节点和右子节点的下标之间的关系:i --> 2i+2
通过子节点找父节点:i --> (i-1)//2
'''
# 定义一个方法用于调整:看父节点去哪个子节点,时间复杂度为o(logn)
def sift(li, low, high): # high是堆的最后一个元素的下标,low是堆的第一个元素的下标
i = low # 第一个元素的下标
j = 2 * i + 1 # 第一个元素的左子节点的下标
tmp = li[low] # 把堆顶的值存入一个临时变量中
while j <= high: # 只要j位置有值,一直循环
if j + 1 <= high and li[j+1] > li[j]: # 如果右子节点存在,并比左子节点大
j = j + 1 # j指向右子节点
if li[j] > tmp:
li[i] = li[j]
i = j # 向下调整一层
j= 2 * i + 1
else: # tmp更大时,将tmp放到i的位置
li[i] = tmp
break
else: # 当j > high时,j指向树的负1层(越界),没有对应的j值与tmp进行比较,直接将tmp值放入i处
li[i] = tmp
# 定义堆排序:建立堆并调整堆并挨个出数,时间复杂度为o(nlogn)
def heap_sort(li):
n = len(li)
# 建立堆,时间复杂度为o(n/2*logn)-->o(nlogn)
for i in range((n-2)//2, -1, -1): # 通过子节点找父节点: i=n-1, (i-1)//2 = (n-1-1)//2;遍历范围至-1;步长-1
sift(li, i, n-1) # high的作用是确保i是最后一层,i不会再超过high,high始终指向整个堆的最后一个元素n-1
# 挨个出数,时间复杂度为o(nlogn)
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,即当前堆的最后一个元素下标为i-1
li = [i for i in range(100)]
import random
random.shuffle(li)
print(li) # 打印原无序列表
heap_sort(li)
print(li) # 打印排序后列表
# 使用内置模块和函数实现堆排序
import heapq # q是queue队列,用堆实现优先队列
import random
# 生成一个随机列表
li = list(rang(100))
random.shuffle(li)
# 建立小根堆
heapq.heapify(li)
# 建立堆排序:每次弹出最小元素
n = len(li)
for i in range(n):
print(heapq.heappop(li), end=',')