76、最小生成树(Prim)

运营商的挑战:在图标出的城市间架设一条通信线路

要求:任意两个城市间都能通信,将架设成本降到最低。

问题:如何在图中选择n-1条边使得n个顶点间两两可达,并且这n-1条边的权值之和最小?

最小生成树:仅使用图中的n-1条边连接图中的n个顶点,不能使用产生回路的边,各边的权值的总和达到最小。

最小生成树算法步骤(Prim):

1、选择某一顶点v0作为起始顶点,使得T={v0},F={v1,v2,v3...vn},E={}

2、每次选择一条边,这条边是所有(u,v)中权值最小的边,且u属于T,v属于F

3、修正T,F,E:T=T+{v},F=F-{v},E=E+{(u,v)}

4、当F!=NULL时,且(u,v)存在,转2;否则,结束。

最小生成树原材料:

Array<bool> mark 标记顶点所属的集合(T or F)

Array<T> cost  记录T集合到F集合中顶点的最小权值

Array<int> adjVex 记录cost中权值的对应顶点

Queue<Edge>ret 记录最小生成树的边

如果T集合到F集合中同一顶点的连接有多条,那么选取权值最小的连接。

注意事项:最小生成树仅针对无向图有意义,必须判断图对象是否能够看做无向图。

问题?什么样的有向图能够看做无向图。

图类型(Graph)中新增成员函数

virtual bool isAdjacent(int i, int j)=0;

判断在当前图中顶点i到顶点j是否邻接。

bool asUndirected();

判断当前的有向图是否能够看做无向图。

bool asUndirected()//判断是否可以看做无向图
{
bool ret=true;
for(int i=0;i<vCount();i++)
{
for(int j=0;j<vCount();j++)
{
if(isAdjacent(i,j))
{
ret=ret&&isAdjacent(j,i)&&(getEdge(i,j)==getEdge(j,i));
}
}
}
return ret;

}

bool isAdjacent(int i,int j)
{
return (0<=i)&&(i<vCount())&&(0<=j)&&(j<vCount())&&(m_edges[i][j]!=NULL);

}

bool isAdjacent(int i, int j)
{
return (0<=i)&&(i<vCount())&&(0<=j)&&(j<vCount())&&(m_list.get(i)->edge.find(Edge<E>(i,j))>=0);

}

SharedPointer<Array<Edge<E> >>prim(const E& LIMIT,const bool MINIMUM=true)//prim算法,第二个参数是最小生成树还是最大生成树
{
LinkQueue<Edge<E> > ret;
if(asUndirected())
{
DynamicArray<int> adjVex(vCount());//最小生成树的边
DynamicArray<bool> mark(vCount());//顶点所属集合
DynamicArray<E> cost(vCount()); //最小值
SharedPointer<Array<int> >aj=NULL;//某个顶点的邻接顶点数组
bool end=false;
int v=0;//从0顶点寻找最小生成树
for(int i=0;i<vCount();i++)
{
adjVex[i]=-1;
mark[i]=false;
cost[i]=LIMIT;
}
mark[v]=true;
aj=getAdjacent(v);
for(int j=0;j<aj->length();j++)
{
cost[(*aj)[j]]=getEdge(v,(*aj)[j]);
adjVex[(*aj)[j]]=v;
}
for(int i=0;(i<vCount())&&!end;i++)//n次
{
E m=LIMIT;
int k=-1;//k记录最小值的顶点
for(int j=0;j<vCount();j++)
{
if(!mark[j]&&(MINIMUM? (cost[j]<m):(cost[j]>m)))//!mark[j]表示F集合中的,不算E集合中的
{
m=cost[j];
k=j;
}
}
end=(k==-1);
if(!end)
{
ret.add(Edge<E>(adjVex[k],k,getEdge(adjVex[k],k)));
mark[k]=true;
aj=getAdjacent(k);
for(int j=0;j<aj->length();j++)
{
if(!mark[(*aj)[j]]&&(MINIMUM? (getEdge(k,(*aj)[j])<cost[(*aj)[j]]):(getEdge(k,(*aj)[j])>cost[(*aj)[j]])))//如果从t到f集合的新连接权值较小,那么记录到cost数组中
{
cost[(*aj)[j]]=getEdge(k,(*aj)[j]);
adjVex[(*aj)[j]]=k;
}
}
}
}
}
else
{
THROW_EXCEPTION(InvalidOperationException,"prim operation is for undirected graph only");
}
if(ret.length()!=(vCount()-1))
{
THROW_EXCEPTION(InvalidOperationException,"no enough edge for prim operation");
}
return toArray(ret);

}

