热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

数据结构图知识点总结

2019独角兽企业重金招聘Python工程师标准一、基本术语图(graph):图是由顶点的有穷非空集合和顶点之间边的集合组成

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

一、基本术语

图(graph):图是由顶点的有穷非空集合和顶点之间边的集合组成,通常表示为:G(V,E),其中,G表示一个图,V是图G中的顶点的集合,E是图G中边的集合。

顶点(Vertex):图中的数据元素。线性表中我们把数据元素叫元素,树中将数据元素叫结点。

边:顶点之间的逻辑关系用边来表示,边集可以是空的。

无向边(Edge):若顶点V1到V2之间的边没有方向,则称这条边为无向边。

无向图(Undirected graphs):图中任意两个顶点之间的边都是无向边。(A,D)=(D,A)

    对于无向图G来说,G1=(V1,{E1}),其中顶点集合V1={A,B,C,D};边集和E1={(A,B),(B,C),(C,D),(D,A),(A,C)}

有向边:若从顶点V1到V2的边有方向,则称这条边为有向边,也称弧(Arc)。用表示,V1为狐尾(Tail),V2为弧头(Head)。(V1,V2)≠(V2,V1)。

有向图(Directed graphs):图中任意两个顶点之间的边都是有向边。

   注意&#xff1a;无向边用“&#xff08;&#xff09;”&#xff0c;而有向边用“<>”表示。

简单图&#xff1a;图中不存在顶点到其自身的边&#xff0c;且同一条边不重复出现。

无向完全图&#xff1a;无向图中&#xff0c;任意两个顶点之间都存在边。

有向完全图&#xff1a;有向图中&#xff0c;任意两个顶点之间都存在方向互为相反的两条弧。

稀疏图&#xff1a;有很少条边。

稠密图&#xff1a;有很多条边。

权&#xff08;Weight&#xff09;&#xff1a;与图的边或弧相关的数。

网&#xff08;Network&#xff09;&#xff1a;带权的图。

子图&#xff08;Subgraph&#xff09;&#xff1a;假设G&#61;&#xff08;V,{E}&#xff09;和G‘&#61;&#xff08;V&#39;,{E&#39;}&#xff09;&#xff0c;如果V&#39;包含于V且E&#39;包含于E&#xff0c;则称G&#39;为G的子图。

度&#xff08;Degree&#xff09;&#xff1a;无向图中&#xff0c;与顶点V相关联的边的数目。有向图中&#xff0c;入度表示指向自己的边的数目&#xff0c;出度表示指向其他边的数目&#xff0c;该顶点的度等于入度与出度的和。

路径的长度&#xff1a;一条路径上边或弧的数量。

连通图&#xff1a;图中任意两个顶点都是连通的。

     图1不是连通图&#xff0c;图2是连通图。

连通分量&#xff1a;无向图中的极大连通子图。&#xff08;子图必须是连通的且含有极大顶点数&#xff09;。图1有两个连通分量

强连通分量&#xff1a;有向图中的极大强连通子图。

生成树&#xff1a;无向图中连通且n个顶点n-1条边叫生成树。

有向树&#xff1a;有向图中一顶点入度为0其余顶点入度为1。

森林&#xff1a;一个有向图由若干棵有向树构成生成森林。

二、图的存储结构

1.邻接矩阵&#xff1a;用两个数组&#xff0c;一个数组保存顶点集&#xff0c;一个数组保存边集。


图的邻接矩阵存储的结构&#xff1a;

#define maxvex 100
typedef struct
{
char vexs[maxvex];
int arc[maxvex][maxvex];
int vertex,edges;
}MGraph;
无向图的创建代码&#xff1a;

#define maxvexs 100
#define infinity 65535//用65535来表示∞
typedef struct
{
    char vexs[maxvexs];
    int arc[maxvexs][maxvexs];
    int vertexes,edges;
}mgraph;
 
