2、 recursive / fibonacci 2.1 蛙跳 mem = {} def fun(n): global mem if n in mem: return mem[n] else: if n==1: mem[n] = 1 return 1 elif n==2: mem[n] = 2 return 2 else: t = fun(n-1)+fun(n-2) mem[n] = t return t class Solution: def jumpFloor(self, number): if number==1: return 1 elif number==2: return 2 elif number>2: f1 = 1 f2 = 2 for i in range(3,number+1): f3 = f1+f2 f1 = f2 f2 = f3 return f3 2.2 猴子登山 mem = {} def f(n): if n in mem: return mem[n] else: if n==1: mem[1] = 1 return 1 elif n==2: mem[2] = 1 return 1 elif n==3: mem[3] = 2 return 2 else: t = f(n-1)+f(n-3) mem[n] = t return t print(f(m)) #--------------------------------- f1 = 1 f2 = 1 f3 = 2 for i in range(3,m): f4 = f1+f3 f1 = f2 f2 = f3 f3 = f4 print(f4) 2.3 汉诺塔 把n个盘子从Left 借助 Mid,移动到Right柱子上,可以分为以下三步: 1、把n-1个盘子从Left 借助 Right,移动到Mid柱子上 2、把剩下最大的那一个盘子从Left移动到 Right柱子上 3、把n-1个盘子从Mid 借助 Left,移动到,Right柱子上 out = [] def hanoi(n, left, mid, right): if n==0: return if n==1: t = 'move from ' + left + ' to ' + right out.append(t) return hanoi(n-1, left, right, mid) hanoi(1, left, mid, right) hanoi(n-1, mid, left, right) 2.4 放苹果 f(m,n)表示将m个苹果放入n个盘子中的摆放方法总数, 放苹果分为两种情况,一种是有1个盘子为空 f(m,n-1),另一种是每个盘子上至少有1个苹果f(m-n,n), 递推关系:f(m,n) = f(m,n-1) + f(m-n,n) 边界条件:当m==1 or n==1时,f(m,n) =1 def f(m,n): if m<0 or n<0: return 0 elif m==1 or n==1: return 1 else: return f(m,n-1) + f(m-n,n) while 1: try: m,n = map(int,input().strip().split()) print(f(m,n)) except: break 3、 dfs / bfs / back 3.1 组合 def dfs(iStart,res): s = sum(res) # 路径和 if s>target: return if s==target: if res not in out: # 这里也可以去重 out.append(res) # 保存路径 return # 返回空回溯 for i in range(iStart,n): dfs(i+1,res+[num[i]]) # i+1,增加路径 dfs(iStart,res) 3.2 排列 def dfs(num,res): if not num: if res not in out: out.append(res) for i in range(len(num)): dfs(num[:i]+num[i+1:],res+[num[i]]) dfs(num,res) 3.3 数独 def dfs(k, res): if k==n: # 如达到最后一个节点 #print(res) return res # 返回第一个合法解,如无返回可打印所有合法解 if 0<=k<=n-1: # 限定搜索范围保证访问不越界 i = I[k] # 取行值 j = J[k] # 取列值 for v in value: # 遍历当前节点每个候选解 if check(S,i,j,v): # 检查当前节点当前候选解合法性 S[i][j] = v # 如合法赋值 solve_opt = dfs(k+1, res+[v]) # 递归进入下一个节点,第二个参数加入当前节点合法解。dfs()的返回值solve只有两种情况, if solve_opt: # 1、为空,说明下一个节点几个候选解check()都返回false,即本节点当前候选解不对, return solve_opt # 本节点要回溯执行下一个候选解,因此要恢复S[i][j]=0,对一下个候选解执行check() S[i][j] = 0 # 2、不为空时,说明到达终点k==n,solve返回所有路径点res solve_opt = dfs(0, res) 3.4 迷宫 xy0 = [0,0] xy1 = [r-1,c-1] MOV = [[1,0],[0,1],[-1,0],[0,-1]] # 四个方向移动一格 #--------------------------------------------------------------------------------- # 递归调用dfs(xy_new, res+[xy_new])时, res+[xy_new]实际保存了不断增长的路径 res = [xy0] def dfs(xy0, res): # xy0为当前搜索点,res保存了当前点历史路径 maze[xy0[0]][xy0[1]] = 9 if xy0[0] == xy1[0] and xy0[1] == xy1[1]: shortest_path = res return shortest_path # 到达终点时读取res返回最路径 for mov in MOV: # 四个方向移动 xy_new = [xy0[0]+mov[0], xy0[1]+mov[1]] # 移动后的新坐标 if 0<=xy_new[0]<=r-1 and 0<=xy_new[1]<=c-1 and maze[xy_new[0]][xy_new[1]] == 0: # 坐标有效且没有被访问过 shortest_path = dfs(xy_new, res+[xy_new]) # 调用下一个节点,第二个参数传递路径 if shortest_path: # 只有dfs返回不为空时,才能返回,空时不能返回,程序会提前结束 return shortest_path #============================================================================================================================= xy0 = [0,0,-1] xy1 = [r-1,c-1,-1] MOV = [[1,0],[0,1],[-1,0],[0,-1]] # 四个方向移动一格 #--------------------------------------------------------------------------------- # Path中的每一行存有路径坐标点,第三个值保存上一个节点的list索引,从0开始 def road(visited): shortest_path = [] shortest_path.append(visited[-1][:-1]) t = visited[-1][-1] while t!=-1: shortest_path.append(visited[t][:-1]) t = visited[t][-1] return shortest_path[::-1] #--------------------------------------------------------------------------------- # BFS(宽度优先搜索)解迷宫问题 def bfs(xy0): queue = [xy0] # 开始点进入队列 visited = [] # 记录所有访问过的点,以及每一个点前一个节点索引 k = 0 while queue: # 队列不为空即运行 xy = queue.pop() # 队列中取出一个新坐标点 maze[xy[0]][xy[1]] = 9 # 开始点标记为已访问值9 visited.insert(k, xy) # 相当于path.append(xy),这样写更清楚 if xy[0] == xy1[0] and xy[1] == xy1[1]: shortest_path = road(visited) # 到达终点时,反向寻找来时路径 return shortest_path for mov in MOV: # 四个方向移动 xy_new = [xy[0]+mov[0], xy[1]+mov[1]] # 移动后的新坐标 if 0<=xy_new[0]<=r-1 and 0<=xy_new[1]<=c-1 and maze[xy_new[0]][xy_new[1]] == 0: # 坐标有效且没有被访问过 xy_new = [xy_new[0],xy_new[1],k] # 将前一个点的索引加入坐标之后(用于反向寻找路径) queue.insert(0, xy_new) # 新坐标不为终点,新坐标入队列, k += 1 3.5 单源最短路 1、对于节点距离,建立一个字典dict_t,以字符形式存储key,距离以数字形式保存在value中,重边取最小 2、定义一个函数dis(i,j):第i节点到第j个节点的距离,访问距离字典,有值返回,无通路返回None 3、dfs搜索,第二个参数传递走过的距离,到达终点时取最小输出,nonlocal关键字的使用 class Solution: def findShortestPath(self , n , m , graph ): # write code here dict_t = {} for ijdis in graph: key = str(ijdis[0])+'-'+str(ijdis[1]) if key not in dict_t: dict_t[key] = ijdis[2] else: dict_t[key] = min(ijdis[2],dict_t[key]) #print(dic) def dis(i,j): key = str(i)+'-'+str(j) if key not in dict_t: return None else: return dict_t[key] #print(dis(1,2)) #print(dis(2,3)) #print(dis(3,4)) res = 0 minpath = float('inf') def dfs(i,res): nonlocal minpath if i==n: if res<minpath: minpath = res for j in range(1,n+1): if dis(i,j): dfs(j,res+dis(i,j)) dfs(1,res) print(minpath) return minpath 4、 stack / queue 4.1 火车进站 目隐藏条件是N趟火车的进站始终按照原本的火车列表顺序,就是假如有两列火车A,B,B不可能在A之前进站 关键的几个变量说明: 1、cur_idx,当前需要进站处理的火车 2、in_trains、已经进站的火车列表 3、out_trains、已经出站的火车列表 4、res、火车所有可能的出站列表 #-------------------------------------------------------------------- # 两点注意: # 1、train, station, out在计算过程中不要有赋值操作 # 2、压栈操作[1,2,3]+[4] = [1,2,3,4],这里4如果是单独一个数要加[] def fun(train, station, out): #print('train=',train) #print('station=',station) #print('out=',out) if train and station == []: # 车站为空,仅火车进站 #print('1') t = train[-1] fun(train[:-1], station + [t], out) elif train == [] and station: # 火车为空,车站火车出站 #print('2') rec.append(out + station[::-1]) return else: # 车站和火车都不为空,有两种选择 #print('3') t = train[-1] fun(train[:-1], station + [t], out) # 火车进站 t = station[-1] fun(train, station[:-1], out + [t]) # 车站火车出站 4.2 表达式求值 解题思路: 1、中缀表达式转为后缀表达式 2、为后缀表达式计算规则:从左到右遍历表达式的每个数字和符号,遇到的是数字就进栈, 遇到的时符号就将栈顶的两个数字出栈进行计算,然后将计算结果入栈,最终栈里的值即为计算的结果。 3、预处理:将字符串按实际物理意义,转成字符列表(含多位数和负数、空格、[]、{}) ''' #---------------------------------------- # 输出num1 + operator + num2结果 def getvalue(num1,num2,operator): if operator == '+': return num1 + num2 elif operator == '-': return num1 - num2 elif operator == '*': return num1 * num2 elif operator == '/': return num1 / num2 else: return [] #---------------------------------------- # 优先级 operator1 >= operator2,输出真 def compare(operator1,operator2): if operator1 in '*/' and operator1 in '*/': return True elif operator1 in '+-' and operator1 in '+-': return True elif operator1 in '*/' and operator1 in '+-': return True elif operator1 in '+-' and operator1 in '*/': return False else: return [] #---------------------------------------- # 中缀表达式转为后缀表达式 # 输入L = ['0','-','1','*','(','0','-','1','-','1',')','-','3','*','(','1','-','2',')','-','3','*','4'] # 输出express= ['0',1','0','1','-','1','-','*','-','3','1','2','-','*','-','3','4','*','-'] def suffix_express(L): express = [] # 后缀表达式 stack = [] # 存中间符号的栈 for i in L: if i.isnumeric(): # 数字直接入表达式 express.append(i) elif i in '+-': # 遇到+—号,如果符号栈空,入符号栈 if len(stack) == 0: stack.append(i) else: # 如果符号栈不为空,符号栈优先级大于等于当前符号的,弹出入表达式 while stack and compare(stack[-1],i): express.append(stack.pop()) stack.append(i) # 当前符号入符号栈 elif i in '*/(': # */(直接入符号栈 stack.append(i) elif i in ')': # 遇到),(之前的符号弹出入表达式 while stack and stack[-1] != '(': express.append(stack.pop()) if stack: # 删除( stack.pop() while stack: # 最后栈中所有符号弹出入表达式 express.append(stack.pop()) return express #---------------------------------------- # 为后缀表达式计算规则: # 从左到右遍历表达式的每个数字和符号,遇到的是数字就进栈,遇到的时符号就将栈顶的两个数字出栈进行计算, # 然后将计算结果入栈,最终栈里的值即为计算的结果。 # 输入express= ['0',1','0','1','-','1','-','*','-','3','1','2','-','*','-','3','4','*','-'] # 输出计算结果 = result def compute(suffix_express): stack = [] for i in suffix_express: if i.isnumeric(): stack.append(i) elif i in '+-*/': n2 = stack.pop() n1 = stack.pop() stack.append(getvalue(int(n1),int(n2),i)) if stack: return stack.pop() return [] #---------------------------------------- # 预处理:将字符串按实际物理意义,转成字符列表(含多位数和负数、空格、[]、{}) # 输入:-1*(-1-1)-3*(1-2)-3*4 # 输出:['0','-','1','*','(','0','-','1','-','1',')','-','3','*','(','1','-','2',')','-','3','*','4'] def preprocess(L): B = '' for i in L: if i in '0123456789+-*/()[]{}': # 去空格 B += i B = B.replace('[','(') # 统一括号 B = B.replace(']',')') B = B.replace('{','(') B = B.replace('}',')') L = '('+ B +')' # 左右加括号用于判断负号 A = [] tmp = '' for i in range(1,len(L)-1): if L[i] == '-' and L[i-1]=='(': # 负号前必有括号,前加0将负号变减号 A.append('0') A.append('-') elif L[i].isnumeric() and L[i+1].isnumeric(): tmp += L[i] elif L[i].isnumeric() and not L[i+1].isnumeric(): tmp += L[i] A.append(tmp) tmp = '' else: A.append(L[i]) return A #String = input() String = '-1*(-1-1)-3*(1-2)-3*4' #print(String) List = preprocess(String) #print(List) express = suffix_express(List) #print(express) result = compute(express) print(String+' = '+str(result)) 4.3 矩阵乘法计算量估算 https://www.nowcoder.com/practice/15e41630514445719a942e004edc0a5b?tab=note # 多个矩阵连乘的总计算量,输入为多个矩阵的size组成的list二维数组 def times(size): n = len(size) tmp = 0 for t in range(n-1): tmp += size[0][0] * size[t][1] * size[t+1][1] return tmp while 1: try: n = int(input()) size = [] for i in range(n): size.append(list(map(int,input().strip().split()))) L = input() L= '(' + L + ')' # 左右加() stack = [] tmp = [] result = 0 k = 0 for i in range(1,len(L)-1): # 不考虑左右加的() if L[i] == '(': # 遇到(,(压栈 stack.append('(') elif L[i].isalpha(): # 遇到矩阵,size压栈 stack.append(size[k]) k += 1 elif L[i] == ')': # 遇到),之前所有矩阵size出栈,并反序 while stack and stack[-1] != '(': tmp.append(stack.pop()) if stack: stack.pop() tmp = tmp[::-1] stack.append([tmp[0][0],tmp[-1][-1]]) # 新矩阵size压栈 result += times(tmp) # 计算乘法计算量并累加 tmp = [] print(result) except: break 4.4 最长无重复子数组 方法一:用一个队列,把元素不停的加入到队列中,如果队列中有相同的元素,就把队首的相同元素及之前的元素都移除 方法二:使用两个指针,一个i一个j,最开始的时候i和j指向第一个元素,然后i往后移,把扫描过的元素都放到map中,如果i扫描过的元素没有重复的就一直往后移,顺便记录一下最大值max,如果i扫描过的元素有重复的,就改变j的位置 class Solution: def maxLength(self , arr ): queue = [] maxLen = 0 for i in arr: if i not in queue: queue.insert(0,i) else: while queue[-1] != i: queue.pop() queue.pop() queue.insert(0,i) if len(queue)>maxLen: maxLen = len(queue) return maxLen