#include <iostream>
#include "ListGraph.h"
#include "MatrixGraph.h"
using namespace std;
using namespace WSlib;
template <typename V,typename E>
Graph<V,E>& GraphEasy()
{
static MatrixGraph<4,V,E> g;
g.setEdge(0,1,1);
g.setEdge(1,0,1);
g.setEdge(0,2,3);
g.setEdge(2,0,3);
g.setEdge(1,2,1);
g.setEdge(2,1,1);
g.setEdge(1,3,4);
g.setEdge(3,1,4);
g.setEdge(2,3,1);
g.setEdge(3,2,1);
return g;
}
template <typename V,typename E>
Graph<V,E>& GraphComplex()
{
static ListGraph<V,E> g(9);
g.setEdge(0,1,10);
g.setEdge(1,0,10);
g.setEdge(0,5,11);
g.setEdge(5,0,11);
g.setEdge(1,2,18);
g.setEdge(2,1,18);
g.setEdge(1,8,12);
g.setEdge(8,1,12);
g.setEdge(1,6,16);
g.setEdge(6,1,16);
g.setEdge(2,3,22);
g.setEdge(3,2,22);
g.setEdge(2,8,8);
g.setEdge(8,2,8);
g.setEdge(3,8,21);
g.setEdge(8,3,21);
g.setEdge(3,6,24);
g.setEdge(6,3,24);
g.setEdge(3,7,16);
g.setEdge(7,3,16);
g.setEdge(3,4,20);
g.setEdge(4,3,20);
g.setEdge(4,5,26);
g.setEdge(5,4,26);
g.setEdge(4,7,7);
g.setEdge(7,4,7);
g.setEdge(5,6,17);
g.setEdge(6,5,17);
g.setEdge(6,7,19);
g.setEdge(7,6,19);
return g;
}
int main()
{
Graph<int,int>& g=GraphComplex<int,int>();
SharedPointer<Array<Edge<int> >> sa=g.prim(0,false);//最大生成树。 prim(65535):最小生成树
int w=0;
for(int i=0;i<sa->length();i++)
{
w+=(*sa)[i].data;
cout<<(*sa)[i].b<<" "<<(*sa)[i].e<<" "<<(*sa)[i].data<<endl;
}
cout<<"Weight:"<<w<<endl;
return 0;
}

小结:最小生成树使得顶点间的连通代价最小,prim算法通过顶点的动态标记寻找最小生成树,prim算法的关键是集合概念的运用(T集合,F集合),利用prim算法的思想也能寻找最大生成树。

77、最小生成树(Kruskal)

最小生成树特征:选取的边是图中权值较小的边,所有边连接后不构成回路。

需要解决的问题:如何判断新选择的边与已选择的边是否构成回路?

技巧:前驱标记数组

定义数组:Array<int>p(vCount());

数组元素的意义:

p[n]表示顶点n在边的连接通路上的另一端顶点。

核心步骤(Kruskal):

1、定义前驱标记数组:Array<int>p(vCount())。

2、获取当前图中的所有边,并存储于edges数组中。

3、对数组edges按照权值进行排序。

4、利用p数组在edges数组中选择前n-1不构成回路的边。

关键的find查找函数:

int find(Array<int>& p,int v)

{while(p[v]!=-1)

{v=p[v];}

return v;}

SharedPointer<Array<Edge<E>>> getUndirectedEdge() //获取无向图的边
{
DynamicArray<Edge<E>>* ret=NULL;
if(asUndirected())
{
LinkQueue<Edge<E>> queue;
for(int i=0;i<vCount();i++)
{
for(int j=i;j<vCount();j++)
{
if(isAdjacent(i,j))
{
queue.add(Edge<E>(i,j,getEdge(i,j)));
}
}
}
ret=toArray(queue);
}
else
{
THROW_EXCEPTION(InvalidOperationException,"this funtion is for undirected only...");
}
return ret;
}