void creatgraph(mgraph *g)
{
    int i,j,k,w;
    printf("输入顶点数和边数:\n");
    scanf("%d,%d",&g->vertexes,&g->edges);
    for(i&#61;0;ivertexes;i&#43;&#43;)//读入顶点信息&#xff0c;建立顶点表
        scanf("%c",&g->vexs[i]);
    for(i&#61;0;ivertexes;i&#43;&#43;)
        for(j&#61;0;jvertexes;j&#43;&#43;)
            g->arc[i][j]&#61;infinity;//初始化邻接矩阵
    for(k&#61;0;kvertexes;k&#43;&#43;)//读入edges条边&#xff0c;建立邻接矩阵
    {
        printf("输入边(Vi,vj)上的下标i&#xff0c;下标j,和权w:\n");
        scanf("%d%d%d",&i,&j,&w);
        g->arc[i][j]&#61;w;
        g->arc[j][i]&#61;w;//无向图&#xff0c;矩阵对称
    }
}

2.邻接表&#xff1a;数组与链表相结合的存储方法。


对于带权值的网图&#xff0c;可以在边表结点定义中再增加一个weight的数据域&#xff0c;存储权值信息即可。

邻接表结点定义

typedef struct EdgeNode
{
    int adjvex; //邻接点域&#xff0c;存储该顶点对应的下标
    int weight; //用于存储权值&#xff0c;对于非网图可以不需要
    struct EdgeNode *next;  //链域&#xff0c;指向下一个邻接点
}EdgeNode;//边表结点
 
typedef struct VertexNode //顶点表结点
{
    char data; //顶点域&#xff0c;存储顶点信息
    EdgeNode *firstedge; //边表头指针
}VertexNode,AdjList[MAXVEX];
 
typedef struct
{
    AdjList adjList;
    int numVertexes,numEdges;//图中当前顶点数和边数
}GraphAdjList;
 

三、图的遍历
1.深度优先遍历&#xff08;DFS&#xff09;&#xff1a;从图中某个顶点v出发&#xff0c;访问此顶点&#xff0c;然后从v的未被访问的邻接点出发深度优先遍历图&#xff0c;直至图中所有和v有路径相通的顶点都被访问到。

2.广度优先遍历&#xff08;BFS&#xff09;&#xff1a;类似于树的层次遍历。

四、最小生成树

最小生成树&#xff1a;构造连通网的最小代价生成树。

1.普里姆&#xff08;prime&#xff09;&#xff1a;

第一种&#xff1a;先将一个起点加入最小生成树&#xff0c;之后不断寻找与最小生成树相连的边权最小的边能通向的点&#xff0c;并将其加入最小生成树&#xff0c;直至所有顶点都在最小生成树中。

第二种&#xff1a;

1.将一个图的顶点分为两部分&#xff0c;一部分是最小生成树中的结点&#xff08;A集合&#xff09;&#xff0c;另一部分是未处理的结点&#xff08;B集合&#xff09;。

2.首先选择一个结点&#xff0c;将这个结点加入A中&#xff0c;然后&#xff0c;对集合A中的顶点遍历&#xff0c;找出A中顶点关联的边权值最小的那个&#xff08;设为v&#xff09;&#xff0c;将此顶点从B中删除&#xff0c;加入集合A中。

3.递归重复步骤2&#xff0c;直到B集合中的结点为空&#xff0c;结束此过程。

4.A集合中的结点就是由Prime算法得到的最小生成树的结点&#xff0c;依照步骤2的结点连接这些顶点&#xff0c;得到的就是这个图的最小生成树。


 

2.克鲁斯卡尔&#xff08;kluskal&#xff09;:在剩下的所有未选取的边中&#xff0c;找最小边&#xff0c;如果和已选取的边构成回路&#xff0c;则放弃&#xff0c;选取次小边。

五、最短路径

1.迪杰斯特拉算法&#xff08;Dijkstra&#xff09;&#xff1a;把图中的顶点集合V分成两组&#xff0c;第一组为已求出最短路径的顶点集合S&#xff08;初始时S中只有源节点&#xff0c;以后每求得一条最短路径&#xff0c;就将它对应的顶点加入到集合S中&#xff0c;直到全部顶点都加入到S中&#xff09;&#xff1b;第二组是未确定最短路径的顶点集合U。

算法步骤&#xff1a;
    &#xff08;1&#xff09;初始化时&#xff0c;S只含有源节点&#xff1b;
    &#xff08;2&#xff09;从U中选取一个距离v最小的顶点k加入S中&#xff08;该选定的距离就是v到k的最短路径长度&#xff09;&#xff1b;
    &#xff08;3&#xff09;以k为新考虑的中间点&#xff0c;修改U中各顶点的距离&#xff1b;若从源节点v到顶点u的距离&#xff08;经过顶点k&#xff09;比原来距离&#xff08;不经过顶点k&#xff09;短&#xff0c;则修改顶点u的距离值&#xff0c;修改后的距离值是顶点k的距离加上k到u的距离&#xff1b;
    &#xff08;4&#xff09;重复步骤&#xff08;2&#xff09;和&#xff08;3&#xff09;&#xff0c;直到终点在S中。

2.弗洛伊德算法&#xff08;Floyd&#xff09;&#xff1a;

1&#xff0c;从任意一条单边路径开始。所有两点之间的距离是边的权&#xff0c;如果两点之间没有边相连&#xff0c;则权为无穷大。
2&#xff0c;对于每一对顶点 u 和 v&#xff0c;看看是否存在一个顶点 w 使得从 u 到 w 再到 v 比已知的路径更短。如果是更新它。

--------------------- 

基本概念
图(Graph)&#xff1a;图(Graph)是一种比线性表和树更为复杂的数据结构。 
图结构&#xff1a;是研究数据元素之间的多对多的关系。在这种结构中&#xff0c;任意两个元素之间可能存在关系。即结点之间的关系可以是任意的&#xff0c;图中任意元素之间都可能相关。 
图G由两个集合V(顶点Vertex)和E(边Edge)组成&#xff0c;定义为G&#61;(V&#xff0c;E) 
线性结构&#xff1a;是研究数据元素之间的一对一关系。在这种结构中&#xff0c;除第一个和最后一个元素外&#xff0c;任何一个元素都有唯一的一个直接前驱和直接后继。 
树结构&#xff1a;是研究数据元素之间的一对多的关系。在这种结构中&#xff0c;每个元素对下(层)可以有0个或多个元素相联系&#xff0c;对上(层)只有唯一的一个元素相关&#xff0c;数据元素之间有明显的层次关系。

图相关的概念和术语
一个图(G)定义为一个偶对(V,E) &#xff0c;记为G&#61;(V,E) 。其中&#xff1a; V是顶点(Vertex)的非空有限集合&#xff0c;记为V(G)&#xff1b;E是无序集V&V的一个子集&#xff0c;记为E(G) &#xff0c;其元素是图的弧(Arc)。 
将顶点集合为空的图称为空图。其形式化定义为&#xff1a;

G&#61;(V &#xff0c;E)
V&#61;{v|vdata object}
E&#61;{| v,wV∧p(v,w)}
1
2
3
1. 无向图和有向图
对于一个图&#xff0c;若每条边都是没有方向的&#xff0c;则称该图为无向图。图示如下&#xff1a; 
 
因此&#xff0c;(Vi&#xff0c;Vj)和(Vj&#xff0c;Vi)表示的是同一条边。注意&#xff0c;无向图是用小括号&#xff0c;而下面介绍的有向图是用尖括号。 
无向图的顶点集和边集分别表示为&#xff1a; 
V(G)&#61;{V1&#xff0c;V2&#xff0c;V3&#xff0c;V4&#xff0c;V5} 
E(G)&#61;{(V1&#xff0c;V2)&#xff0c;(V1&#xff0c;V4)&#xff0c;(V2&#xff0c;V3)&#xff0c;(V2&#xff0c;V5)&#xff0c;(V3&#xff0c;V4)&#xff0c;(V3&#xff0c;V5)&#xff0c;(V4&#xff0c;V5)} 
对于一个图G&#xff0c;若每条边都是有方向的&#xff0c;则称该图为有向图。图示如下&#xff1a; 


有向图的顶点集和边集分别表示为&#xff1a; 
V(G)&#61;{V1&#xff0c;V2&#xff0c;V3} 
E(G)&#61;{

2,无向完全图和有向完全图
我们将具有n(n-1)/2条边的无向图称为无向完全图。同理&#xff0c;将具有n(n-1)条边的有向图称为有向完全图。

完全无向图
对于无向图&#xff0c;若图中顶点数为n &#xff0c;用e表示边的数目&#xff0c;则e [0&#xff0c;n(n-1)/2] 。具有n(n-1)/2条边的无向图称为完全无向图。 
完全无向图另外的定义是&#xff1a; 
对于无向图G&#61;(V&#xff0c;E)&#xff0c;若vi&#xff0c;vj V &#xff0c;当vi≠vj时&#xff0c;有(vi ,vj)E&#xff0c;即图中任意两个不同的顶点间都有一条无向边&#xff0c;这样的无向图称为完全无向图。 
#### 完全有向图 
对于有向图&#xff0c;若图中顶点数为n &#xff0c;用e表示弧的数目&#xff0c;则e[0&#xff0c;n(n-1)] 。具有n(n-1)条边的有向图称为完全有向图。 
完全有向图另外的定义是&#xff1a; 
对于有向图G&#61;(V&#xff0c;E)&#xff0c;若vi&#xff0c;vjV &#xff0c;当vi ≠vj时&#xff0c;图中任意两个不同的顶点间都有一条弧&#xff0c;这样的有向图称为完全有向图。

子图和生成子图
设有图G&#61;(V&#xff0c;E)和G’&#61;(V’&#xff0c;E’)&#xff0c;若V’V且E’E &#xff0c;则称图G’是G的子图&#xff1b;若V’&#61;V且E’E&#xff0c;则称图G’是G的一个生成子图。

连通图(无向图)
连通图是指图G中任意两个顶点Vi和Vj都连通&#xff0c;则称为连通图。比如图(b)就是连通图。下面是一个非连通图的例子。 


图的创建和遍历
图的遍历方法
1) 深度优先搜索遍历 
深度优先搜索DFS遍历类似于树的前序遍历。其基本思路是&#xff1a; 
a) 假设初始状态是图中所有顶点都未曾访问过&#xff0c;则可从图G中任意一顶点v为初始出发点&#xff0c;首先访问出发点v&#xff0c;并将其标记为已访问过。 
b) 然后依次从v出发搜索v的每个邻接点w&#xff0c;若w未曾访问过&#xff0c;则以w作为新的出发点出发&#xff0c;继续进行深度优先遍历&#xff0c;直到图中所有和v有路径相通的顶点都被访问到。 
c) 若此时图中仍有顶点未被访问&#xff0c;则另选一个未曾访问的顶点作为起点&#xff0c;重复上述步骤&#xff0c;直到图中所有顶点都被访问到为止。 
图示如下&#xff1a;

