【递归】
通过LeetCode上【70. 爬楼梯】学习(建议)
class Solution(object):
def climbStairs(self, n):
"""
:type n: int
:rtype: int
"""
condition = [0] * (n + 1)
condition[0] = 1
condition[1] = 1
for i in range(2, n+1):
condition[i] = condition[i-1] + condition[i-2]
return condition[n]
【回溯】
利用回溯算法求解八皇后问题
"""
八皇后问题:
回溯算法
"""
# 设置棋盘的大小规模
max_coordinate = 8
# 构建数据结构
# 初始化坐标列表,
# 列表的value为纵坐标值(即棋盘上行数的标记值),
# 列表的index为横坐标值(即棋盘列数标记值)
queen_list = [None]*8
def show():
column = 0
# 对列进行遍历,打印坐标
while column < max_coordinate:
print("(%d, %d)" % (column, queen_list[column]), end=" ")
column += 1
print("")
# 对第棋盘上第column列的情况进行检查,看看是否能够放置皇后
def check(column):
column_2 = 0
# 对比column小的列进行遍历
while column_2 < column:
# 如果比column小的列中有和column对应的queen_list的值相等(即在同一行),
# 或者有二者的行标记之差的绝对值等于列标记之差的情况(即在其对角线上),
# 则不能放置该皇后
if (queen_list[column_2] == queen_list[column]) or (abs(queen_list[column_2] - queen_list[column]) == column - column_2):
return False
column_2 += 1
# 否则,可以放置该皇后
return True
# 回溯算法的递归函数主体
# 传入一个初始的横坐标值(即对应棋盘上的列数的标记值)
def put_queen(column):
row = 0
#
# 对棋盘的行进行遍历:0~7行
while row < max_coordinate:
# 假设该皇后可以放置在棋盘的第row行,第column列上
queen_list[column] = row
# 对第棋盘的column列进行检查,如果满足条件则进行下一列的放置
if check(column):
# 如果已至最后一列,则调用显示方法,打印结果,跳出递归
if column == max_coordinate - 1:
show()
else:
# 如果未至最后一列,则递归调用自身,实现在下一列中放置另一个皇后
put_queen(column + 1)
row += 1
def main():
put_queen(0)
print("="*10)
if __name__ == '__main__':
main()
"""
八皇后问题
递归回溯算法
"""
def queen(queen_list, current_column=0):
for row in range(len(queen_list)):
# 如果已至最后一列,则打印结果,跳出递归
if current_column == len(queen_list):
for i in range(len(queen_list)):
print("(%d, %d)" % (i, queen_list[i]), end=" ")
print("")
return
# 假设当前列能够放置一个皇后,用queen_list的index记录列标,value记录行标
# flag为可行性的标记
queen_list[current_column],flag = row,True
# 对当前列之前的各列进行遍历
for column in range(current_column):
# 排除同行及对角线上的位置,将flag设置为False
if (queen_list[column] == row) or (abs(row - queen_list[column]) == current_column - column):
flag = False
# 只要有一个不满足的条件,就跳出遍历
break
# 如果可以放置,则递归调用自身,对下一列进行筛选
if flag:
queen(queen_list, current_column + 1)
queen([None]*8)
利用回溯算法求解 0-1 背包问题
bestV = 0 # 最大价值
currW = 0 # 当前背包重量
currV = 0 # 当前背包价值
bestx = None # 最优解路径
def backtrack(i):
global bestV,bestx,currV,currW,x
if i>= n:
if bestV<currV:
bestV = currV
bestx = x[:]
else:
if currW+w[i]<=c:
x[i]=1
currW += w[i]
currV += v[i]
backtrack(i+1)
currW -= w[i]
currV -= v[i]
x[i]=0
backtrack(i+1)
if __name__=='__main__':
n=6
c=10
w = [2, 2, 3, 1, 5, 2]
v = [2, 3, 1, 5, 4, 3]
x = [0 for i in range(n)]
backtrack(0)
print(bestV) #最大价值
print([i+1 for i in range(n) if bestx[i]]) #最优解路径
## 输出
# 15
# [2, 4, 5, 6]
【分治】
利用分治算法求一组数据的逆序对个数
#include "stdio.h"
int count=0;
void Merge(int r[],int r1[],int s,int m,int t) // 合并子序列
{
int i=s,j=m+1,k=s;
int b;
while(i<=m && j<=t){
if(r[i]<=r[j]){ // 取较小者放入r1[k]中
r1[k++]=r[i++];
}
else{
count+=m-i+1; // 若左边数大于右边数,则左边数及其后边数都大于该右边数
b=i;
while(b<=m){
printf("[%d,%d]\n",r[b],r[j]);
b++;
}
r1[k++]=r[j++];
}
}
while(i<=m) // 若第一个子序列没处理完,则进行收尾处理;下同
r1[k++]=r[i++];
while(j<=t)
r1[k++]=r[j++];
}
void MergeSort(int r[],int s,int t){ // 对序列r[s]~r[t]进行归并排序
int m,r1[1000],i;
if(s==t)
return ;
else{
m=(s+t)/2; // 划分
MergeSort(r,s,m); // 子问题1
MergeSort(r,m+1,t); // 子问题2
Merge(r,r1,s,m,t); // 合并
for(i=s;i<=t;i++)
r[i]=r1[i];
}
}
int main(){
int r[]={23,13,35,6,19,50,28,38,26,17,45},i;
MergeSort(r,0,10); // 三个参数分别为待查数组、起始下标、截止下标
for(i=0;i<=10;i++)
printf("%d ",r[i]);
printf("\n一共 %d个逆序对\n",count);
}
【动态规划】
0-1 背包问题
def bag(n, c, w, v):
value = [[0 for j in range(c + 1)] for i in range(n + 1)]
for i in range(1, n + 1):
for j in range(1, c + 1):
if j < w[i - 1]:
value[i][j] = value[i - 1][j]
else:
value[i][j] = max(value[i - 1][j], value[i - 1][j - w[i - 1]] + v[i - 1])
# 背包总容量够放当前物体,取最大价值
return value
def show(n, c, w, value):
print('最大价值为:', value[n][c])
x = [0 for i in range(n)]
j = c
for i in range(n, 0, -1):
if value[i][j] > value[i - 1][j]:
x[i - 1] = 1
j -= w[i - 1]
print('背包中所装物品为:')
for i in range(n):
if x[i]:
print('第', i+1, '个,', end='')
if __name__=='__main__':
n=6
c=10
w = [2, 2, 3, 1, 5, 2]
v = [2, 3, 1, 5, 4, 3]
value = bag(n,c,w,v)
show(n, c, w, value)
# 输出
'''
最大价值为: 15
背包中所装物品为:
第 2 个,第 4 个,第 5 个,第 6 个,
'''
最小路径和(详细可看 Minimum Path Sum)
class Solution:
def minPathSum(self, grid):
"""
:type grid: List[List[int]]
:rtype: int
"""
n = len(grid)
m = len(grid[0])
for i in range(1,n):
grid[i][0] = grid[i-1][0] + grid[i][0] #首先需要寻找左边界各点的路径总和
for j in range(1,m):
grid[0][j] = grid[0][j-1] + grid[0][j] #寻找上边界各点的路径总和
for i in range(1,n):
for j in range(1,m):
grid[i][j] = min(grid[i-1][j] , grid[i][j-1]) + grid[i][j] #以边界处为依据一步步推出内部个点的路径总和
return grid[n-1][m-1]
编程实现莱文斯坦最短编辑距离
import numpy
def wer2(r, h):
'''
This function was originally written by Martin Thoma
https://martin-thoma.com/word-error-rate-calculation/
Calculation of WER with Levenshtein distance.
Works only for iterables up to 254 elements (uint8).
O(nm) time ans space complexity.
Parameters
----------
r : list
h : list
Returns
-------
int
Examples
--------
>>> wer("who is there".split(), "is there".split())
1
>>> wer("who is there".split(), "".split())
3
>>> wer("".split(), "who is there".split())
3
'''
# Initialization
#生成一个全是0的二维数组
d = numpy.zeros((len(r)+1)*(len(h)+1), dtype=numpy.uint8)
print(d)
#此时的d形如:[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
#将二维数组变成n*n的格式
d = d.reshape((len(r)+1, len(h)+1))
print(d)
#此时的的形如:
"""
[[0 0 0 0]
[0 0 0 0]
[0 0 0 0]
[0 0 0 0]]
"""
#为数组两侧赋值
for i in range(len(r)+1):
for j in range(len(h)+1):
if i == 0:
d[0][j] = j
elif j == 0:
d[i][0] = i
print(d)
#此时的d形如:
"""
[[0 1 2 3]
[1 0 0 0]
[2 0 0 0]
[3 0 0 0]
]
"""
# Computation
#计算编辑距离
for i in range(1, len(r)+1):
for j in range(1, len(h)+1):
if r[i-1] == h[j-1]:
d[i][j] = d[i-1][j-1]
else:
#替换
substitution = d[i-1][j-1] + 1
#插入
insertion = d[i][j-1] + 1
#删除
deletion = d[i-1][j] + 1
print(substitution,insertion,deletion)
d[i][j] = min(substitution, insertion, deletion)
print(d)
#最终的d形如:
"""
[[0 1 2 3]
[1 1 2 3]
[2 2 1 2]
[3 3 2 2]]
"""
return d[len(r)][len(h)]
if __name__ == '__main__':
res=wer2(['c','b','c'],['a','b','d'])
print(res)
编程实现查找两个字符串的最长公共子序列
#include<iostream>
#include<string>
#include<algorithm>
using namespace std;
int arr[1001][1001];
string ReverseSentence(string str) {
string res = "", tmp = "";
for (int i = 0; i < str.size(); ++i) {
if (str[i] == ' ') res = " " + tmp + res, tmp = "";
else tmp += str[i];
}
if (tmp.size()) res = tmp + res;
return res;
}
int main() {
string str1, str2;
while (getline(cin, str1)) {
str2 = ReverseSentence(str1);
int al = str1.size();
int bl = str2.size();
for (int i = 1; i <= al; i++)
{
for (int j = 1; j <= bl; j++)
{
if (str1[i - 1] == str2[j - 1])
arr[i][j] = arr[i - 1][j - 1] + 1;
else
arr[i][j] = max(arr[i - 1][j], arr[i][j - 1]);
}
}
cout << arr[al][bl];
}
return 0;
}
编程实现一个数据序列的最长递增子序列
import numpy as np
from numpy import *
class LIS(object):
'''
动态规划实现n^2和nlogn算法实现获得【最长公共子序列】(LIS)
list:作为计算LIS的数组,可由用户输入,也可由程序生成
'''
def __init__(self, list=[]):
self.list = list
#函数说明:用复杂度为n2的DP算法计算LIS长度。用了2层嵌套循环
# 外层循环用来逐个扫描输入,假设当前扫描到的元素是X
# 内层循环用来找出在X的左边(也就是已经扫描过的),且值比X小的元素E,使X能拼接到以E结尾的LIS的后面
# 参考博客:https://www.cnblogs.com/zhangbaochong/p/5793965.html
#参数:self.list
#返回值:LIS的长度
def lis_length_dp_n2(self):
list_len = len(self.list)
if list_len < 1:
return
pos = [-1]*list_len # pos[i]代表以self.list[i]结尾的最长上升子序列的上一个序列内数的下标
dp_len = [1]*list_len # dp_len[i]代表以self.list[i]结尾的最长上升子序列的长度!!!
max_length = 1
for i in range(1, list_len):
for j in range(0, i):
if self.list[j] < self.list[i] and dp_len[i] < dp_len[j]+1:
dp_len[i] = dp_len[j]+1
pos[i] = j
if max_length < dp_len[i]:
max_length = dp_len[i]
max_len = max(dp_len)
last_pos = dp_len.index(max_len) #返回最大值对应的下标
lis = []
for i in range(max_len):
lis.append(self.list[last_pos])
last_pos = pos[last_pos]
lis.reverse() #反转
print("in lis_length_dp_n2 a LIS is: "+str(lis))
return max_length