SharedPointer<Array<Edge<E>>> kruskal(const bool MINMUM=true)
{
LinkQueue<Edge<E> >ret;
SharedPointer<Array<Edge<E>>> edges=getUndirectedEdge();
DynamicArray<int> p(vCount());
for(int i=0;i<p.length();i++)
{
p[i]=-1;
}
Sort::Shell(*edges,MINMUM);
for(int i=0;(i<edges->length()&&(ret.length()<(vCount()-1)));i++)//最多循环多少次,挑选n-1条
{
int b=find(p,(*edges)[i].b);
int e=find(p,(*edges)[i].e);
if(b!=e)
{
p[e]=b;
ret.add((*edges)[i]);
}
}
if(ret.length()!=(vCount()-1))
{
THROW_EXCEPTION(InvalidOperationException,"no enough edges for kruskal operator...");
}
return toArray(ret);

}

小结:prim算法以顶点为核心寻找最小生成树,不够直接。Kruskal算法以边为核心寻找最小生成树,直观简单。kruskal算法的关键是前驱标记数组的使用。前驱标记数组用于判断新选择的边是否会构成回路。

78、最短路径(Dijkstra)

最短路径的概念:如果从有向图中某一顶点(起始顶点)到达另一顶点(终止顶点)的路径不止一条,那么如何找到一条路径使得此路径各边上的权值总和达到最小。

问题的提法:给定一个带权有向图G与起始顶点v,求从v到G中其它顶点的最短路径(每条边上都存在有意义的权值)。

解决思路:Dijkstra提出按路径长度的递增次序,逐步产生最短路径。

首先求出长度最短的一条最短路径,在参照它求出长度次短的一条最短路径,依次类推,直到从起始顶点v到其它各顶点的最短路径全部求出为止。

准备工作:

辅助数组:Array<E>dist;

dist[i]表示当前从起始顶点v0到顶点vi的路径长度。

初始设置:

若从起始顶点v0到顶点vi有边:dist[i]为该边上的权值,若从起始顶点v0到顶点vi无边:dist[i]为无穷。

Dijkstra算法步骤:

1、初始化:s<-{v0}, dist[j]<-Edge[0][j], j=1,2,...,n-1;

2、找出最小路径值所对应的顶点k:dist[k]==min{ dist[i] },i属于V-S;S<--S并{k}; //标记k顶点进入S集合,找到从起始到k的最短路径。

3、对于每一个i属于V-S修改:dist[i]<--min{dist[i], dist[k]+Edge[k][i]}

4、判断:若S=V,则算法结束,否则转2.

Dijkstra算法精髓:s集合内的顶点是已经找到最短路径的顶点,v0到w的最短路径只能通过s集内的顶点。

dist[w]可能改变:

if(dist[u]+Edge[u,w]<dist[w])

{ dist[w]=dist[u]+Edge[u,w]; }

问题:如何记录最短路径上的各个顶点?

定义辅助数组:

Array<int>path;

path[i]表示当前路径上顶点i的前驱顶点。

初始化:path={ -1 };

修改:

if(dist[u]+Edge[u,w]<dist[w])

{dist[w]=dist[u]+Edge[u,w];

path[w]=u; } 

SharedPointer<Array<int> > dijkstra(int i, int j, const E& LIMIT)
{
LinkQueue<int> ret;
if((0<=i)&&(i<vCount())&&(0<=j)&&(j<vCount()))
{
DynamicArray<E> dist(vCount());
DynamicArray<int> path(vCount());
DynamicArray<bool> mark(vCount());
for(int k=0;k<vCount();k++)
{
mark[k]=false;
path[k]=-1;
dist[k]=isAdjacent(i,k)? (path[k]=i,getEdge(i,k)):LIMIT;
}
mark[i]=true;
for(int k=0;k<vCount();k++)
{
E m=LIMIT;
int u=-1;
for(int w=0;w<vCount();w++)
{
if(!mark[w]&&(dist[w]<m))
{
m=dist[w];
u=w;//u是未标记最小值的序号
}
}
if(u==-1)
{
break;
}
mark[u]=true;
for(int w=0;w<vCount();w++)
{
if(!mark[w]&& isAdjacent(u,w) &&(dist[u]+getEdge(u,w)<dist[w]))
{
dist[w]=dist[u]+getEdge(u,w);//初始点到中转点加中转点加终点距离小于初始点到终点距离
path[w]=u;
}
}
}
LinkStack<int> s;
s.push(j);
for(int k=path[j];k!=-1;k=path[k])
{
s.push(k);//经过的顶点放到栈里边,path中是逆序的,通过栈中转一下
}
while(s.size()>0)
{
ret.add(s.top());
s.pop();
}
}
else
{
THROW_EXCEPTION(InvalidParameterException,"index s is invalid");
}
if(ret.length()<2)
{
THROW_EXCEPTION(ArithmeticException,"no path from i to j..");
}
return toArray(ret);

}

