树
嵌套数组
def tree(r):
return [r,[],[]]
def insert_left(root,newnode):
a=root.pop(1)
if len(a)>=1:
root.insert(1,[newnode,a,[]])
else:
root.insert(1,[newnode,[],[]])
def insert_right(root,newnode):
a=root.pop(2)
if len(a)>=1:
root.insert(2,[newnode,[],a])
else:
root.insert(2,[newnode,[],[]])
a=tree(3)
insert_left(a,5)
insert_left(a,7)
insert_right(a,4)
insert_right(a,1)
print(a)
#[3,
[7,
[5, [], []],
[]],
[1,
[],
[4, [], []]]]
链表存储
from collections import deque
class node:
def __init__(self,elem):
self.elem=elem
self.lchild=None
self.rchild=None
class tree:
def __init__(self):
self.root=None
def add(self,elem):
Node=node(elem)
if self.root==None:
self.root=Node
else:
queue=deque()
queue.append(self.root)
while queue:
cnode=queue.popleft()
if cnode.lchild==None:
cnode.lchild=Node
return
else:
queue.append(cnode.lchild)
if cnode.rchild==None:
cnode.rchild=Node
return
else:
queue.append(cnode.rchild)
#前序
def pre(self,node):
if node is None:
return
else:
print(node.elem,end=' ')
self.pre(node.lchild)
self.pre(node.rchild)
Tree=tree()
for i in range(10):
Tree.add(i)
Tree.pre(Tree.root)
0 1 3 7 8 4 9 2 5 6
树状数组
下标从1开始
已知一个数组,求树状数组用在某个位置加上v的方法
a=[0]+[5,7,4,2] #下标必须从0开始
b=[0]*len(a)
def lowbit(n):
return n & (-n)
#在i上加v
def add(i,v,b):
while i<=n:
b[i]+=v
i+=lowbit(i)
#从第1个数字到到第i个数字
def query(b,i):
ans=0
while i>0:
ans+=b[i]
i-=lowbit(i)
return ans
b=[0]*(n+1)
#树状数组
for i in range(1,n+1):
add(i,a[i],b)
BFS广度优先搜索
类似于树的层次搜索,答案不唯一
graph={"A":["B","C"],
"B":["A","C","D"],
"C":["A","B","E"],
"D":["B","C","E"],
"E":["C","D","F"],
"F":["D","E"]}
def BFS(graph,start):
queue=[] 用队列来存储,也可用双向队列deque() from collections import deque
queue.append(start)
s=set() 用集合来存储该点是否被访问了
s.add(start)
while len(queue)>0:
a=queue.pop(0)
print(a,end=' ')
for i in graph[a]:
if i not in s:
queue.append(i)
s.add(i)
BFS(graph,"C")
#C A B E D F
DFS深度优先搜索
用栈来存储,先进后出
#第二种方法建立一个parent字典存储访问某一个点前是访问哪一个点,就可以从任意一个点到任意点的路径
graph={"A":["B","C"],
"B":["A","C","D"],
"C":["A","B","E"],
"D":["B","C","E"],
"E":["C","D","F"],
"F":["D","E"]}
def BFS(graph,start):
stack=[]
stack.append(start)
s=set()
s.add(start)
#parent={start:None}
while len(stack)>0:
a=stack.pop()
print(a,end=' ')
for i in graph[a]:
if i not in s:
stack.append(i)
s.add(i)
#parent[i]=a
#print(parent)
#return parent
BFS(graph,"B")
'''
v="B"
while v!=None:
print(v)
v=parent[v]
'''
#B D E F C A
Dijkstra算法
求解起始点到其他各点的最短距离
需要有一个元组记录一个点被访问的前一个点(parent),一个see集合记录已经访问过的点,一个距离元组记录开始点到各点的最短距离(distance)
用堆队列pqueue来更新起始点到其他各点的距离,堆队列能根据距离自动排序
import heapq #用堆存储,能够根据距离自动排序
import math
graph={"A":{"B":5,"C":1},
"B":{"A":5,"C":2,"D":1},
"C":{"A":1,"B":2,"E":8},
"D":{"B":1,"C":4,"F":6},
"E":{"C":8,"D":3},
"F":{"D":6}}
def distan(graph,s):
distance={s:0}
for i in graph.keys():
if i != s:
distance[i]=math.inf
return distance
def dijkstra(graph,s):
parent={s:None}
see=set()
distance=distan(graph,s)
pqueue=[]
heapq.heappush(pqueue,(0,s)) #用堆压进去队列里,能够根据距离自动排序
while len(pqueue)>0:
a=heapq.heappop(pqueue)
dis=a[0]
see.add(a[1])
node=graph[a[1]].keys()
for i in node:
if i not in see:
if dis+graph[a[1]][i]<distance[i]:
heapq.heappush(pqueue,(dis+graph[a[1]][i],i))
parent[i]=a[1]
distance[i]=dis+graph[a[1]][i]
return distance,parent
distance,parent=dijkstra(graph,"B")
print(distance)
print(parent)
#输出
{'B': 0, 'A': 3, 'C': 2, 'D': 1, 'E': 10, 'F': 7}
{'B': None, 'A': 'C', 'C': 'B', 'D': 'B', 'F': 'D', 'E': 'C'}
快速幂
以为例说明如何用倍增法做快速幂。
(1)a^11=a^(8+2+1),11的二进制为1101=2^3+2^2+2^0=8+4+1
(2)如何跳过那些没有的幂次?例如1011需要跳过。做个判断,用二进制的位运算实现:
n & 1 取n的最后一位,并且判断这一位是否需要跳过
n >>=1 把n右移一位,目的是把刚处理过的n的最后一位去掉。
取模a^n mod m=(a mod m)^n mod m
def quick(a,n):
ans=1
while n:
if n&1:
ans=ans*a
a*=a #a^2,a^4....
n>>=1
print(ans)
return ans
def quickmod(a,n,m):
ans=1
while n:
if n&1:
ans=(ans*a)%m
a=a*a%m
n>>=1
print(ans)
return ans
quick(2,10)
quickmod(2,10,3)
排序
选择排序
每次将最小的元素移到前边
def selectsort(a):
for j in range(len(a)-1): #n个元素只需要进行n-1次
for i in range(j+1,len(a)):
if a[j]>a[i]:
a[j],a[i]=a[i],a[j]
print(a)
return a
a=[8,5,6,4,3,7,10,2]
selectsort(a)
输出
[2, 8, 6, 5, 4, 7, 10, 3]
[2, 3, 8, 6, 5, 7, 10, 4]
[2, 3, 4, 8, 6, 7, 10, 5]
[2, 3, 4, 5, 8, 7, 10, 6]
[2, 3, 4, 5, 6, 8, 10, 7]
[2, 3, 4, 5, 6, 7, 10, 8]
[2, 3, 4, 5, 6, 7, 8, 10]
冒泡排序
相邻两两进行比较,将最大的数一直移到最后面
def maopaosort(a):
for j in range(len(a)-1,0,-1): #n个数只需要进行n-1次
for i in range(j):
if a[i]>a[i+1]:
a[i],a[i+1]=a[i+1],a[i]
print(a)
return a
a=[8,5,6,4,3,7,10,2]
maopaosort(a)
输出
[5, 6, 4, 3, 7, 8, 2, 10]
[5, 4, 3, 6, 7, 2, 8, 10]
[4, 3, 5, 6, 2, 7, 8, 10]
[3, 4, 5, 2, 6, 7, 8, 10]
[3, 4, 2, 5, 6, 7, 8, 10]
[3, 2, 4, 5, 6, 7, 8, 10]
[2, 3, 4, 5, 6, 7, 8, 10]
归并排序
分组,每组进行比较,直到所有有序
例如:[8,5,6,4,3,7,10,2]
[8,5,6,4] [3,7,10,2]
[8,5][6,4][3,7][10,2]
不能再继续分下去(start=end),则对他们进行排序,再两两合并
def merge(a,start,mid,end):
temp=[]
i=start
j=mid+1
while i<=mid and j<=end:
if a[i]>a[j]:
temp.append(a[j])
j+=1
else:
temp.append(a[i])
i+=1
temp.extend(a[i:mid+1])
temp.extend(a[j:])
for i in range(start,end+1):
a[i]=temp[i-start]
#从后面的半部分开始时需要结合start将temp赋值给a中的i位置
print(a)
return a
def metgesort(a,start,end):
if start==end:
return
mid=(start+end)//2
metgesort(a,start,mid)
metgesort(a,mid+1,end)
merge(a,start,mid,end)
a=[8,5,6,4,3,7,10,2]
metgesort(a,0,7)
输出
[5, 8, 6, 4, 3, 7, 10, 2]
[5, 8, 4, 6, 3, 7, 10, 2]
[4, 5, 6, 8, 3, 7, 10, 2]
[4, 5, 6, 8, 3, 7, 10, 2]
[4, 5, 6, 8, 3, 7, 2, 10]
[4, 5, 6, 8, 2, 3, 7, 10]
[2, 3, 4, 5, 6, 7, 8, 10]
插入排序
对于前面 j-1个元素有序,将a[j]与前 j-1个元素进行比较并插入,使用复制替换
def insertsort(a,j):
for i in range(j):
if a[i]>a[j]:
a[i],a[j]=a[j],a[i]
print(a)
return a
a=[8,5,6,4,3,7,10,2]
for i in range(1,len(a)):
insertsort(a,i)
输出
[5, 8, 6, 4, 3, 7, 10, 2]
[5, 6, 8, 4, 3, 7, 10, 2]
[4, 5, 6, 8, 3, 7, 10, 2]
[3, 4, 5, 6, 8, 7, 10, 2]
[3, 4, 5, 6, 7, 8, 10, 2]
[3, 4, 5, 6, 7, 8, 10, 2]
[2, 3, 4, 5, 6, 7, 8, 10]
桶排序
将元素按大小分别放在几个桶里,对桶内元素进行排序,再从第1个桶开始合并桶里排列好的数据
import math
def selectsort(a):
for j in range(len(a)-1):
for i in range(j,len(a)):
if a[i]<a[j]:
a[i],a[j]=a[j],a[i]
return a
def bucketsort(a):
maxA=max(a)
minA=min(a)
temp=[[],[],[]]
bucketgroup=3 #桶的个数
innumber=math.ceil((maxA-minA)/bucketgroup) #每个桶的元素数量max
for i in a:
j=(i-minA)//innumber #应该放在哪一个桶里
temp[j].append(i)
print(temp) #输出每个桶分配的元素情况
result=[]
for i in range(bucketgroup):
b=selectsort(temp[i])
result.extend(b)
print(result)
return result
a=[8,5,6,4,3,7,10,2]
bucketsort(a)
[[4, 3, 2], [5, 6, 7], [8, 10]]
[2, 3, 4, 5, 6, 7, 8, 10]
计数排序
需要一个计数器来记录数组中的元素个数,元素范围为 [1,9]
def countsort(a):
n=len(a)
maxa=max(a)
count1=[0]*(maxa+1)
for i in a:
count1[i]+=1
print(count1) #打印计数器
temp=[]
for i in range(1,maxa+1):
if count1[i]>0:
temp.extend([i]*count1[i])
print(temp)
a=[8,5,6,4,2,7,7,2]
countsort(a)
输出
[0, 0, 2, 0, 1, 1, 1, 2, 1] #计数器
[2, 2, 4, 5, 6, 7, 7, 8]
基数排序
类似于桶排序,建立9个桶,分别先对个位数进行分类,再依次输出,再对十,百...位进行分类,直到最大的那一位
def radixsort(a):
base=1
maxa=max(a)
while base<maxa:
count1=[] #创建计数器
for i in range(10):
count1.append([])
#分类
for num in a:
j=num//base%10
count1[j].append(num)
#输出
temp=[]
for k in range(10):
if count1[k]:
temp.extend(count1[k])
a=temp
base*=10
print(a)
a=[81,587,674,4475,2235,774,745,4212]
radixsort(a)
输出
[81, 4212, 674, 774, 4475, 2235, 745, 587]
[4212, 2235, 745, 674, 774, 4475, 81, 587]
[81, 4212, 2235, 4475, 587, 674, 745, 774]
[81, 587, 674, 745, 774, 2235, 4212, 4475]
ST表
用于求区间的最值
import math
#ST表的创建
def st(arr):
n=len(arr)
m=int(math.log(n,2))+1
ST=[[0]*m for i in range(n)] #ST[i][j]表示左端点为i,长度为2^j区间的最值,也就是[i,i+2^j-1]
for i in range(n):
ST[i][0]=arr[i]
for j in range(1,m):
for i in range(n):
if i+2**j-1<n:
ST[i][j]=max(ST[i][j-1],ST[i+2**(j-1)][j-1])
return ST
#求某个区间的最值
def query(ST,l,r):
x=int(math.log(r-l+1,2))
print(max(ST[l][x],ST[r-2**x+1][x]))
return max(ST[l][x],ST[r-2**x+1][x]) #将区间拆成长度相同的两段
arr=[9,3,1,7,5,6,0,8]
ST=st(arr)
print(ST)
query(ST,2,7)
0-1背包问题
也就是在容量为M的背包里,放入n种物品(第i种物品的重量为wi,价值为vi),使得价值最大,每一种物品只有一个
二维数组
m=10 #背包重量
n=4 #物品
a=[[0,0],[2,1],[3,3],[4,5],[7,9]]
dp=[[0]*(m+1) for i in range(n+1)] dp[i][j]为第i种物品在容量为j时的最大价值
print(dp)
for i in range(n+1):
for j in range(m+1):
if i==0 or j==0:
dp[i][j]=0
else:
if a[i][0]>j:
dp[i][j]=dp[i-1][j] 不拿
else:
dp[i][j]=max(dp[i-1][j],dp[i-1][j-a[i][0]]+a[i][1]) 拿
print(dp)
输出
[[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[0, 0, 1, 3, 3, 4, 4, 4, 4, 4, 4],
[0, 0, 1, 3, 5, 5, 6, 8, 8, 9, 9],
[0, 0, 1, 3, 5, 5, 6, 9, 9, 10, 12]]
一维滚动数组
m=10 #背包重量
n=4 #物品
a=[[0,0],[2,1],[3,3],[4,5],[7,9]] d[j]表示容量为j时价值最大为多少
dp=[0]*(m+1)
for i in range(1,n+1):
for j in range(m,a[i][0]-1,-1): 从尾开始填充
if a[i][0]<=j:
dp[j]=max(dp[j],dp[j-a[i][0]]+a[i][1])
print(dp)
输出
[0, 0, 1, 3, 5, 5, 6, 9, 9, 10, 12]
完全背包问题
与0-1背包问题不同的是,物品的数量是无限的
m=10 #背包重量
n=4 #物品
a=[[0,0],[2,1],[3,3],[4,6],[8,10]]
dp=[[0]*(m+1) for i in range(n+1)]
print(dp)
for i in range(n+1):
for j in range(m+1):
if i==0 or j==0:
dp[i][j]=0
else:
dp[i][j]=dp[i-1][j] #继承上一层的状态
if a[i][0]<=j:
dp[i][j]=max(dp[i][j],dp[i][j-a[i][0]]+a[i][1])
print(dp)
输出
[[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5],
[0, 0, 1, 3, 3, 4, 6, 6, 7, 9, 9],
[0, 0, 1, 3, 6, 6, 7, 9, 12, 12, 13],
[0, 0, 1, 3, 6, 6, 7, 9, 12, 12, 13]]
滚动数组
m=10 #背包重量
n=4 #物品
a=[[0,0],[2,1],[3,3],[4,6],[8,10]]
dp=[0]*(m+1)
print(dp)
for i in range(1,n+1):
for j in range(a[i][0],m+1):
dp[j]=max(dp[j],dp[j-a[i][0]]+a[i][1])
print(dp)
输出
[0, 0, 1, 3, 6, 6, 7, 9, 12, 12, 13]