注&#xff1a;红色数字代表遍历的先后顺序&#xff0c;所以图(e)无向图的深度优先遍历的顶点访问序列为&#xff1a;V0&#xff0c;V1&#xff0c;V2&#xff0c;V5&#xff0c;V4&#xff0c;V6&#xff0c;V3&#xff0c;V7&#xff0c;V8 
如果采用邻接矩阵存储&#xff0c;则时间复杂度为O(n2)&#xff1b;当采用邻接表时时间复杂度为O(n&#43;e)。 
2) 广度优先搜索遍历 
广度优先搜索遍历BFS类似于树的按层次遍历。其基本思路是&#xff1a; 
a) 首先访问出发点Vi 
b) 接着依次访问Vi的所有未被访问过的邻接点Vi1&#xff0c;Vi2&#xff0c;Vi3&#xff0c;…&#xff0c;Vit并均标记为已访问过。 
c) 然后再按照Vi1&#xff0c;Vi2&#xff0c;… &#xff0c;Vit的次序&#xff0c;访问每一个顶点的所有未曾访问过的顶点并均标记为已访问过&#xff0c;依此类推&#xff0c;直到图中所有和初始出发点Vi有路径相通的顶点都被访问过为止。 
图示如下&#xff1a; 
 
因此&#xff0c;图(f)采用广义优先搜索遍历以V0为出发点的顶点序列为&#xff1a;V0&#xff0c;V1&#xff0c;V3&#xff0c;V4&#xff0c;V2&#xff0c;V6&#xff0c;V8&#xff0c;V5&#xff0c;V7 
如果采用邻接矩阵存储&#xff0c;则时间复杂度为O(n2)&#xff0c;若采用邻接表&#xff0c;则时间复杂度为O(n&#43;e)。

