算法设计与分析知识点总结
算法
###算法是一组有穷的规则,它规定了解决某一特定类型问题的一系列运算。
##算法的五个重要特性:确定性、可行性(有效性)、输入、输出、有穷性
###五个方面:设计、表示、证明、分析、测试
###分析算法的两个阶段:事前分析、事后测试
**算法分析— 频率计数
-
统计算法中各类运算的执行次数
- 基本运算:指那些时间囿界于常数的运算
- 复合运算:具有固定执行时间的程序块,如一条语句、一个过程或函数等,它们的一次执行时间也可视为常量、单位时间。
-
运算的执行次数是从算法的控制流程得来的
-
一般用频率计数函数表达式中的最高次项表示算法复杂性分析的最终结果 —— 限界函数,且忽略掉其系数,记为: g(n)
-
算法时间/空间复杂度的限界函数常用的有三个
-
上界函数:如果存在两个正常数c和n0,对于所有 的n≥n0,有|f(n)| ≤ c|g(n)|,则记作f(n) = Ο(g(n))。
-
下界函数:如果存在两个正常数c和n0,对于所有的n≥n0,有|f(n)| ≥ c|g(n)|,则记作f(n) = Ω(g(n))。
-
“均值”函数:如果存在正常数c1,c2和n0,对于所有的n≥n0,有 c1|g(n)| ≤|f(n)| ≤ c2|g(n)|, 则记作f(n) = Θ(g(n))
-
-
Ο(1) < Ο(logn) < Ο(n) < Ο(nlogn) < Ο(n2) < Ο(n3)Ο(2n) < Ο(n!) < Ο(nn)
-
o记号:对任意正常数c,存在常数n0>0,使对所有的n≥n0,有|f(n)| ≤ c|g(n)|,则记作:f(n) = o(g(n))。
- 例:2n = o(n2),但2n2≠ o(n2)
-
ω记号:对任意正常数c,存在常数n0>0,使对所有的n≥n0,有c|g(n)|≤|f(n)| ,则记作:f(n) = ω(g(n))。
- 例:n2/2 = ω(n),但n2/2≠ ω(n2)
###证明的方法:数学归纳法、反证法、反例法
递归分治法
递归
-
在定义一个过程或函数时出现调用本过程或本函数的成分,称之为递归
- 调用自身,称之为直接递归
- 过程或函数p调用过程或函数q,而q又调用p,称之为间接递归
- 如果一个递归过程或递归函数中递归调用语句是最后一条执行语句,则称这种递归调用为尾递归
-
在以下三种情况下,常常要用到递归的方法
-
定义是递归的:许多数学公式、数列等的定义是递归的。例,求n!和Fibonacci数列等。
- Fibonacci: int Fib(int n)
{ if (n1 || n2)
return 1;
else
return Fib(n-1)+Fib(n-2);
- Fibonacci: int Fib(int n)
-
数据结构是递归的:例如单链表就是一种递归数据结构
-
求解方法是递归的:典型的有Hanoi问题求解
-
-
递归设计-获取递归模型例
-
简单选择排序
- void SelectSort(int a[],int n,int i)
{ int j,k;
if (i==n-1) return; //满足递归出口条件
else
{ k=i; //k记录a[i…n-1]中最小元素的下标
for (j=i+1;j<n;j++) //在a[i…n-1]中找最小元素
if (a[j]<a[k])
k=j;
if (k!=i) //若最小元素不是a[i]
swap(a[i],a[k]); //a[i]和a[k]交换
SelectSort(a,n,i+1);
}
}
- void SelectSort(int a[],int n,int i)
-
冒泡排序
- void BubbleSort(int a[],int n,int i)
{ int j;
bool exchange;
if (in-1) return; //满足递归出口条件
else
{ exchange=false; //置exchange为false
for (j=n-1;j>i;j–)
if (a[j]<a[j-1]) //当相邻元素反序时
{ swap(a[j],a[j-1]);
exchange=true; //发生交换置exchange为true
}
if (exchangefalse) //未发生交换时直接返回
return;
else //发生交换时继续递归调用
BubbleSort(a,n,i+1);
}
}
- void BubbleSort(int a[],int n,int i)
-
n皇后问题
- bool place(int i,int j) //测试(i,j)位置能否摆放皇后
{ if (i==1) return true; //第一个皇后总是可以放置
int k=1;
while (k<i) //k=1~i-1是已放置了皇后的行
{ if ((q[k]==j) || (abs(q[k]-j)==abs(i-k)))
return false;
k++;
}
return true;
}
void queen(int i,int n) //放置1~i的皇后
{ if (i>n)
dispasolution(n); //所有皇后放置结束
else
{ for (int j=1;j<=n;j++) //在第i行上试探每一个列j
if (place(i,j)) //在第i行上找到一个合适位置(i,j)
{ q[i]=j;
queen(i+1,n);
}
}
}
- bool place(int i,int j) //测试(i,j)位置能否摆放皇后
-
分治法
-
化整为零、分而治之
-
抽象控制算法
- SOLUTION DandC(p,q) /* divide and conquer */
{
if(small(p,q))
return conquer(p,q);
else
{
m=divide(p,q);
return combine( DandC(p,m), DandC(m+1,q) );
}
}
- SOLUTION DandC(p,q) /* divide and conquer */
-
例:二分检索
- int BinSearch1(p, q, x)
{ int k=(p+q)/2;
if(q<p) return –1; /* 参数错误 */
if(x==a[k]) return k;
if(x<a[k]) return BinSearch1(p, k-1, x);
if(x>a[k]) return BinSearch1(k+1, q, x);
}
- int BinSearch1(p, q, x)
动态规划法
适用条件
- **最优化原理(最优子结构性质):不论过去状态和决策如何,对前面的决策所形成的状态而言,余下的诸决策必须构成最优策略。简而言之,一个最优化策略的子策略总是最优的。一个问题满足最优化原理又称其具有最优子结构性质。
- 无后效性:将各阶段按照一定的次序排列好之后,对于某个给定的阶段状态,它以前各阶段的状态无法直接影响它未来的决策,而只能通过当前的这个状态。、
- **子问题的重叠性:动态规划算法的关键在于解决冗余,这是动态规划算法的根本目的。动态规划实质上是一种以空间换时间的技术,它在实现的过程中,不得不存储产生过程中的各种状态,所以它的空间复杂度要大于其他的算法。选择动态规划算法是因为动态规划算法在空间上可以承受,而搜索算法在时间上却无法承受,所以我们舍空间而取时间。
局限性
- 它没有统一的处理方法,必须根据问题的各种性质并结合一定的技巧来处理;另外当变量的维数增大时,总的计算量及存贮量急剧增大。因而,受计算机的存贮量及计算速度的限制,当今的计算机仍不能用动态规划方法来解决较大规模的问题
基本步骤
- 划分阶段:按照问题的时间或空间特征,把问题分为若干个阶段。在划分阶段时,注意划分后的阶段一定要是有序的或者是可排序的,否则问题就无法求解。
- 确定状态和状态变量:将问题发展到各个阶段时所处于的各种客观情况用不同的状态表示出来。当然,状态的选择要满足无后效性。
- 确定决策并写出状态转移方程:因为决策和状态转移有着天然的联系,状态转移就是根据上一阶段的状态和决策来导出本阶段的状态。所以如果确定了决策,状态转移方程也就可写出。但事实上常常是反过来做,根据相邻两个阶段的状态之间的关系来确定决策方法和状态转移方程
- 寻找边界条件:给出的状态转移方程是一个递推式,需要一个递推的终止条件或边界条件。
基本要素
-
最优子结构
- 在分析问题的最优子结构性质时,所用的方法具有普遍性:首先假设由问题的最优解导出的子问题的解不是最优的,然后再设法说明在这个假设下可构造出比原问题最优解更好的解,从而导致矛盾。
-
重叠子问题
- 递归算法求解问题时,每次产生的子问题并不总是新问题,有些子问题被反复计算多次。这种性质称为子问题的重叠性质
- 动态规划算法,对每一个子问题只解一次,而后将其解保存在一个表格中,当再次需要解此子问题时,只是简单地用常数时间查看一下结果
**典型问题
-
矩阵连乘问题:给定n个矩阵{A1,A2,…,An},其中Ai与Ai+1是可乘的,i=1,2…,n-1。如何确定计算矩阵连乘积的计算次序,使得依此次序计算矩阵连乘积需要的数乘次数最少
- 计算最优值 :int MatrixChain::MChain()
{ //求A[0:n-1]的最优解值
for (int i=0;i<n; i++) m[i][i]=0;
for (int r=2; r<=n; r++)
for (int i=0; i<=n-r; i++)
{
int j=i+r-1;
m[i][j]=m[i+1][j]+p[i]*p[i+1]*p[j+1]; //m[i][j] 的初值
s[i][j]=i;
for (int k=i+1; k<j; k++)
{
int t=m[i][k]+m[k+1][j]+p[i]*p[k+1]*p[j+1];
if (t<m[i][j])
{m[i][j]=t; s[i][j]=k;}
}
}
return m[0][n-1];
}
- 计算最优值 :int MatrixChain::MChain()
-
0-1背包问题
-
剪绳子问题
- def jianshengzi(n):
if n < 2:
return 0
if n == 2:
return 1 #长度为2,只能剪成11
if n == 3:
return 2 #长度为3,剪成21 > 111
递归问题是 f(n) = max{f(i)*f(n-i)}
for i in range(4,n+1):
maxs = 0
for j in range(1,i/2+1):
mult = h[j] * h[i-j]
if maxs < mult:
maxs = mult
h[i] = maxs # 每次J的迭代轮询出该长度的最大值
print h
return h[n] - def jianshengzi(n):
贪心算法
###贪心算法总是作出在当前看来最好的选择。也就是说贪心算法并不从整体最优考虑,它所作出的选择只是在某种意义上的局部最优选择。
基本要素
- 贪心选择性质:所求问题的整体最优解可以通过一系列局部最优的选择,即贪心选择来达到。
- 最优子结构性质:当一个问题的最优解包含其子问题的最优解时,称此问题具有最优子结构性质。问题的最优子结构性质是该问题可用动态规划算法或贪心算法求解的关键特征。
问题
-
活动安排问题
- public static int greedySelector(int [] s, int [] f, boolean a[])
{
int n=s.length-1;
a[1]=true;
int j=1;
int count=1;
for (int i=2;i<=n;i++) {
if (s[i]>=f[j]) {
a[i]=true;
j=i;
count++;
}
else a[i]=false;
}
return count;
}
各活动的起始时间和结束时间存储于数组s和f中且按结束时间的非减序排列
- public static int greedySelector(int [] s, int [] f, boolean a[])
-
背包问题(单位重量价值最高)
- public static float knapsack(float c,float [] w, float [] v,float [] x)
{
int n=v.length;
Element [] d = new Element [n];
for (int i = 0; i < n; i++) d[i] = new Element(w[i],v[i],i);
MergeSort.mergeSort(d);
int i;
float opt=0;
for (i=0;i<n;i++) x[i]=0;
for (i=0;i<n;i++) {
if (d[i].w>c) break;
x[d[i].i]=1;
opt+=d[i].v;
c-=d[i].w;
}
if (i<n){
x[d[i].i]=c/d[i].w;
opt+=x[d[i].i]*d[i].v;
}
return opt;
}
- public static float knapsack(float c,float [] w, float [] v,float [] x)
-
最优装载
- public static float loading(float c, float [] w, int [] x)
{
int n=w.length;
Element [] d = new Element [n];
for (int i = 0; i < n; i++)
d[i] = new Element(w[i],i);
MergeSort.mergeSort(d);
float opt=0;
for (int i = 0; i < n; i++) x[i] = 0;
for (int i = 0; i < n && d[i].w <= c; i++) {
x[d[i].i] = 1;
opt+=d[i].w;
c -= d[i].w;
}
return opt;
}//载重量为c,集装箱i的重量为Wi
- public static float loading(float c, float [] w, int [] x)
-
哈夫曼编码:用最小堆实现优先队列Q
-
哈夫曼算法的正确性
- 贪心选择性质
- 最优子结构性质
-
-
多机调度问题
- 要求给出一种作业调度方案,使所给的n个作业在尽可能短的时间内由m台机器加工处理完成
拟阵
-
(1) S 是一个有限集
(2) L是一个有限非空集,其元素是 S 的部分子集
(3) 具有遗传性 ,即对于任意 A⊆B,若 B∈L,那么一定有 A∈L
(4) 具有交换性 ,即对任意 A∈L,B∈L且 |A|<|B| ( |A| 表示 A 中的元素个数,以下同义) , 一定存在元素 x∈B且 x∉A,使得 A ∪ { x } ∈L -
拟阵解决贪心问题:即求出权值最大独立集,对于任何能够转换为拟M=(S,L)的问题,都可以通过如下算法解决
- Set Solve(M,w)//给出拟阵M与权值函数w (Set这里意为返回一个集合)
{
清空A;
将S按w(x)的大小降序排好;
for(对每一个x∈S,按w(x)降序)
{
if(A∪{x}∈L)
A=A∪{x};
}
return A;//即权值最大独立集
}
- Set Solve(M,w)//给出拟阵M与权值函数w (Set这里意为返回一个集合)
-
最小生成树问题。拟阵的角度分析最小生成树问题。
对于无向图 G= (V , E) 【注 : V 是点集,E 是边集】
我们定义这样一个M=(S,L):
(1) S=E
(2) L={ x | x⊆E 且图 G’=(V,x) 中无环 }
这个 M 显然满足子集系统的前两个条件。这里证明其具有遗传性与交换性:
遗传性:对任意A∈L, B⊆A , 显然B⊆E。假设 B 中有环,那么 A 中也一定有环,与 A∈L 矛盾,故 B 中无环,因此B∈L , M具有遗传性。
交换性:对任意A∈L,B∈L,设|A|<|B|,显然 GA =(V,A) 中有|V|-|A| 个连通分量 , GB=(V,B) 中有|V|-|B|个连通分量。
由|A|<|B| 有 |V|-|A|>|V|-|B| , 可知此时 B 中一定存在一条边 x 连接了 G_A 中的两个不同的连通分量。显然,G’=(V,A∪{x})中无环且A∪{x}∈L ,因此M具有交换性。
综上,M是一个拟阵。
回溯法
###回溯法在问题的解空间树中,按深度优先策略,从根结点出发搜索解空间树。算法搜索至解空间树的任意一点时,先判断该结点是否包含问题的解。如果肯定不包含,则跳过对该结点为根的子树的搜索,逐层向其祖先结点回溯;否则,进入该子树,继续按深度优先策略搜索。
- 具有限界函数的深度优先生成法称为回溯法。
基本思想
-
基本步骤
- 针对所给问题,定义问题的解空间;
- 确定易于搜索的解空间结构;
- 以深度优先方式搜索解空间,并在搜索过程中用剪枝函数避免无效搜索。
-
常用剪枝函数
- 用约束函数在扩展结点处剪去不满足约束的子树
- 用限界函数剪去得不到最优解的子树
典型问题
-
0-1背包问题
- bestV = 0
curW = 0
curV = 0
bestx = None
def backtrack(i):
global bestV, curW, curV, x, bestx
if i >= n:
if bestV < curV:
bestV = curV
bestx = x[:]
else:
if curW + w[i] <= c:
x[i] = True
curW += w[i]
curV += v[i]
backtrack(i + 1)
curW -= w[i]
curV -= v[i]
x[i] = False
backtrack(i + 1) - if name == ‘main’:
n = 3
c = 30
w = [16,15,15]
v = [45,25,25]
x = [False for i in range(n)]
backtrack(0)
print(bestV)
print(bestx)
- bestV = 0
-
n后问题
- def conflict(state, nextColumn):
nextRow = rows = len(state)
for row in range(rows):
column = state[row]
if abs(column - nextColumn) in (0, nextRow - row):
return True
return False
- def conflict(state, nextColumn):
def queens(num, state=()):
for pos in range(num):
if not conflict(state, pos):
if len(state) == num - 1:
yield (pos,)
else:
for result in queens(num, state + (pos,)):
yield (pos,) + result
def prettyprint(solution):
def line(pos, length=len(solution)):
return ’ . ’ * (pos) + ’ x ’ + ’ . ’ * (length - pos - 1)
for pos in solution:
print(line(pos))
- if name == ‘main’:
solutions = queens(8)
for index, solution in enumerate(solutions):
print(“第%d种解决方案:” % (index + 1), solution)
prettyprint(solution)
print(’*’ * 100)
-
推销员问题
- void iterativeBacktrack (){
int t=1;
while(t>0){
if (f(n,t)<=g(n,t))
for(int i=f(n,t);i<=g(n,t);i++){
x[t]=h(i);
if(constraint(t) && bound(t)){
if(solution(t)) output(x);//输出最优解
else t++;//搜索下一层节点
}
}
else t–;//回溯到上一节点
}
}
- void iterativeBacktrack (){
-
图的m着色问题
- GraphColor(int n,int m,int color[],bool c[][5])
{
int i,k;
for (i=0; i<n; i++ ) //将解向量color[n]初始化为0
color[i]=0;
k=0;
while (k>=0)
{ color[k]=color[k]+1; //使当前颜色数加1
while ((color[k]<=m) && (!ok(color,k,c,n))) //当前颜色是否有效
color[k]=color[k]+1; //无效,搜索下一个颜色
if (color[k]<=m) //求解完毕,输出解
{
if (k==n-1)break; //是最后的顶点,完成搜索
else k=k+1; //否,处理下一个顶点
}
else //搜索失败,回溯到前一个顶点
{
color[k]=0;
k=k-1;
}
}
}
- GraphColor(int n,int m,int color[],bool c[][5])
-
批处理作业调度
- private static void backtrack(int i) {
if (i > n) {
for (int j = 1; j <= n; j++) bestx[j] = x[j];
bestf = f;
} else
for (int j = i; j <= n; j++) {
f1+=m[x[j]][1];
f2[i]=((f2[i-1]>f1)?f2[i-1]:f1)+m[x[j]][2];
f+=f2[i];
if (f < bestf) {
MyMath.swap(x,i,j);
backtrack(i+1);
MyMath.swap(x,i,j);
}
f1-=m[x[j]][1];
f-=f2[i];
}
} - public class FlowShop
static int n, // 作业数
f1, // 机器1完成处理时间
f, // 完成时间和
bestf; // 当前最优值
static int [][] m; // 各作业所需的处理时间
static int [] x; // 当前作业调度
static int [] bestx; // 当前最优作业调度
static int [] f2; // 机器2完成处理时间
- private static void backtrack(int i) {
分支限界法
###分支限界法则以广度优先或以最小耗费优先的方式搜索解空间树,将活结点存放在一个特殊的表中。其策略是:在扩展结点处,先生成其所有的儿子结点,将那些导致不可行解或导致非最优解的儿子舍弃,其余儿子加入活结点表中。此后,从活结点表中按照一定的规则取出一个结点作为当前扩展结点。并重复上述结点扩展过程。
- 分支限界法常以广度优先或以最小耗费(最大效益)优先的方式搜索问题的解空间树。
限界函数的构造
- 一个限界函数f(d)通常可以由两个部分构成:⑴从开始结点到结点d的已有耗损值g(d);⑵再从结点d到达目标的期望耗损值 h(d):f(d) = g(d) + h(d)
- 计算位置:搜索树的结点
值:极大化问题是以该点为根的子树所有可行解的值的上界 ( 极小化问题为下界)
性质:对极大化问题父结点代价不小于子结点的代价 (极小化问题相反)
分支限界法求解最大化问题的一般过程
- 1.根据限界函数确定目标函数的界[down, up];
2.将待处理结点表PT初始化为空;
3.对根结点的每个孩子结点x执行下列操作
3.1 估算结点x的目标函数值value;
3.2 若(value>=down),则将结点x加入表PT中;
4.循环直到某个叶子结点的目标函数值在表PT中最大
4.1 i=表PT中值最大的结点;
4.2 对结点i的每个孩子结点x执行下列操作
4.2.1 估算结点x的目标函数值value;
4.2.2 若(value>=down),则将结点x加入表PT中;
4.2.3 若(结点x是叶子结点且结点x的value值在表PT中最大),
则将结点x对应的解输出,算法结束;
4.2.4 若(结点x是叶子结点但结点x的value值在表PT中不是最大),
则令down=value,并且将表PT中所有小于value的结点删除;
典型例题
-
0-1背包问题
-
限界函数:𝑢𝑏=𝑣+(𝑊−𝑤)×(𝑣_(𝑖+1)∕𝑤_(𝑖+1) )
-
#coding : utf-8
import numpy as np
import queue
import math
w = [4,7,5,3]#weight
v = [40,42,25,12]#value
def bag(capacity):
vec_len = 2**(len(v)+1) - 1#tree `s size
vec_v = np.zeros(vec_len)
vec_w = np.zeros(vec_len)
vec_w[0]=capacity
que = queue.Queue();
que.put(0)
best = 0 -
while(not que.empty()):
current = que.get()#获取队列
level = int(math.log(current+1,2))
if(vec_v[current] > vec_v[best]):
best = currentleft = 2current+1#left child index
right = 2current+2#right child indexif(left < vec_len and vec_w[current]-w[level] > 0 and vec_v[current]+v[level] > vec_v[best] ):
vec_v[left] = int(vec_v[current]+v[level])
vec_w[left] = vec_w[current]-w[level]
que.put(left)写入队列
if(right < vec_len and sum(v[level+1:-1])+vec_v[current] > vec_v[best]):
vec_v[right] = vec_v[current]
vec_w[right] = vec_w[current]
que.put(right)
print(vec_w,vec_v[best])
-
if name == ‘main’:
bag(10)
-
旅行售货员问题
- import math
n = 4
x = [0, 1, 2, 3]
定义图的字典形式
G = {
‘1’: {‘2’: 30, ‘3’: 6, ‘4’: 4},
‘2’: {‘1’: 30, ‘3’: 5, ‘4’: 10},
‘3’: {‘1’: 6, ‘2’: 5, ‘4’: 20},
‘4’: {‘1’: 4, ‘2’: 10, ‘3’: 20}
}
定义图的数组形式
graph = [
[0, 30, 6, 4],
[30, 0, 5, 10],
[6, 5, 0, 20],
[4, 10, 20, 0]
]
- bestcost = math.inf # 好吧 干脆就无穷好了
nowcost = 0 # 全局变量,现在的花费
def TSP(graph, n, s):
global cc, bestc
if (s == n):
if (graph[x[n - 1]][x[0]] != 0 and (cc + graph[x[n - 1]][x[0]] < bestcost)):
print(‘best way:’, x)
bestcost = cc + graph[x[n - 1]][x[0]]
print(‘bestcost’, bestcost)
else:
for i in range(s, n):
# 如果下一节点不是自身 而且 求得的值小于目前的最佳值
if (graph[x[i - 1]][x[i]] != 0 and cc+ graph[x[i - 1]][x[i]] < bestcost):
x[i], x[s] = x[s], x[i] # 交换一下
cc += graph[x[s - 1]][x[s]] # 将花费加入
TSP(graph, n, s + 1)
cc -= graph[x[s - 1]][x[s]] # 回溯上去还需要减去
x[i], x[s] = x[s], x[i] # 别忘记交换回来
TSP(graph, n, 1)
概率算法
算法分类
-
数值概率算法
-
蒙特卡罗(Monte Carlo)算法
- 用随机投点法计算pi值
-
拉斯维加斯(Las Vegas)算法
-
n 皇后问题
- boolean queensLV(int x[]) {
int k=1; // 摆放皇后棋子的编号
int count=1;
while ((k<=n)&&(count>0)) {
count=0; //有多少个可选的位置
int j=0;
for (int i=1; i<=n; i++) {
x[k]=i;
if (place(k))
if (random(++count)==0) j=i;
}
if (count>0) x[k++]=j;//第K个皇后有count个位置可选,从中任选1个。
}
return (count>0);
}
void nQueen() {
x=new int[n+1];
for (int i=0;i<=n;i++)
x[i]=0;
while (!queensLV(x));
}
- boolean queensLV(int x[]) {
-
-
舍伍德(Sherwood)算法
###基本特征是对所求解问题的同一实例用同一概率算法求解两次可能得到完全不同的效果(所需时间或计算结果)
###随机数在概率算法设计中扮演着十分重要的角色:伪随机数(线性同余法)
NP完全理论
###P类问题:多项式时间内可解的问题
###NP类问题:多项式时间内可验证问题(指验证其解的正确性)
###归约:一个问题A可以约化为问题B的含义即是,可以用问题B的解法解决问题A
近似算法
###在多项式时间复杂度内,求解到最优解一定距离的次优解。有确切最优解但是并不能保证得到最优解的算法都可以称之为近似算法
典型问题
-
顶点覆盖问题
- VertexSet approxVertexCover ( Graph g )
{ cset=;
e1=g.e;
while (e1 != ) {
从e1中任取一条边(u,v);
cset=cset∪{u,v};
从e1中删去与u和v相关联的所有边;
}
return c
}
Cset用来存储顶点覆盖中的各顶点。初始为空,不断从边集e1中选取一边(u,v),将边的端点加入cset中,并将e1中已被u和v覆盖的边删去,直至cset已覆盖所有边。即e1为空。
- VertexSet approxVertexCover ( Graph g )
-
旅行售货员问题
- void approxTSP (Graph g)
{
(1)选择g的任一顶点r;
(2)用Prim算法找出带权图g的一棵以r为根的最小生成树T;
(3)前序遍历树T得到的顶点表L;
(4)将r加到表L的末尾,按表L中顶点次序组成回路H,作为计 算结果返回;
}
- void approxTSP (Graph g)
XMind - Trial Version