Sn的阶乘
T(n)=T(n-1)+O(1)=T(n-2)+2O(1)=…=T(1)+(n-1)O(1)=O(n)
全排列问题
T(n)=nT(n-1)=O(n!)
斐波那契数列
T(n)=O(2^n)
汉诺塔问题
T(n)=O(2^n)
选择、冒泡排序
T(n)=O(n^2)
分治法的基本思想:
将一个难以直接解决的大问题,分解为多个规模较小的相同子问题,各子问题相互独立,递归地解决各子问题,将子问题的解归并成原问题的解
二分查找:binarySearch
给定已排好序的n个元素s1…sn,在这n个元素中找到特定元素x
时间复杂度:
最好情况:O(1)
最坏情况:O(logn)
平均:O(logn)
合并排序:mergeSort
对n个元素进行排序
时间复杂度:
T(n)=O(1),n=1
T(n)=2T(n/2)+O(1)=O(n),n>1
快速排序:QuickSort
对n个元素进行排序
分:选定一个元素作为基准元素,小于基准元素的放左边,大于基准元素的放右边
治:递归求解子问题
取第一个元素为基准元素
通过一趟扫描将待排序的元素分割成三个独立的序列:第一个序列中所有元素均不大于基准元素,第二个序列中的元素是基准元素,第三个序列中的元素均大于基准元素。由于第二个元素已经处于正确位置,因此需要再按此方法将第一个序列和第三个序列分别进行排序,整个过程可以递归进行,最终可使整个序列变成有序序列。
时间复杂度:
最坏情况:O(n^2)
最好情况:O(nlogn)
平均:O(nlogn)
二分:
int BinarySearch(int s[n],int x,int low,int high)
{
if (low>high) return -1;
int middle=(low+high)/2;
if(x==s[middle]) return middle;
else if(x>s[middle])
return BinarySearch (s, x, middle+1,
high);
else
return BinarySearch (s, x, low, middle-
1);
}
合并
void Merge(int A[],int low,int middle,int high)
{
int i,j,k;
int *B=new int[high-low+1];
i=low; j=middle+1; k=0;
while(i<=middle&&j<=high)
if(A[i]<=A[j])
B[k++]=A[i++];
else
B[k++]=A[j++];
while (i<=middle)
B[k++]=A[i++];
while (j<=high)
B[k++]=A[j++];
for(i=low, k=0; i<=high; i++)
A[i++]=B[k++]; }
快速排序
void QuickSort(int r[ ],int low,int high)
{
int pivotpos;
if(low<high)
{
pivotpos=Partition(r,low,high);
QuickSort(r,low,pivotpos-1);
QuickSort(r,pivotpos+1,high);
动态规划:
动态规划的解题步骤:
最优子结构性质分析 建立最优值的递归关系式 自底向上求最优值 构造最优解
思想:
将待求解问题分为若干个相互联系的子问题
在求解过程中将以求解的子问题的最优值进行保存,在需要时找出
用一个表来记录所有已解子问题的最优值,不管子问题是否被用到,都将其计算结果填入该表,需要时取出
根据最优值构造最优解
矩阵连乘问题:
stpe1:确定合适的数据结构。采用二维数组m来存放各子问题的最优值,二维数组来s来存放各个子问题的最优决策
step2:初始化。令m[i][i]=0,s[i][i]=0,其中i=1,2…n
step3:循环阶段:按照地推关系式计算两个矩阵AiAi+1相乘时的最优值并将其存入m[i][i+1],同时记录最优决策计入s[i][i+1],i=1,2,3…n。以此类推直到计算n个矩阵,将最优值和最优解记录到m[1][n],s[1][n]
step4:根据二维数组s记录的最优决策信息来构造最优解
时间复杂度:
T(n)=O(1),n=1
T(n)=T(n-1)+O(1)=O(n),n>1
递推关系式:
m[i][j]=0 ,i=j
m[i][j]=min[i][k]+m|k +1][j]+Pi-1PkPj },i< j
0-1背包问题:
约束条件:
(w1x1+w2x2+w3x3+…wnxn)≤W
xi=0或xi=1 ,1<=i<=n
目标函数:
max(v1x1+v2x2+…+vnxn)
递推关系式:
C[0][j]=C[i][0]=0
C[i][j]:
C[i-1][j] j<wi
max(C[i-1][j],C[i-1][j-wi]+vi) j≥wi
解的结构:
(x1,x2…xn),xi表示第i个物品的状态,xi=0表示物品不放入背包,xi=1表示物品放入背包
数据结构:
数组v[n]存放每个物品的价值
数组w[n]存放每个物品的重量
数组c[n+1][W+1]存放每一次迭代的结果
数组x[n]存放物品的状态
算法步骤:
输入:背包容量W,n个物品的重量和价值
输出:装入背包最优值和最优解
n<-物品个数
for i <-1 to n-1 do
for j<-1 to W do
if w[i]<=j then
c[i][j]<-max(c[i-1][j-w[i]]+v[i],c[i-1][j])
else
c[i][j]<-c[i-1][j]
j<-W
for i<-n until 0 do
if c[i][j]>c[i-1][j] then
x[i-1]<-1
j<-j-w[i-1]
return c[n][W],x
贪心算法:
思想:
从问题的某一个初始解出发,在每个阶段都根据贪心策略来做出当前最优的决策,逐步逼近给定的目标,尽可能快地求得更好的解,当进行到算法某一步不能继续前进时,算法结束
会议安排问题:
贪心策略:
优先安排结束时间早且不与已安排会议重叠的会议
根据结束时间从小到大排序
算法步骤:
输入:n个会议的开始和结束时间s[1,2,…n],f[1,2…n]
输出:会议状态x[1,2…n]
n<-会议个数
j<-1
for i<-2 to n do
if si>=fj then
x[i]<-1
j<-i
else
x[i]<-0
return x
多机作业调度问题
输入:m台机,n个作业,每个作业的处理时间t
输出:每台机器处理的任务序列,和每台机器的处理时间
num[1…m]=0;f[1…m]=0 //初始化
// 对t[1…n]按降序地址排序,排序结果返回到数组a[1…n]中
// 使得t[a[1]]>=t[a[2]]>=…>=t[a[n]]>.
a=sort(t,n)
for i = 1 to n:
j=min(f, m) //求f[1…m]的最小值对应的下标
num[j]=num[j]+1 //在第j台机器上安排作业a[i]
M[j,num[j]]=a[i]
f[j]=f[j]+t[a[i]]
end for
mint = max(f,m) //求f[1…m]的最大值
return mint,num,M
文字描述:
Step1:将输入的任务处理时间有大到小排序
Step2:首先将排列好的任务,安排前m(机器个数)个任务依次给所有机器。
Step3:比较机器当前处理时间,查找最先空闲的机器,将任务序列按顺序依次分配,并记录每台机器工作总时间。
Step:输出每个机器的任务分配及处理时间。
注:当任务总数小于等于机器数时,应直接考虑第二步。
O(n^2)
哈夫曼编码:
核心思想:让权值大的叶子离根近
输入:字符集C及每个字符出现的频率f(i),i∈C
输出:Q
n←|C|
Q←sort©//频率有小到大排序
for i←1 to n-1do
构造节点z
z.left←Q中频率第一小字符x
z.right←Q中频率第二小字符y
f(z)←f(x)+f(y)
insert(Q,z)
return Q
描述:
把字符看作孤立的根节点组树的集合,依次从集合中选择两个频率最小的字符,组成左右子树构造一个新树,新树的频率是左右子树频率之和,然后把新树插入到树的集合中,重复以上过程n-1次后构造出哈夫曼树,把左子树赋值为0,右子树赋值为1,从根到叶子的路径就是对应叶子字符的编码
单源最短路径:
数据结构:
顶点状态:s[i]=0或1 是否已找到源点到i点的最短路径
路径长度记录:dist数组
路径记录:前驱数组pre[]
算法:Dijkstra
输入:有向带权图G=(V,E,W),V={1,2,…,n},源点s=1。
输出:从s到每个顶点的最短路径pre
S[1]←1
dist[1]←0
pre[1]←0
for i←2 to n do
dist[i]=w(s,i)//s到i没有边,则w(s,i)=∞
While V-S≠Ф do
从V-S中取dist最短的路径顶点j
S[j]←1
for i←2 to n do
if s[i]=0 and dist[j]+w(j,i)<dist[i] then
dist[i]←dist[j]+w(j,i)
pre[i]←j
return pre
最小生成树:
算法:Prim(G)
输入:无向带权图G,
输出:最小生成树T
U←{1},T←{ }
for i←1 to n do
if G[1][i]<∞
closest[i]←1
lowcost←G[1][i]
while V-U≠Ф do
从V-U中选择最小的lowcost[j]
U←UU{j}
T←T U{(closest[j],j)}
for i←1 to n do
if S[i]==0 and G[j][i]<lowcost[i]
closest[i]←j
lowcost[i]←G[j][i]
return T
T(n)=O(n^2)
算法:Kruskal
sort(E) //E为边集
T←Ф
j←0//记录加入的边的条数
for i←1 to |E| do//|E|是图中边的条数
e←E[i]
if e的两个端点不在同一个连通分支
then
T←{e}
j←j+1
if j=n-1//n为图顶点个数
break;
return T
时间复杂度:T(n)=O(n(n-1))
是否形成回路:
Kruskal算法采用集合的性质来进行判定:
如果选择加入的边起点和终点都在T的集合里,那么就可以断定一定会形成回路
回溯法
搜索思想:
从根开始,以深度优先搜索的方式进行搜索
根节点是活节点并且是当前的扩展结点
在搜索的过程中,当前的扩展结点向纵深方向移动一个新节点,判断该新节点是否满足隐约束。
如果满足,则新节点成为活结点,并且成为当前的扩展结点,继续深一层的搜索
如果不满足,则换该新节点的兄弟节点继续搜索。如果新节点没有兄弟节点,或兄弟节点已全部搜索完毕,则扩展结点成为死节点,搜索回溯到其父节点处继续进行
搜索过程知道找到问题的解或根节点变成死节点为止
最长公共子序列
c[i][j]表示序列Xi和Yi的最长公共子序列的长度。
c[i][j]:0 i=0或j=0
c[i][j]: c[i-1][j-1]+1 i,j>0且xi=yi
c[i][j]:max(c[i][j-1],c[i-1],[j]) i,j>0; xi!=yi
算法步骤:
step1:确定合适的数据结构。采用二维数组c来存放各个子问题的最优值,二维数组b来存放各个子问题最优值的来源,b[i][j]=1表示c[i][j]由c[i-1][j-1]得到,b[i][j]=2表示c[i][j]由c[i][j-1]得到,b[i][j]=3表示c[i][j]由c[i-1][j]得到。数组x[1:m]和y[1:n]分别存放X序列和Y序列
step2:初始化。令c[i][0]=0,c[0][j]=0,其中0≤i≤m,0≤j≤n
step3:循环阶段。根据递推关系式依次求出最优值c[i][j],同时记录b[i][j],直到c[m][n]便是序列X和Y的最长公共子序列
step4:根据二维数组b记录的相关信息以自底向上的方式来构造最优解
图的m可着色问题
解的形式:(x1,x2…xn),表示第i个节点着xi号颜色
解空间组织结构:满m叉树,树的深度为n
约束条件:和已确定颜色且有边相连的顶点颜色不同
限界条件:不需要
约束函数:
def Ok(k):
for j in range(1,k):
if ((a[k][j]==1) and (x[j]==x[k])):
return False
return True
时间复杂度:T(n)=O(nm^n) +O(nmn)=O(nmn)`
旅行售货员问题
解的形式:(x1,x2…xn),xi=1表示城市i在最短路线中,xi=0表示城市i不在最短路线中,其中起始点x1=1
解空间组织结构:深度为n排列树
约束条件:
用a[][]来存储无向带权图的邻接矩阵,如果a[i][j]不等于无穷大则表示城市i和城市j有边相连,能走通
限界条件:
cl<bestl
cl:当前已走过城市的路径长度
bestl:当前已找到最短路径的路径长度
时间复杂度:T(n)=O(n!)+O(n!)=O(n!)
n皇后问题
解的形式为(x1, x2, … , xn),xi表示第i个皇后位于第i行、第xi列
(i=1,2,3,…n)
初始状态为:(0,0,…,0)
解空间的组织结构可以是排列树,也可以是满n叉树。
只需要设置约束条件,不需要限界条件。
约束条件:不同列且不同斜线
解向量中的任意两个分量xi,xj满足:xi≠xj且|i-j|≠|xi-xj|
输入 N // 输入皇后的个数
q[1…N] //存储每行的皇后的具体位置(列标)
n_queens(k , n): // 确定第 k 行皇后的位置
if k > n: // 递归的出口
Print q // 输出各个皇后的位置
else:
for j <- 1 to n: // 从第 k 行第 1 列开始,判断各个位置是否可行
if isSafe(k , j): // 如果可行,继续判断下一行
q[k] <- j // 将第 k 行皇后放置的位置 j 记录下来
n_queens(k+1 , n) // 继续判断下一行皇后的位置
O(n*n!)
最大团
问题描述:找出包含顶点最多的团(完全图)
解的形式:(x1,x2…xn),xi表示第i个顶点是否在最大团中,xi=1表示在最大团中,xi=0表示不在最大团中
解空间结构:满二叉树深度为n或n+1
约束条件:任意两点间都有边相连
限界条件:
cn+rn>bestn
cn:当前在顶点集中的顶点个数
rn:剩余顶点个数
bestn:最优解包含的顶点个数
约束函数:
def place(t):
OK = True
for j in range(t):
if x[j] and a[t][j]==0
OK = False
break
return OK
E