图的其他算法实现
图的创建
AdjGraph  *Create_Graph(MGraph * G)
{   printf(“请输入图的种类标志&#xff1a;”) ;
scanf(“%d”, &G->kind) ;
G->vexnum&#61;0 ;       /*  初始化顶点个数  */
return(G) ; 
}
1
2
3
4
5
6
7
图的顶点定位
图的顶点定位操作实际上是确定一个顶点在vexs数组中的位置(下标) &#xff0c;其过程完全等同于在顺序存储的线性表中查找一个数据元素。

int  LocateVex(MGraph *G , VexType *vp) 
   {  int  k ;
       for (k&#61;0 ; kvexnum ; k&#43;&#43;)
            if (G->vexs[k]&#61;&#61;*vp)  return(k) ;
       return(-1) ;     /*  图中无此顶点  */
   }
1
2
3
4
5
6
7
向图中增加顶点
向图中增加一个顶点的操作&#xff0c;类似在顺序存储的线性表的末尾增加一个数据元素。

int  AddVertex(MGraph *G , VexType *vp) 
{  int  k , j ;
if  (G->vexnum>&#61;MAX_VEX)
{  printf(“Vertex Overflow !\n”) ; return(-1) ;  }
if  (LocateVex(G , vp)!&#61;-1)
{  printf(“Vertex has existed !\n”) ; return(-1) ; }
k&#61;G->vexnum ; G->vexs[G->vexnum&#43;&#43;]&#61;*vp ;
if (G->kind&#61;&#61;DG||G->kind&#61;&#61;AG) 
for (j&#61;0 ; jvexnum ; j&#43;&#43;)
G->adj[j][k].ArcVal&#61;G->adj[k][j].ArcVal&#61;0 ;
      /*  是不带权的有向图或无向图  */
else
for (j&#61;0 ; jvexnum ; j&#43;&#43;)  
{   G->adj[j][k].ArcVal&#61;INFINITY ;
    G->adj[k][j].ArcVal&#61;INFINITY ;
         /*  是带权的有向图或无向图  */
}
return(k) ;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
向图中增加一条弧
根据给定的弧或边所依附的顶点&#xff0c;修改邻接矩阵中所对应的数组元素。

int  AddArc(MGraph *G , ArcType *arc) 
{   int  k , j ;
k&#61;LocateVex(G , &arc->vex1)  ;
j&#61;LocateVex(G , &arc->vex1)  ;
if (k&#61;&#61;-1||j&#61;&#61;-1)     
{  printf(“Arc’s Vertex do not existed !\n”) ;
return(-1) ;
if (G->kind&#61;&#61;DG||G->kind&#61;&#61;WDG) 
{  G->adj[k][j].ArcVal&#61;arc->ArcVal;
G->adj[k][j].ArcInfo&#61;arc->ArcInfo ;
        /*  是有向图或带权的有向图*/
}
else
{  G->adj[k][j].ArcVal&#61;arc->ArcVal ;
G->adj[j][k].ArcVal&#61;arc->ArcVal ;
G->adj[k][j].ArcInfo&#61;arc->ArcInfo ;
G->adj[j][k].ArcInfo&#61;arc->ArcInfo ;
      /*  是无向图或带权的无向图,需对称赋值  */
}
return(1) ;  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
最小生成树
一个连通图的生成树是一个极小的连通子图&#xff0c;它含有图中全部顶点&#xff0c;但只有足以构成一棵树的n-1条边。那么我们把构造连通网的最小代价生成树称为最小生成树。 
找连通网的最小生成树&#xff0c;经典的有两种算法&#xff0c;普里姆算法和克鲁斯卡尔算法。

邻接矩阵存储
&#64;Test  
    public void Dijkstra(){  
        int distance[] &#61; new int[9];  
        int pre[] &#61; new int[9];  
        boolean finished[] &#61; new boolean[9];  
        finished[0] &#61; true;  
        for(int i&#61;0;i<9;i&#43;&#43;){  
            distance[i] &#61; g1.adjMatrix[0][i];  
        }  
        int k &#61; 0;  
        for(int i&#61;1;i<9;i&#43;&#43;){  
            int min &#61; 65536;  
            for(int j&#61;0;j<9;j&#43;&#43;){  
                if(!finished[j]&&distance[j]                     min &#61; distance[j];  
                    k &#61; j;  
                }  
            }  
            finished[k] &#61; true;  
            System.out.println(pre[k]&#43;","&#43;k);  
            for(int j&#61;1;j<9;j&#43;&#43;){  
                if(!finished[j]&&(min&#43;g1.adjMatrix[k][j])                     distance[j] &#61; min&#43;g1.adjMatrix[k][j];  
                    pre[j] &#61; k;  
                }  
            }  
        }  
    }  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
拓扑排序
(1)栈&#xff1a;用来存放入度为0的节点&#xff1b; 
(2)变种邻接列表&#xff1a;作为图的存储结构&#xff1b;此邻接列表的顶点节点还需要存放入度属性&#xff1b;

private static String topologicalSort(Graph2 g2) {  
        Stack s &#61; new Stack();  
        int count &#61; 0;  
        for(int i&#61;0;i             if(g2.nodes[i].indegree&#61;&#61;0){  
                s.push(i);  
            }  
        }  
        while(!s.isEmpty()){  
            int value &#61; s.pop();  
            System.out.println(value&#43;"、");  
            count&#43;&#43;;  
            EdgeNode node &#61; g2.nodes[value].next;  
            while(node!&#61;null){  
                g2.nodes[node.idx].indegree--;  
                if(g2.nodes[node.idx].indegree&#61;&#61;0){  
                    s.push(node.idx);  
                }  
                node &#61; node.next;  
            }  

        }  
        if(count             return "error";  
        }  
        return "ok";  
    }  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
图的边表存储结构
在某些应用中&#xff0c;有时主要考察图中各个边的权值以及所依附的两个顶点&#xff0c;即图的结构主要由边来表示&#xff0c;称为边表存储结构。 
在边表结构中&#xff0c;边采用顺序存储&#xff0c;每个边元素由三部分组成&#xff1a;边所依附的两个顶点和边的权值&#xff1b;图的顶点用另一个顺序结构的顶点表存储。如图&#xff1a; 

--------------------- 

1、名词解释&#xff1a;
图&#xff08;Graph&#xff09;是由顶点的有穷非空集合和顶点之间边的集合组成&#xff0c;通常表示为&#xff1a;G&#xff08;V&#xff0c;E&#xff09;&#xff0c;其中&#xff0c;G表示一个图&#xff0c;V是图G中顶点的集合&#xff0c;E是图G中边的集合。在图中的数据元素&#xff0c;我们称之为顶点&#xff08;Vertex&#xff09;&#xff0c;顶点集合有穷非空。在图中&#xff0c;任意两个顶点之间都可能有关系&#xff0c;顶点之间的逻辑关系用边来表示&#xff0c;边集可以是空的。

图按照边的有无方向分为无向图和有向图。无向图由顶点和边组成&#xff0c;有向图由顶点和弧构成。弧有弧尾和弧头之分&#xff0c;带箭头一端为弧头。

图按照边或弧的多少分稀疏图和稠密图。如果图中的任意两个顶点之间都存在边叫做完全图&#xff0c;有向的叫有向完全图。若无重复的边或顶点到自身的边则叫简单图。

图中顶点之间有邻接点、依附的概念。无向图顶点的边数叫做度。有向图顶点分为入度和出度。

图上的边或弧带有权则称为网。

图中顶点间存在路径&#xff0c;两顶点存在路径则说明是连通的&#xff0c;如果路径最终回到起始点则称为环&#xff0c;当中不重复的叫简单路径。若任意两顶点都是连通的&#xff0c;则图就是连通图&#xff0c;有向则称为强连通图。图中有子图&#xff0c;若子图极大连通则就是连通分量&#xff0c;有向的则称为强连通分量。

无向图中连通且n个顶点n-1条边称为生成树。有向图中一顶点入度为0其余顶点入度为1的叫有向树。一个有向图由若干棵有向树构成生成森林。

2、图的存储结构—-邻接矩阵
图的邻接矩阵的表示方式需要两个数组来表示图的信息&#xff0c;一个一维数组表示每个数据元素的信息&#xff0c;一个二维数组&#xff08;邻接矩阵&#xff09;表示图中的边或者弧的信息。

如果图有n个顶点&#xff0c;那么邻接矩阵就是一个n*n的方阵&#xff0c;方阵中每个元素的值的计算公式如下&#xff1a; 


邻接矩阵表示图的具体示例如下图所示&#xff1a;

首先给个无向图的实例&#xff1a;

下面是一个有向图的实例&#xff1a; 


OK&#xff0c;到这里为止&#xff0c;我们给出一个无向图的邻接矩阵和一个有向图的邻接矩阵&#xff0c;我们可以从这两个邻接矩阵得出一些结论&#xff1a;

无向图的邻接矩阵都是沿对角线对称的
要知道无向图中某个顶点的度&#xff0c;其实就是这个顶点vi在邻接矩阵中第i行或&#xff08;第i列&#xff09;的元素之和&#xff1b;
对于有向图&#xff0c;要知道某个顶点的出度&#xff0c;其实就是这个顶点vi在邻接矩阵中第i行的元素之和&#xff0c;如果要知道某个顶点的入度&#xff0c;那就是第i列的元素之和。
但是&#xff0c;如果我们需要表示的图是一个网的时候&#xff0c;例如假设有个图有n个顶点&#xff0c;同样该网的邻接矩阵也是一个n*n的方阵&#xff0c;只是方阵元素的值的计算方式不同&#xff0c;如下图所示&#xff1a; 


这里的wij表示两个顶点vi和vj边上的权值。无穷大表示一个计算机允许的、大于所有边上权值的值&#xff0c;也就是一个不可能的极限值。下面是具体示例&#xff0c;表示的一个有向网的图和邻接矩阵&#xff1a;

3、图的存储结构—-邻接矩阵的代码实现
#include
using namespace std;


enum Graphkind{ DG, DN, UDG, UDN }; //{有向图&#xff0c;无向图&#xff0c;有向网&#xff0c;无向网}
typedef struct  Node
{
    int * vex;  //顶点数组
    int vexnum; //顶点个数
    int edge;   //图的边数
    int ** adjMatrix; //图的邻接矩阵
    enum Graphkind kind;
}MGraph;
void createGraph(MGraph & G,enum Graphkind kind)
{
    cout <<"输入顶点的个数" <     cin >> G.vexnum;
    cout <<"输入边的个数" <     cin >> G.edge;
    //输入种类

    //cout <<"输入图的种类&#xff1a;DG:有向图 DN&#xff1a;无向图&#xff0c;UDG&#xff1a;有向网,UDN:无向网" <     G.kind &#61; kind;
    //为两个数组开辟空间
    G.vex &#61; new int[G.vexnum];
    G.adjMatrix &#61; new int*[G.vexnum];
    cout <     int i;
    for (i &#61; 0; i     {
        G.adjMatrix[i] &#61; new int[G.vexnum];
    }
    for (i &#61; 0; i     {
        for (int k &#61; 0; k         {
            if (G.kind &#61;&#61; DG || G.kind &#61;&#61; DN)
            {
                G.adjMatrix[i][k] &#61; 0;
            }
            else {
                G.adjMatrix[i][k] &#61; INT_MAX;
            }
        }

    }

    /*//输入每个元素的信息,这个信息&#xff0c;现在还不需要使用
    for (i &#61; 0; i     {
        cin >> G.vex[i];
    }*/
    cout <<"请输入两个有关系的顶点的序号&#xff1a;例如&#xff1a;1 2 代表1号顶点指向2号顶点" <     for (i &#61; 0; i     {
        int a, b;
        cin >> a;
        cin >> b;
        if (G.kind &#61;&#61; DN) {
            G.adjMatrix[b - 1][a - 1] &#61; 1;
            G.adjMatrix[a - 1][b - 1] &#61; 1;
        }
        else if (G.kind &#61;&#61; DG)
        {
            G.adjMatrix[a - 1][b - 1] &#61; 1;
        }
        else if (G.kind &#61;&#61; UDG)
        {
            int weight;
            cout <<"输入该边的权重&#xff1a;" <             cin >> weight;
            G.adjMatrix[a - 1][b - 1] &#61; weight;
        }
        else {
            int weight;
            cout <<"输入该边的权重&#xff1a;" <             cin >> weight;
            G.adjMatrix[b - 1][a - 1] &#61; weight;
            G.adjMatrix[a - 1][b - 1] &#61; weight;
        }   
    }
}


void print(MGraph g)
{
    int i, j;
    for (i &#61; 0; i     {
        for (j &#61; 0; j         {
            if (g.adjMatrix[i][j] &#61;&#61; INT_MAX)
                cout <<"∞" <<" ";
            else
            cout <         }
        cout <     }
}

void clear(MGraph G)
{
    delete G.vex;
    G.vex &#61; NULL;
    for (int i &#61; 0; i     {
        delete G.adjMatrix[i];
        G.adjMatrix[i] &#61; NULL;
    }
    delete G.adjMatrix;
}
int main()
{

        MGraph G;
        cout <<"有向图例子&#xff1a;" <         createGraph(G, DG);
        print(G);
        clear(G);
        cout <         cout <<"无向图例子&#xff1a;" <         createGraph(G, DN);
        print(G);
        clear(G);

        cout <         cout <<"有向图网例子&#xff1a;" <         createGraph(G, UDG);
        print(G);
        clear(G);

        cout <         cout <<"无向图网例子&#xff1a;" <         createGraph(G, UDN);
        print(G);
        clear(G);

        cout <     return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
输出结果&#xff1a; 


4、图的存储结构—-邻接矩阵的优缺点
优点&#xff1a; 
直观、容易理解&#xff0c;可以很容易的判断出任意两个顶点是否有边&#xff0c;最大的优点就是很容易计算出各个顶点的度。
缺点&#xff1a; 
当我么表示完全图的时候&#xff0c;邻接矩阵是最好的表示方法&#xff0c;但是对于稀疏矩阵&#xff0c;由于它边少&#xff0c;但是顶点多&#xff0c;这样就会造成空间的浪费。
5、 图的存储结构—邻接表
邻接表是图的一种链式存储结构。主要是应对于邻接矩阵在顶点多边少的时候&#xff0c;浪费空间的问题。它的方法就是声明两个结构。如下图所示&#xff1a;

OK&#xff0c;我们虽然知道了邻接表是这两个结构来表示图的&#xff0c;那么它的怎么表示的了&#xff0c;不急&#xff0c;我们先把它转为c&#43;&#43;代码先&#xff0c;然后&#xff0c;再给个示例&#xff0c;你就明白了。

typedef char Vertextype;
//表结点结构
struct ArcNode {
    int adjvex;   //某条边指向的那个顶点的位置&#xff08;一般是数组的下标&#xff09;。
    ArcNode * nextarc; //指向下一个表结点
    int weight;   //这个只有网图才需要使用。普通的图可以直接忽略
};

//头结点
struct Vnode
{
    Vertextype data;  //这个是记录每个顶点的信息&#xff08;现在一般都不需要怎么使用&#xff09;
    ArcNode * firstarc; //指向第一条依附在该顶点边的信息&#xff08;表结点&#xff09;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
无向图的示例&#xff1a;

从图中我们可以知道&#xff0c;顶点是通过一个头结点类型的一维数组来保存的&#xff0c;其中我们每个头结点的firstarc都是指向第一条依附在该顶点边的信息&#xff0c;表结点的adjvex表示的该边的另外一个顶点在顶点数组中的下标&#xff0c;weight域对于普通图是无意义的&#xff0c;可以忽略&#xff0c;nextarc指向的是下一条依附在该顶点的边的信息。 
下面再给出一个有向图的例子&#xff1a; 


通过上述的两个例子&#xff0c;我们应该明白邻接表是如何进行表示图的信息的了。

6、图的存储结构—-邻接表的代码实现
#include
#include
using namespace std;

typedef string Vertextype;
//表结点结构
struct ArcNode {
    int adjvex;   //某条边指向的那个顶点的位置&#xff08;一般是数组的下标&#xff09;。
    ArcNode * nextarc; //指向下一个表结点
    int weight;   //这个只有网图才需要使用。
};

//头结点
struct Vnode
{
    Vertextype data;  //这个是记录每个顶点的信息&#xff08;现在一般都不需要怎么使用&#xff09;
    ArcNode * firstarc; //指向第一条依附在该顶点边的信息&#xff08;表结点&#xff09;
};

//
struct Graph
{
    int kind;  //图的种类(有向图&#xff1a;0,无向图&#xff1a;1&#xff0c;有向网&#xff1a;2&#xff0c;无向网&#xff1a;3)
    int vexnum; //图的顶点数
    int edge;  //图的边数
    Vnode * node; //图的&#xff08;顶点&#xff09;头结点数组
};

void createGraph(Graph & g,int kind)
{
    cout <<"请输入顶点的个数&#xff1a;" <     cin >> g.vexnum;
    cout <<"请输入边的个数&#xff08;无向图/网要乘2&#xff09;&#xff1a;" <     cin >> g.edge;
    g.kind &#61; kind; //决定图的种类
    g.node &#61; new Vnode[g.vexnum];
    int i;
    cout <<"输入每个顶点的信息&#xff1a;" <     for (i &#61; 0; i     {
        cin >> g.node[i].data;
        g.node[i].firstarc&#61;NULL;

    }

    cout <<"请输入每条边的起点和终点的编号&#xff1a;" <     for (i &#61; 0; i     {
        int a;
        int b;
        cin >> a; //起点
        cin >> b; //终点
        ArcNode * next&#61;new ArcNode;
        next->adjvex &#61; b - 1;
        if(kind&#61;&#61;0 || kind&#61;&#61;1)
        next->weight &#61; -1;
        else {//如果是网图&#xff0c;还需要权重
            cout <<"输入该边的权重&#xff1a;" <             cin >> next->weight;
        }
        next->nextarc &#61; NULL;

        //将边串联起来
        if (g.node[a - 1].firstarc &#61;&#61; NULL) {
            g.node[a - 1].firstarc&#61;next;

        }
        else 
        {
            ArcNode * p;
            p &#61; g.node[a - 1].firstarc;
            while (p->nextarc)//找到该链表的最后一个表结点
            {
                p &#61; p->nextarc;
            }
            p->nextarc &#61; next;
        }
    }
}

void print(Graph  g)
{
    int i;
    cout <<"图的邻接表为&#xff1a;" <     for (i &#61; 0; i     {
        cout <         ArcNode * now;
        now &#61; g.node[i].firstarc;
        while (now)
        {
            cout <adjvex <<" ";
            now &#61; now->nextarc;
        }
        cout <     }
}

int main()
{
    Graph g;
    cout <<"有向图的例子" <     createGraph(g,0);
    print(g);
    cout <

    cout <<"无向图的例子" <     createGraph(g, 1);
    print(g);
    cout <     return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
输出结果&#xff1a; 
  

7、图的存储结构—-邻接表的优缺点
优点&#xff1a; 
对于&#xff0c;稀疏图&#xff0c;邻接表比邻接矩阵更节约空间。
缺点&#xff1a; 
不容易判断两个顶点是有关系&#xff08;边&#xff09;&#xff0c;顶点的出度容易&#xff0c;但是求入度需要遍历整个邻接表。
8、有向图的存储结构—-十字链表
十字链表是有向图的一个专有的链表结构&#xff0c;我们之前也说了&#xff0c;邻接表对于我们计算顶点的入度是一个很麻烦的事情&#xff0c;而十字链表正好可以解决这个问题。十字链表和邻接表一样&#xff0c;他会有两个结构来表示图&#xff1a;其中一个结构用于保存顶点信息&#xff0c;另外一个结构是用于保存每条边的信息&#xff0c;如下图所示&#xff1a; 


同样&#xff0c;我们知道头结点就是用于保存每个顶点信息的结构&#xff0c;其中data主要是保存顶点的信息&#xff08;如顶点的名称&#xff09;&#xff0c;firstin是保存第一个入度的边的信息&#xff0c;firstout保存第一个出度的边的信息。其中&#xff0c;表结点就是记录每条边的信息&#xff0c;其中tailvex是记录这条边弧头的顶点的在顶点表中的下标&#xff08;不是箭头那个&#xff09;&#xff0c;headvex则是记录弧尾对应的那个顶点在顶点表中的下标&#xff08;箭头的那个&#xff09;&#xff0c;hlink是指向具有下一个具有相同的headvex的表结点&#xff0c;tlink指向具有相同的tailvex的表结点&#xff0c;weight是表示边的权重&#xff08;网图才需要使用&#xff09;。具体的代码表示如下&#xff1a;

typedef string Vertextype;
//表结点结构
struct ArcNode {
    int tailvex;   //弧尾的下标&#xff0c;一般都是和对应的头结点下标相同
    int headvex;   //弧头的下标
    ArcNode * hlink; //指向下一个弧头同为headvex的表结点 &#xff0c;边是箭头的那边
    ArcNode * tlink;  //指向下一个弧尾为tailvex的表结点,边不是箭头的那边
    int weight;  //只有网才会用这个变量

};

//头结点
struct Vnode
{
    Vertextype data;  //这个是记录每个顶点的信息&#xff08;现在一般都不需要怎么使用&#xff09;
    ArcNode *firstin; //指向第一条&#xff08;入度&#xff09;在该顶点的表结点
    ArcNode *firstout; //指向第一条&#xff08;出度&#xff09;在该顶点的表结点
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
下面&#xff0c;我们给出一个有向图的十字链表的例子&#xff1a; 


其实&#xff0c;这个自己也可以去尝试手画一个十字链表出来&#xff0c;其实都是很简单的

9、有向图的存储结构—-十字链表代码实现
#include
#include
using namespace std;

typedef string Vertextype;
//表结点结构
struct ArcNode {
    int tailvex;   //弧尾的下标&#xff0c;一般都是和对应的头结点下标相同
    int headvex;   //弧头的下标
    ArcNode * hlink; //指向下一个弧头同为headvex的表结点 &#xff0c;边是箭头的那边
    ArcNode * tlink;  //指向下一个弧尾为tailvex的表结点,边不是箭头的那边
    int weight;  //只有网才会用这个变量

};

//头结点
struct Vnode
{
    Vertextype data;  //这个是记录每个顶点的信息&#xff08;现在一般都不需要怎么使用&#xff09;
    ArcNode *firstin; //指向第一条&#xff08;入度&#xff09;在该顶点的表结点
    ArcNode *firstout; //指向第一条&#xff08;出度&#xff09;在该顶点的表结点
};

struct Graph
{
    int kind;  //图的种类(有向图&#xff1a;0&#xff0c;有向网&#xff1a;1)
    int vexnum; //图的顶点数
    int edge;  //图的边数
    Vnode * node; //图的&#xff08;顶点&#xff09;头结点数组
};

void createGraph(Graph & g,int kind)
{
    cout <<"请输入顶点的个数&#xff1a;" <     cin >> g.vexnum;
    cout <<"请输入边的个数&#xff08;无向图/网要乘2&#xff09;&#xff1a;" <     cin >> g.edge;
    g.kind &#61; kind; //决定图的种类
    g.node &#61; new Vnode[g.vexnum];
    int i;
    cout <<"输入每个顶点的信息&#xff1a;" <     for (i &#61; 0; i     {
        cin >> g.node[i].data;
        g.node[i].firstin &#61; NULL;
        g.node[i].firstout &#61; NULL;
    }

    cout <<"请输入每条边的起点和终点的编号&#xff1a;" <     for (i &#61; 0; i     {
        int a, b;
        cin >> a;
        cin >> b;

        ArcNode * next &#61; new ArcNode;
        next->tailvex &#61; a - 1; //首先是弧头的下标
        next-> headvex &#61; b - 1; //弧尾的下标
        //只有网图需要权重信息
        if(kind&#61;&#61;0)
        next->weight &#61; -1;
        else
        {
            cout <<"输入该边的权重&#xff1a;" <             cin >> next->weight;
        }
        next->tlink &#61; NULL;
        next->hlink &#61; NULL;
        //该位置的顶点的出度还为空时&#xff0c;直接让你fisrstout指针指向新的表结点
        //记录的出度信息
        if (g.node[a - 1].firstout &#61;&#61; NULL)
        {
            g.node[a - 1].firstout &#61; next;
        }
        else
        {
            ArcNode * now;
            now &#61; g.node[a - 1].firstout;
            while (now->tlink)
            {
                now &#61; now->tlink;
            }
            now->tlink &#61; next;
        }
        //记录某个顶点的入度信息
        if (g.node[b - 1].firstin &#61;&#61; NULL)
        {
            g.node[b - 1].firstin &#61; next;
        }
        else {
            ArcNode * now;
            now &#61; g.node[b - 1].firstin;
            while (now->hlink)//找到最后一个表结点
            {
                now &#61; now->hlink;
            }
            now->hlink &#61; next;//更新最后一个表结点
        }
    }
}

void print(Graph g)
{
    int i;
    cout <<"各个顶点的出度信息" <     for (i &#61; 0; i     {
        cout <         ArcNode * now;
        now &#61; g.node[i].firstout;
        while (now)
        {
            cout <headvex <<" ";
            now &#61; now->tlink;
        }
        cout <<"^" <     }

    cout <<"各个顶点的入度信息" <

    for (i &#61; 0; i     {
        cout <         ArcNode * now;
        now &#61; g.node[i].firstin;
        while (now)
        {
            cout <tailvex <<" ";
            now &#61; now->hlink;
        }
        cout <<"^" <

    }
}


int main()
{
    Graph g;
    cout <<"有向图的例子" <     createGraph(g, 0);
    print(g);
    cout <     return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145


10、无向图的存储结构—-邻接多重表
邻接多重表是无向图的另一种链式存储结构。我们之前也说了使用邻接矩阵来存储图比价浪费空间&#xff0c;但是如果我们使用邻接表来存储图时&#xff0c;对于无向图又有一些不便的地方&#xff0c;例如我们需要对一条已经访问过的边进行删除或者标记等操作时&#xff0c;我们除了需要找到表示同一条边的两个结点。这会给我们的程序执行效率大打折扣&#xff0c;所以这个时候&#xff0c;邻接多重表就派上用场啦。

首先&#xff0c;邻接多重表同样是对邻接表的一个改进得到来的结构&#xff0c;它同样需要一个头结点保存每个顶点的信息和一个表结点&#xff0c;保存每条边的信息&#xff0c;他们的结构如下&#xff1a; 


其中&#xff0c;头结点的结构和邻接表一样&#xff0c;而表结点中就改变比较大了&#xff0c;其中mark为标志域&#xff0c;例如标志是否已经访问过&#xff0c;ivex和jvex代表边的两个顶点在顶点表中的下标&#xff0c;ilink指向下一个依附在顶点ivex的边&#xff0c;jlink指向下一个依附在顶点jvex的边&#xff0c;weight在网图的时候使用&#xff0c;代表该边的权重。

下面是一个无向图的邻接多重表的实例&#xff08;自己也可以尝试去画画&#xff0c;具体的原理都是很简单的&#xff09;&#xff1a; 


11、无向图的存储结构—-邻接多重表代码实现
#include
#include
using namespace std;

//表结点
struct ArcNode
{
    int mark; //标志位
    int ivex; //输入边信息的那个起点
    ArcNode * ilink; //依附在顶点ivex的下一条边的信息
    int jvex;   //输入边信息的那个终点
    ArcNode * jlink; //依附在顶点jvex的下一条边的信息
    int weight;
};

//头结点
struct VexNode {
    string data;   //顶点的信息&#xff0c;如顶点名称
    ArcNode * firstedge; //第一条依附顶点的边
};

struct Graph {
    int vexnum;   //顶点的个数
    int edge;    //边的个数
    VexNode *node; //保存顶点信息
};

void createGraph(Graph & g)
{
    cout <<"请输入顶点的个数&#xff1a;" <     cin >> g.vexnum;
    cout <<"请输入边的个数&#xff08;无向图/网要乘2&#xff09;&#xff1a;" <     cin >> g.edge;
    g.node &#61; new VexNode[g.vexnum];

    int i;
    cout <<"输入每个顶点的信息&#xff1a;" <     for (i &#61; 0; i     {
        cin >> g.node[i].data;
        g.node[i].firstedge &#61; NULL;
    }

    cout <<"请输入每条边的起点和终点的编号&#xff1a;" <     for (i &#61; 0; i     {
        int a, b;
        cin >> a;
        cin >> b;

        ArcNode * next &#61; new ArcNode;
        next->mark &#61; 0;
        next->ivex &#61; a - 1; //首先是弧头的下标
        next->jvex &#61; b - 1; //弧尾的下标
        next->weight &#61; -1;
        next->ilink &#61; NULL;
        next->jlink &#61; NULL;

        //更新顶点表a-1的信息
        if (g.node[a - 1].firstedge &#61;&#61; NULL)
        {
            g.node[a - 1].firstedge &#61; next;
        }
        else {
            ArcNode * now;
            now &#61; g.node[a - 1].firstedge;
            while (1) {
                if (now->ivex &#61;&#61; (a - 1) && now->ilink &#61;&#61; NULL)
                {
                    now->ilink &#61; next;
                    break;
                }
                else if (now->ivex &#61;&#61; (a - 1) && now->ilink !&#61; NULL) {
                    now &#61; now->ilink;
                }
                else if (now->jvex &#61;&#61; (a - 1) && now->jlink &#61;&#61; NULL)
                {
                    now->jlink &#61; next;
                    break;
                }
                else if (now->jvex &#61;&#61; (a - 1) && now->jlink !&#61; NULL) {
                    now &#61; now->jlink;
                }
            }
        }
        //更新顶点表b-1
        if (g.node[b - 1].firstedge &#61;&#61; NULL)
        {
            g.node[b - 1].firstedge &#61; next;
        }
        else {
            ArcNode * now;
            now &#61; g.node[b - 1].firstedge;
            while (1) {
                if (now->ivex &#61;&#61; (b - 1) && now->ilink &#61;&#61; NULL)
                {
                    now->ilink &#61; next;
                    break;
                }
                else if (now->ivex &#61;&#61; (b - 1) && now->ilink !&#61; NULL) {
                    now &#61; now->ilink;
                }
                else if (now->jvex &#61;&#61; (b - 1) && now->jlink &#61;&#61; NULL)
                {
                    now->jlink &#61; next;
                    break;
                }
                else if (now->jvex &#61;&#61; (b - 1) && now->jlink !&#61; NULL) {
                    now &#61; now->jlink;
                }
            }
        }

    }
}

void print(Graph g)
{
    int i;
    for (i &#61; 0; i     {
        cout <         ArcNode * now;
        now &#61; g.node[i].firstedge;
        while (now)
        {
            cout <<"ivex&#61;" <ivex <<" jvex&#61;" <jvex <<" ";
            if (now->ivex &#61;&#61; i)
            {
                now &#61; now->ilink;
            }
            else if (now->jvex &#61;&#61; i)
            {
                now &#61; now->jlink;
            }
        }
        cout <     }
}
int main()
{
    Graph g;
    createGraph(g);
    print(g);
    system("pause");
    return 0;

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
输出结果&#xff1a; 

--------------------- 


转:https://my.oschina.net/hblt147/blog/2987962



推荐阅读
  • 本文详细介绍了Java中vector的使用方法和相关知识,包括vector类的功能、构造方法和使用注意事项。通过使用vector类,可以方便地实现动态数组的功能,并且可以随意插入不同类型的对象,进行查找、插入和删除操作。这篇文章对于需要频繁进行查找、插入和删除操作的情况下,使用vector类是一个很好的选择。 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • 本文讨论了如何优化解决hdu 1003 java题目的动态规划方法,通过分析加法规则和最大和的性质,提出了一种优化的思路。具体方法是,当从1加到n为负时,即sum(1,n)sum(n,s),可以继续加法计算。同时,还考虑了两种特殊情况:都是负数的情况和有0的情况。最后,通过使用Scanner类来获取输入数据。 ... [详细]
  • 本文讨论了使用差分约束系统求解House Man跳跃问题的思路与方法。给定一组不同高度,要求从最低点跳跃到最高点,每次跳跃的距离不超过D,并且不能改变给定的顺序。通过建立差分约束系统,将问题转化为图的建立和查询距离的问题。文章详细介绍了建立约束条件的方法,并使用SPFA算法判环并输出结果。同时还讨论了建边方向和跳跃顺序的关系。 ... [详细]
  • 本文介绍了多因子选股模型在实际中的构建步骤,包括风险源分析、因子筛选和体系构建,并进行了模拟实证回测。在风险源分析中,从宏观、行业、公司和特殊因素四个角度分析了影响资产价格的因素。具体包括宏观经济运行和宏经济政策对证券市场的影响,以及行业类型、行业生命周期和行业政策对股票价格的影响。 ... [详细]
  • 本文介绍了Python对Excel文件的读取方法,包括模块的安装和使用。通过安装xlrd、xlwt、xlutils、pyExcelerator等模块,可以实现对Excel文件的读取和处理。具体的读取方法包括打开excel文件、抓取所有sheet的名称、定位到指定的表单等。本文提供了两种定位表单的方式,并给出了相应的代码示例。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • Java序列化对象传给PHP的方法及原理解析
    本文介绍了Java序列化对象传给PHP的方法及原理,包括Java对象传递的方式、序列化的方式、PHP中的序列化用法介绍、Java是否能反序列化PHP的数据、Java序列化的原理以及解决Java序列化中的问题。同时还解释了序列化的概念和作用,以及代码执行序列化所需要的权限。最后指出,序列化会将对象实例的所有字段都进行序列化,使得数据能够被表示为实例的序列化数据,但只有能够解释该格式的代码才能够确定数据的内容。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • 本文介绍了P1651题目的描述和要求,以及计算能搭建的塔的最大高度的方法。通过动态规划和状压技术,将问题转化为求解差值的问题,并定义了相应的状态。最终得出了计算最大高度的解法。 ... [详细]
  • 本文介绍了解决二叉树层序创建问题的方法。通过使用队列结构体和二叉树结构体,实现了入队和出队操作,并提供了判断队列是否为空的函数。详细介绍了解决该问题的步骤和流程。 ... [详细]
  • Python正则表达式学习记录及常用方法
    本文记录了学习Python正则表达式的过程,介绍了re模块的常用方法re.search,并解释了rawstring的作用。正则表达式是一种方便检查字符串匹配模式的工具,通过本文的学习可以掌握Python中使用正则表达式的基本方法。 ... [详细]
author-avatar
Ale__x小葡萄
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有