往期内容:
关注我,持续更新本篇内容!有想要看的部分可以评论区留言
【可与python】数据结构与算法实现,内含思路讲解
线性结构
有唯一前驱和后继,两端
栈(stack) Last in Firts out (LIFO)
特性:次序反转
举例:undo,back操作
操作
- Stack() 创建栈
- push(item) 加入栈顶
- pop()移除
- peek()查看栈顶
- isEmpty() 是否空栈
- size() 栈的大小
python 中 可以将list右端看作栈顶
class Stack():
'''将list [-1]作为栈顶,比选择[0]做栈顶更快(时间复杂度)'''
# - Stack() 创建栈
# - push(item) 加入栈顶
# - pop()移除栈顶元素
# - peek()查看栈顶
# - isEmpty() 是否空栈
# - size() 栈的大小
def __init__(self):
self.items=[]
def push(self,item):
self.items.append(item)
def pop(self):
self.items.pop()
def peek(self):
return self.items[-1]
def isEmpty(self):
return self.items==[]
def size(self):
return len(self.items)
栈的简单应用
括号匹配
例如[[][[]][][][]]
从左到右扫描,左括号加入栈顶,右括号与栈顶匹配
结束条件:不匹配、扫描完成后没有右括号但栈不为空
十进制转换为二进制
对二求余数不断求余数后的每一个数放在栈里push,求完后pop每一个数
注意最先求出的数实际上是最低位数,例如4的二进制是100,最后求出的是1但是1最先出,所以满足栈,
中缀表示法
A+BC 优先级
括号 强制优先级
全括号表达式 (A+(BC))
中缀表示: A+B
前缀表示: +AB
后缀表示: AB+
中缀表示: A+B*C
前缀表示: +A*BC
后缀表示: ABC*+
中缀表示:(A+B)*C
后缀表示: AB+C*
在全括号表达式 (A+(B*C)) 将*移动至其左括号匹配的)处将其替代并去掉*对应的),则有(A+BC*) 后缀表达式的第一步,以此类推
没有括号时,中缀表示: A+B*C,虽然+先出现但是优先级比*低,所以先保存到栈里,等*处理完最后输出,
若遇到的下一个运算符更低或遇到右括号,直接输出,若不低则进入栈,
代码中用字典保存优先级,例如p={‘*’:2,“+”:1}
后缀表达式求值
操作符只作用于离他最近的操作数,所以最后入栈的AB先计算
队列(Queue) First in First out (FIFO)
操作
- Queue() 创建队列
- enqueue(item) 加入队列
- dequeue()队首移除
- isEmpty() 是否空
- size() 大小
class Queue():
'''将list [-1]作为首端'''
# - Queue() 创建队列
# - enqueue(item) 加入队列
# - dequeue()队首移除
# - isEmpty() 是否空
# - size() 大小
def __init__(self):
self.items=[]
def enqueue(self,item):
self.items.insert(0,item)
#0()n复杂度
def dequeue(self):
self.items.pop()
def isEmpty(self):
return self.items==[]
def size(self):
return len(self.items)
队列的简单应用
约瑟夫问题
打印队列
双端队列Deque
两端都可进可出
- Deque()
- addFront()
- addRear()
- removeFront()
- removeRear()
- isEmpty()
- size()
列表
(无序表)相关位置
- List()
- add(item) ======> insert(4,item),append(item)
- remove(item) ===> pop(index(item)),pop()
- search(item) ===> index(item)
- isEmpty()
- size()
无序列表:链表
表头…–>C—>B---->A—>|尾
新加入F
表头…F—>C—>B---->A—>|尾
利用节点实现
class Node():
# 链表准备工作
def __int__(self,initdata):
self.data = initdata
self.next = None
def getData(self):
return self.data
def getNext(self):
return self.next
def setData(self,newdata):
self.data = newdata
def setNext(self,newnext):
self.next = newnext
class UnorderedList():
def __init__(self):
self.head = None
def isEmpty(self):
return self.head == None
def add(self,item):
temp = Node(item)
temp.setNext(self.head)
self.head = temp
def size(self):
current = self.head
count = 0
while current != None:
count = count + 1
current = current.getNext()
return count
def search(self,item):
current = self.head
found = False
while current !=None and not found:
if current.getData()==item:
found = True
else:
current = current.getNext()
return found
def remove(self,item):
# 如果current是要删除的节点,则将current前一个节点的next连接到current的后一个节点,也就是previous,
# 如果previous后一个节点是None,也就是表头,那么current的前一个节点直接连接到表头
previous = self.head
current = None
found = False
while not found:
if current.getData() == item:
found = True
else:
previous = current
current = current.getNext()
if previous == None:
self.head = current.getNext() #None-->found--->B-->A---| None--->B--->A--->|
else:
# None--> C -->found - -->B -->A - -- | None--> C -->B -->A - -- |
previous.setNext = current.getNext()
return found
有序表
依照可比性质排列
例如:
表头…–>9—>5---->3—>|尾
递归
将问题分解为规模更小的相同问题
调用自身
# 前n项和
def n_sum(n):
while n==1:
return 1
else:
return n+n_sum(n-1)
print(n_sum(6))
规律
- 基本结束条件(最小规模问题直接解决)
- 算法必须改变状态向基本结束条件演进(减小问题规模)
- 递归算法必须调用自身(解决减小了规模的相同问题)
修改系统递归最大值
# 在python中内置的sys模块可以获取和调整最大递归深度
import sys
sys.getrecursionlimit() # 返回 1000
sys.setrecursionlimit(3000) # 此时修改为3000
递归图形
使用turtle作图
螺旋线
import turtle
t = turtle.Turtle()
def drawSpiral(t,linelen):
if linelen >0:
t.forward(linelen)
t.right(90)
drawSpiral(t,linelen-5)
drawSpiral(t,100)
turtle.done()
分形树(自相似递归图形)
分形Fractal
t = turtle.Turtle()
def tree(brach_len):
if brach_len>=5: #树干太短结束
t.forward(brach_len) #画树干
t.right(20)
tree(brach_len-15)
t.left(40)
tree(brach_len - 15)
t.right(20) #回正
t.backward(brach_len) #退回原位置
t.left(90)
t.penup()
t.backward(100) #画笔起始在中央,penup走的时候不留痕迹到底边
t.speed(1)
# “fastest”: 0 最快
# “fast”: 10 快
# “normal”: 6 正常
# “slow”: 3 慢
# “slowest”: 1 最慢
# 速度值从 1 到 10,画线和海龟转向的动画效果逐级加快
t.pendown()
t.pencolor("green")
t.pensize(2)
tree(70)
t.hideturtle()
turtle.done()
谢尔宾斯基Sierpinski 三角形
分形构造,对等边三角形中间挖空,挖空后对其余三角形重复以上操作
面积为0,周长无穷,介于1维与2维之间的分数维(约1.585维)构造
from turtle import*
def drawTriangle(points,color, myTurtle):
myTurtle.fillcolor(color)
myTurtle.up( )
myTurtle.goto(points[0])
myTurtle.down( )
myTurtle.begin_fill()
myTurtle.goto(points [1])
myTurtle.goto(points[2])
myTurtle.goto(points[0])
myTurtle.end_fill()
def getMid(p1, p2):
return ( (p1[0] +p2[0])/2,(p1[1] + p2[1]) / 2)
def sierpinski(points,degree,myTurtle) :
colormap = [ 'blue', 'red' , 'green' , 'white', 'yellow',
'violet' , 'orange' ]
drawTriangle(points, colormap[ degree] , myTurtle)
if degree > 0 :
sierpinski( [points [0],
getMid(points [0], points [1]),getMid(points[0], points[2])],degree-1,myTurtle)
sierpinski( [points[1],
getMid(points [0], points[1]),getMid(points [1], points[2])],degree-1, myTurtle)
sierpinski( [points[2],
getMid(points[2], points[1]),getMid(points[0], points [2])],degree-1,myTurtle)
myTurtle = Turtle( )
mywin = myTurtle.getscreen( )
myPoints = [ (-500,-250),(0,500),(500,-250)]
sierpinski(myPoints, 5,myTurtle)
mywin.exitonclick( )
复杂递归问题
汉诺塔问题
#汉诺塔问题
def moveTower(height, fromPole,withPole,toPole):
if height >=1:
moveTower(height-1,fromPole,toPole,withPole) #把最大disk上的tower(不包含最大的disk)从from挪到withPole
moveDisk(height,fromPole,toPole) #挪动最大的的disk(用此时的总高度表示最底层disk)到toPole
moveTower(height-1,withPole,fromPole,toPole)
def moveDisk(disk,fromPole,toPole):
print(f"move {disk} from {fromPole} to {toPole}")
moveTower(2,"A","B","C")
探索迷宫
分治策略
优化(贪心、递归、动态规划)
找零兑换问题
贪心策略解法
每次一尽量大的一部分对应到兑换硬币问题,每次以最多数量的最大面值来迅速减少找零面值
递归解法
从最大问题往上递推
def recMC(coinValuelist,change):
minCoins = change
if change in coinValuelist:
return 1
else:
return 1+min([ recMC(coinValuelist,change-k) for k in coinValuelist if k < change ] ) #k<change,把硬币大于面值的去掉
# 不懂的话使用以下代码
# for i in [k for k in coinValuelist if k<change]: #硬币面值小于change的硬币面值集合
# numCoins = 1 + recMC(coinValuelist,change-i)
# if numCoins < minCoins:
# minCoins = numCoins
# return minCoins
print(recMC([1,5,10,25],63))
#耗时长! 重复计算!
加入记录表memo
def recMC(coinValuelist,change,knownResults):
minCoins = change
if change in coinValuelist:
knownResults[change] =1
return 1
elif knownResults[change]>0:
return knownResults[change] #记录表,避免重复计算
else:
for i in [k for k in coinValuelist if k<change]: #硬币面值小于change的硬币面值集合
numCoins = 1 + recMC(coinValuelist,change-i,knownResults)
if numCoins < minCoins:
minCoins = numCoins
knownResults[change]=minCoins
return minCoins
print(recMC([1,5,10,25],63,knownResults=[0]*64))
动态规划解法
大问题的最优解包含每一个更小问题的最优解
从最小问题推到最大问题
对于找零问题,change为1一直往上推
def dynMC(coinValuelist,change,knownResults):
for cents in range(1,change+1):
minCoints = cents #初始化一个最大值
for i in [k for k in coinValuelist if k<= cents]: #硬币面值小于change的硬币面值集合, 找零一次后的所有出路
if knownResults[cents-i]+1 < minCoints: #上面完成1次找零,这里加1
minCoints = knownResults[cents-i]+1
knownResults[cents] = minCoints
return knownResults[change]
print(dynMC([1,5,10,21,25],63,knownResults=[0]*64))
回溯输出
def dynMC(coinValuelist,change,knownResults,coinsUsed):
for cents in range(1,change+1):
minCoints = cents #初始化一个最大值
newCoin = 1 #初始化新的硬币
for i in [k for k in coinValuelist if k<= cents]: #硬币面值小于change的硬币面值集合, 找零一次后的所有出路
if knownResults[cents-i]+1 < minCoints: #上面完成1次找零,这里加1
minCoints = knownResults[cents-i]+1 #记录最小coints使用
newCoin = i #记录对应面值
knownResults[cents] = minCoints
coinsUsed[cents] = newCoin #记录本次新加的硬币
return knownResults[change]
print(dynMC([1,5,10,21,25],63,knownResults=[0]*64,coinsUsed=[0]*64))
def printCoins(coinUsed, change):
coin = change
while coin>0:
thisCoin = coinUsed[coin]
print(thisCoin)
coin = coin-thisCoin
背包问题
动态规划
item | weight | value
1 | 2 | 3
2 | 3 | 4
3 | 4 | 8
4 | 5 | 8
5 | 9 | 10
限制weight<=20
求最大value
思路:
设m(i,w)为装i个物品,共重w时的价值
tr[i]={“w”:w_i ,“v”:v_i} 为第i个物品的weight和value
则:
m(i,w)=
- 0, i=0
- 0, w=0
- m(i-1,w),w_i > w 即当装第i件物品,但第i件大于背包承重,所以没有转
- max(m(i-1,w),m(i-1,w-w_i)+v_i), otherwise 即当装或不装第i件物品
tr = [None,{"w":2,"v":3},{"w":3,"v":4},{"w":4,"v":8},{"w":5,"v":8},{"w":9,"v":10}] # index==0设置为None,与item编号对齐
max_W = 20 # 背包最大承重
m = {(i,w):0 for i in range(len(tr)) for w in range(max_W+1)} #初始化所有的value值为0
#注意这时包括了思考的前两种情况
for i in range(1,len(tr)): # 实际上len(tr)==5+1, 所以这里 [1,5],后面编号也i也就对应item[i],巧妙!
for w in range(1,max_W+1): #要包括最大承重本身
if tr[i]["w"]>w:
m[(i,w)]=m[(i-1,w)]
else:
m[(i,w)]= max(m[(i-1,w)],m[(i-1,w-tr[i]["w"])]+tr[i]["v"])
print(m[(len(tr)-1,max_W)])
未完待更…