数据结构-图内容总结

一、图的两种常用存储结构

邻接矩阵和邻接表存储的都是边的信息,顶点类还需要单独定义和实现。

  1. 邻接矩阵
    特点:无向图中为对称矩阵,适合存储稠密图。
  2. 邻接表
    特点:适合存储稀疏图。

二、图的两种遍历方式

  1. 深度优先遍历(DFS)
    类似于树的先序遍历,采用递归的方法。(以邻接矩阵为例)

    void CMap::depthFirstTraverse(int nodeIndex){
    	//输出顶点值
        cout<<m_pNodeArray[nodeIndex].m_cData<<" ";
        //设置当前节点为已经遍历过
        m_pNodeArray[nodeIndex].m_bIsVisited = true;
        //找到与当前节点连接的节点(还需要未被遍历过)进行遍历
        for(int i =0;i<m_iCapacity;i++){
            if(m_pMatrix[nodeIndex*m_iCapacity+i] && !m_pNodeArray[i].m_bIsVisited){
                depthFirstTraverse(i);
            }
        }
    }
    
  2. 广度优先遍历
    使用队列存储即将访问的节点,与树的层序遍历类似。(以邻接矩阵为例)

    void CMap::breadthFirstTraverse(int nodeIndex){
        queue<int> queue1;
        queue1.push(nodeIndex);//将起始节点压入堆栈
        while(!queue1.empty()){ 
            int temp = queue1.front(); 
            queue1.pop();//取出并删除堆栈第一个节点
            if(!m_pNodeArray[temp].m_bIsVisited){
                cout<<m_pNodeArray[temp].m_cData<<" ";  
                //将该节点访问状态设置为true
                m_pNodeArray[temp].m_bIsVisited = true;
                //找到与该节点相连且未被访问过的节点,压入队列下次访问
                for(int i =0;i<m_iCapacity;i++){
                    if(m_pMatrix[temp*m_iCapacity+i] && !m_pNodeArray[i].m_bIsVisited){
                        queue1.push(i);
                    }
                }
            }
        }
    }
    

三、最短路径的两种算法

  1. Dijkstra算法
    算法的原理这个视频讲的非常清楚B站Dijkstra算法,大概意思是 每次从未确定的顶点中选择一个 源点到未确定顶点集合中cost最小的点,更新与它相连的其他最小cost 并 将 它的确定状态设置为true,直到遍历到终点。

    int CMap::dijkstra(int start, int end) {
        int value[m_iCapacity];    //记录源点到各个点的最小cost
        for(int i = 0;i<m_iCapacity;i++){
            value[i] = 100;        //设置初值为一个很大的值
        }
        value[start] = 0;
        for(int i=0;i<m_iCapacity-1;i++){
            int min = 100;    //记录每次
            int min_index;
            for(int j = 0;j<m_iCapacity;j++){
            //寻找未确定的顶点集合中cost的最小值
                if(value[j]<min && !m_pNodeArray[j].m_bIsVisited){
                    min = value[j];
                    min_index = j;
                }
            }
            m_pNodeArray[min_index].m_bIsVisited = true;
            //更新与最小点连接的其他点的最小cost 
            for(int j = 0;j<m_iCapacity;j++){
                if(!m_pNodeArray[j].m_bIsVisited && m_pMatrix[min_index*m_iCapacity+j]>0)
                    if(value[min_index]+m_pMatrix[min_index*m_iCapacity+j]<value[j]){
                        value[j] = value[min_index]+m_pMatrix[min_index*m_iCapacity+j];
                    }
            }
        }
        return value[end];
    }
    
  2. 弗洛伊德(Floyed)算法
    这个视频B站视频Floyed算法也讲的比较明白,大概思想是遍历每个顶点,更新以当前顶点为中间点的最小cost。比如有ABCD四个点,遍历A时比较 B到C的最小costB到A的最小cost+A到C的最小cost 两者哪个更小。最终返回起始点到终点的最小cost即可。

    int CMap::floyed(int start, int end) {
        int D[m_iCapacity*m_iCapacity];  //存储两点之间cost最小值
        int P[m_iCapacity*m_iCapacity];  //存储两点之间经过的最后一个顶点
        //初始化 初值为很大的数
        for(int i =0;i<m_iCapacity*m_iCapacity;i++){
            if(m_pMatrix[i]) D[i] = m_pMatrix[i];
            else D[i] = 100;
            P[i] = i%m_iCapacity;
        }
        //以i为中间点,从j到k的cost
        for(int i =0;i<m_iCapacity;i++){
            for(int j=0;j<m_iCapacity;j++){
                for(int k=0;k<m_iCapacity;k++){
                    if(D[j*m_iCapacity+i]+D[i*m_iCapacity+k]<D[j*m_iCapacity+k]){
                    	//当从j到k经过i的cost值最小时,更新从j到k的最小值
                        D[j*m_iCapacity+k] = D[j*m_iCapacity+i]+D[i*m_iCapacity+k];
                        //更新从j到k经过的顶点坐标
                        P[j*m_iCapacity+k] = P[j*m_iCapacity+i] ;
                    }
                }
            }
        }
    	//输出路径
        int j = end;
        cout<<end<<" ";
        while(start!=j){
            cout<<P[j*m_iCapacity+start]<<" ";
            j = P[j*m_iCapacity+start];
        }
        cout<<endl;
        //返回最小cost
        return D[start*m_iCapacity+end];
    }
    