#include <iostream>
#include "ListGraph.h"
#include "MatrixGraph.h"
using namespace std;
using namespace WSlib;
template <typename V,typename E>
Graph<V,E>& GraphEasy()
{
static MatrixGraph<4,V,E> g;
g.setEdge(0,1,1);
g.setEdge(0,2,3);
g.setEdge(1,2,1);
g.setEdge(1,3,4);
g.setEdge(2,3,1);
return g;
}
template <typename V,typename E>
Graph<V,E>& GraphComplex()
{
static ListGraph<V,E> g(5);
g.setEdge(0,1,10);
g.setEdge(0,3,30);
g.setEdge(0,4,100);
g.setEdge(1,2,50);
g.setEdge(2,4,10);
g.setEdge(3,2,20);
g.setEdge(3,4,60);
return g;
}
int main()
{
Graph<int,int>& g=GraphComplex<int,int>();
SharedPointer<Array<int> > p=g.dijkstra(0,4,65535);//最大生成树。 prim(65535):最小生成树
int w=0;
for(int i=0;i<p->length();i++)
{
cout<<(*p)[i]<<" ";
}
cout<<endl;
return 0;
}

小结:Dijkstra最短路径算法是基于递推的思想完成的,起始顶点到其它各顶点的最短路径通过动态推导得到,未标记顶点的最短路径只能由已标记顶点计算得出,算法的最终结果是起始顶点到其它各顶点的最短路径。

79、最短路径(Floyd)

问题的提法:已知一个各边权值均大于0的带权有向图,对每一对顶点vi!=vj,求出vi与vj之间的最短路径值以及最短路径上的顶点。

核心:定义一个n阶方阵序列:A^(-1),A^0,...,A^(n-1)

其中:A^(-1)[i][j]=Edge[i[j]  //A^(-1)邻接矩阵

A^k[i][j]=min{ A^(k-1)[i][j], A^(k-1)[i][k]+A^(k-1)[k][j] },k=0,1,...,n-1 //前一个方阵元素值与前一个的ik+kj值的最小值

例:A[0][2]=min{ A[0][2], A[0][1]+A[1][2] }

A^(-1)[i][j]:vi到vj的权值,无中间顶点。

A^(0)[i][j]: vi到vj的路径长度,路径的中间顶点为0。

A^(1)[i][j]:vi到vj的路径长度,路径的中间顶点可能为0或1。

...A^(k)[i][j]:vi到vj的路径长度,路径的中间顶点编号不大于k。

A^(n-1)[i][j]:vi到vj的最短路径长度。

Floy算法精髓:

A^(-1)定义为邻接矩阵,则A^(0)...A^(n-1)通过中转顶点逐一递推得到。

A^(k)矩阵中元素的更新:A^(k)[i][j]=min{ A^(k-1)[i][j], A^(k-1)[i][k]+A^(k-1)[k][j]}

A矩阵的推导就是最短路径的推导:A[i][j]为i到j的路径值,在推导过程中逐步减小。

Floy算法的实现:初始化

本质:使用邻接矩阵初始化A^(-1)

本质:使用中转顶点逐步推导最短路径。

如何记录路径?

定义辅助数组:int path[N][N]; //路径矩阵

path[i][j]表示i到j的路径上所经过的第一个顶点

初始化:path[i][j]=-1; or path[i][j]=j;

修改:

if((dist[i][k]+dist[k][j])<dist[i][j])

{ dist[i][j]=dist[i][k]+dist[k][j] ; 

path[i][j]=path[i][k] } ;//从i到j的第一个顶点与i到k的第一个顶点相同

SharedPointer<Array<int> > floyd(int x,int y,const E& LIMIT)
{
LinkQueue<int> ret;
if((0<=x)&&(x<=vCount())&&(0<=y)&&(y<=vCount()))
{
DynamicArray<DynamicArray<E> >dist(vCount());
DynamicArray<DynamicArray<int> >path(vCount());
for(int k=0;k<vCount();k++)
{
dist[k].resize(vCount());
path[k].resize(vCount());
}
for(int i=0;i<vCount();i++)
{
for(int j=0;j<vCount();j++)
{
path[i][j]=-1;
dist[i][j]=isAdjacent(i,j)? (path[i][j] = j, getEdge(i,j)) : LIMIT;
}
}
for(int k=0;k<vCount();k++)
{
for(int i=0;i<vCount();i++)
{
for(int j=0;j<vCount();j++)
{
if(dist[i][k]+dist[k][j]<dist[i][j])
{
dist[i][j]=dist[i][k]+dist[k][j];
path[i][j]=path[i][k];//i到j与i到k的第一个顶点相同
}
}
}
}
while((x!=-1)&&(x!=y))
{
ret.add(x);
x=path[x][y];//递推,如果0到2,首先path[0][2]得到第一个顶点x=1,然后path[1][2]得到第二个
}
if(x!=-1)//最后一次x=y上个循环结束了,但是终点没有进队列
{
ret.add(x);
}
if(ret.length()<2)
{
THROW_EXCEPTION(ArithmeticException,"no path from x to y..");
}
//ret=dist[x][y];
}
else
{
THROW_EXCEPTION(InvalidOperationException,"Index <x,y> is invalid");
}
return toArray(ret);

}

template<typename V,typename E>
Graph<V,E>& GraphSample()
{
static ListGraph<V,E> g(3);
g.setEdge(0,1,4);
g.setEdge(0,2,11);
g.setEdge(1,2,2);
g.setEdge(1,0,6);
g.setEdge(1,0,6);
g.setEdge(2,0,3);
return g;
}
int main()
{
Graph<int,int>& g=GraphSample<int,int>(); //没有&会报无法实例化抽象类,父类对象可以引用指向子类对象
SharedPointer<Array<int> > r=g.floyd(0,2,65535);
for(int j=0;j<r->length();j++)
{
cout<<(*r)[j]<<" ";
}
cout<<endl;
return 0;
}

小结:Floyd算法通过递推逐步求得所有顶点间的最短路径。Floyd算法的本质是通过中转顶点寻找更短的路径,邻接矩阵是最短路径推导的起始矩阵,路径矩阵记录了最短路径上的各个顶点。

80、最长不下降序列

不下降序列问题:设由n个数组成的数列,记为:a[1]、a[2]、...、a[n],

若存在i1<i2<...<im满足:a[i1]<=a[i2]<=a[i3]<=...<=a[im],则称为长度为m的不下降序列。

例如:3,18,7,14,10,12,23,41,16,24

则:3、18、23、24是一个长度为4的不下降序列。

3、7,10,12,16,24是一个长度为6的不下降序列。

如何求最长不下降序列?如何求所有不下降序列?

问题建模:使用数列中的元素和元素间的关系建立模型。

图中顶点的附加数据值为对应的数列元素值。

图中的边按照如下方式建立:当数列中的某个元素与后续元素存在不下降关系时:从该元素对应的顶点到后续元素对应的顶点存在一条有向边,边的权值固定为1。

建模示例:1,3,4,2,5(0,1,2,3,4)

求图中的最多顶点路径(路径上经过的顶点数目最多)。

解决思路:以每一个顶点作为起始顶点寻找局部最多顶点路径

v0->p0,v1->p1,...,v(n-1)->p(n-1)

寻找全局最多顶点的路径:

pm=max{  p0,p1,...,p(n-1) }

局部最多顶点路径的求解思路:

获取当前顶点v的邻接顶点 { aj0,aj1,...}

以各个邻接顶点为起点求解最多顶点路径{ paj0,paj1,...}

pv=max{paj0,paj1,...}+1.

p0=max{p1,p2}+1=max{2,3}+1=3+1=4.

原材料:

Array<int> count; count[i]表示以i起始的最多顶点路径上的顶点数。

Array<int> path; path[i]表示以i起始的最多顶点路径上经过的第一个顶点。

Array<bool> mark; 如果i起始的最多顶点路径已找到,则:mark[i]为true。

寻找局部顶点数最多的路径:

定义功能:search_max_path(v,count,path,mark)

以v作为起始顶点寻找最多顶点路径。

count记录经过的最多顶点数。

path记录最多顶点路径上经过的第一个顶点。

mark记录最多顶点路径是否已经找到。

#include <iostream>
#include "ListGraph.h"
#include "MatrixGraph.h"
using namespace std;
using namespace WSlib;
int search_max_path(Graph<int,int>& g,int v,Array<int>& count,Array<LinkList<int>* >& path,Array<bool>& mark)
{
int ret=0;
//int k=-1;
SharedPointer<Array<int> >aj=g.getAdjacent(v);
for(int i=0;i<aj->length();i++)
{
int num=0;//邻接顶点的局部最多顶点路径上的顶点数
if(!mark[(*aj)[i]])
{
num=search_max_path(g,(*aj)[i],count,path,mark);
}
else
{
num=count[(*aj)[i]];
}
if(ret<num)//如果找到多的,记录
{
ret=num; //记录顶点数
//k=(*aj)[i];//记录顶点号
}
}
for(int i=0;i<aj->length();i++) //新加
{
if(ret==count[(*aj)[i]])
{
path[v]->insert((*aj)[i]);
}
}
ret++;
count[v]=ret;
mark[v]=true;
//path[v]=k;//经过的第一个顶点
return ret;
}
SharedPointer<Graph<int,int> >create_graph(int* a, int len)
{
ListGraph<int,int>* ret=new ListGraph<int,int>(len);
for(int i=0;i<len;i++)
{
ret->setVertex(i,a[i]);//将数据元素值与顶点一一对应
}
for(int i=0;i<len;i++) //设置对应边的值
{
for(int j=i+1;j<len;j++) //相对位置在前的元素应该小于相对位置在后边的元素
{
if(a[i]<=a[j])
{
ret->setEdge(i,j,1);//在图模型中建立边,不关心权值
}
}
}
return ret;
}
void init_array(Array<int>& count,Array<LinkList<int>* >& path,Array<bool>& mark)
{
for(int i=0;i<count.length();i++)
{
count[i]=0;
}
for(int i=0;i<path.length();i++)
{
path[i]=new LinkList<int>();//由于类型改变,初始化方式也改变
}
for(int i=0;i<mark.length();i++)
{
mark[i]=false;
}
}
void print_path(Graph<int,int>& g,int v,Array<LinkList<int>* >& path,LinkList<int>& cp)//打印v作为起始顶点的最多顶点路径,cp保存路径上的顶点号
{
cp.insert(v);
if(path[v]->length()>0)//还没到一条链表的终点
{
//下边是一个一个加进入链表,最后在踢出去
for(path[v]->move(0);! path[v]->end();path[v]->next()) //循环重复的
{
print_path(g,path[v]->current(),path,cp);//将链表中顶点都作为最短路径上的顶点打印
}
}
else//到了最后一个顶点,开始具体打印,打印出一条最多顶点的路径
{
cout<<"element:";
for(cp.move(0);!cp.end();cp.next())
{
cout<<g.getVertex(cp.current())<<" ";
}
cout<<endl;
}
cp.remove(cp.length()-1); //只要打印一次就要删除最近加入的顶点。移除重复的5
}
void prient_max_path(Graph<int,int>& g,Array<int>& count,Array<LinkList<int>* >& path)
{
int max=0;
LinkList<int> cp;
for(int i=0;i<count.length();i++)
{
if(max<count[i])
{
max=count[i];
}
}
cout<<"len:"<<max<<endl;
for(int i=0;i<count.length();i++)//打印路径
{
if(max==count[i]) //发现了最大值  把顶点拿出来
{
/*cout<<"element:"<<g.getVertex(i)<<" ";
for(int j=path[i];j!=-1;j=path[j])
{
cout<<g.getVertex(j)<<" ";
}
cout<<endl;*/
print_path(g,i,path,cp);
}
}
}
void search_max_path(Graph<int,int>& g,Array<int>& count,Array<LinkList<int>* >& path,Array<bool>& mark)
{
for(int i=0;i<g.vCount();i++)
{
if(!mark[i])
{
search_max_path(g,i,count,path,mark);//从0到n运行search_path
}
}
}
void solution(int* a,int len)
{
DynamicArray<int> count(len);
//DynamicArray<int> path(len);
DynamicArray<LinkList<int>* >path(len);//能够找多条
DynamicArray<bool> mark(len);
SharedPointer<Graph<int,int> > g;
g=create_graph(a,len);
init_array(count,path,mark);
search_max_path(*g,count,path,mark); //传递的是图对象本身,而不是指向图对象的指针指针
prient_max_path(*g,count,path);
}
int main()
{
int a[]={2,1,3,5,4};  //只会打印134,135没有找到,改进
solution(a,sizeof(a)/sizeof(*a));
return 0;
}

数据结构学习重点:

编程能力的训练(语言强化训练,逻辑思维训练)。

数据组织方式的训练。

简单算法设计的训练。

经典算法的学习。

外传:二分查找,字典类型的创建,二叉排序树,平衡二叉树,哈希类型的创建。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值