四、最小生成树的两种算法

最小生成:无回路、V个顶点有V-1条边
最小生成树:包含全部顶点、V-1条边都在图里、任加一条边就会构成回路。
最小生成树: 边的权重之和最小。
B站视频最小生成树

  1. Prim普利姆算法
    和Dijkstra很像,但也有不同之处
    Prim每次比较的是i到j的最小权值,而Dijkstra比较的是从起点到i的最小权值+i到j的最小权值。

    for(int j = 0;j<m_iCapacity;j++){
                if(!m_pNodeArray[j].m_bIsVisited && m_pMatrix[min_index*m_iCapacity+j]>0)
                //这里的比较不需要加value[min_index]
                    if(m_pMatrix[min_index*m_iCapacity+j]<value[j]){
                        value[j] = m_pMatrix[min_index*m_iCapacity+j];
                    }
            }
    
  2. Kruskal克鲁斯卡尔算法
    主要思想:将权值按从小到大排序,从小到大逐个生成图。如果新插入的权值对应的边使得图变成环,就不加入这个权值,直到插入的边的个数为V-1即可。
    如何判断图成为环:维护一个顶点个数长度的数组,初始化为节点自身的编号,数组内容记录更新构建最小生成树过程中的根节点,如果一条边起点和终点的根节点一样则会形成环。
    在这里插入图片描述
    如图所示,边排序好之后为{[E,F] [C,D] [D,E] [C,F] [B,F]…}

    1. 最小权值为2,E[E]!=F[F],选择EF,将起点为F的全部改成E         ~~~~~~~         E对应的起点为[E],F对应的起点为[E]
    2. 最小权值为3,C[C]!=D[D],选择CD,将起点为D的全部改成C        ~~~~~~       C对应的起点为[C],D对应的起点为[C]
    3. 最小权值为4,D[C]!=E[E],选择DE,将起点为E的全部改成C        ~~~~~~        F、E起点都改为[C]
    4. 最小权值为5,C[C]==E[C],C和E对应的起点都是C,不选择CE
    5. 最小权值为6,C[C]==F[C],C和F对应的起点都是C,不选择CF
    6. 最小权值为7,B[B]!=F[C],选择BF,将起点为C的全部改为B         ~~~~~~~         C对应的起点(E、F、C、D)都改为[B]
    7. 最小权值为8,E[B]!=G[G],选择EG,将起点为G的全部改为B        ~~~~~~        G对应的起点都改为[B]
    8. 最小权值为9,F[B]==G[B],不选择FG
    9. 最小权值为12,A[A]!=B[B],选择AB,将起点B的全部改为A           ~~~~~~~~~           B(B、C、D、E、F、G)对应的起点都改为[A]

       ~~~~~~       A~G共7个顶点,选择了6条边即可完成选择。

五、拓扑排序

看的视频:浙江大学-数据结构P99

  1. 一些名词
  • AOV网:人们常用有向图来描述和分析一项工程的计划和实施过程,一个工程常被分为多个小的子工程,这些子工程被称为活动(Activity),在有向图中若以顶点表示活动,有向边表示活动之间的先后关系。有向无环图才是合理的。
  • 拓扑序列:顶点活动网中将活动按发生的先后次序进行的一种排列。 拓扑排序,是对一个有向无环图(Directed Acyclic Graph简称DAG)G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若边(u,v)∈E(G),则u在线性序列中出现在v之前。
  1. 基本思想:维护一个队列,一开始为空。首先将入度为0的点加入队列中,每操作并弹出一个顶点,将与该顶点相连的顶点入度-1,并判断入度是否为0,如果入度为0则加入队列中,直至队列为空。

六、关键路径

  1. AOE网:在带权有向图中以顶点表示事件,有向边表示活动,边上的权值表示该活动持续的时间。
  2. 关键路径:路径上各个活动所持续时间之和称为路径长度,则从源点到汇点具有在最大长度的路径即为关键路径。
    最大长度路径即加工工序中最耗时的部分,关键路径也就是求加工工序正常完成的最短时间。
           ~~~~~~        看的视频:浙江大学-数据结构P100
           ~~~~~~        可以看视频进行理解。
    视频中P100的结果
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值