上一篇博客中讲了图的基本概念及如何存储,下面学习图的遍历及最小生成树的问题。

图的遍历

广度优先搜索(Breadth First Search,BFS)

举一个例子:

  假设我们都从顶点A开始遍历,左侧为有向图,它的广度优先搜索结果为:A D B E C;右侧为无向图,它的广度优先搜索结果为:A E D B C。
广度优先搜索类似于树的层序遍历,在实现的时候,需要借助队列来实现。

//我们使用一个数组来区别一个顶点是否已经遍历过,遍历过的设置为true。
    void BFS(const V& v)
    {
        queue<int> q;
        vector<bool> visited(_v.size(),false);//遍历过的置为true,默认为false
        size_t index = GetIndex(v);
        q.push(index);
        _BFS(q,visited);
        for (size_t i = 0; i < _v.size(); i++)//为什么要这么写?
            //假如我们的图中的一个顶点与其他的顶点没有关系,即两个顶点之间没有边,
            //那么如果我们不这么写,就会漏掉这个顶点。
            //因此,我们的处理方法是 先遍历一次,然后在看哪个点没有遍历,
            //如果有点没遍历,那么将该点push到队列中遍历。
        {
            if (visited[i] == false)
            {
                q.push(i);
                _BFS(q,visited);
            }
        }
        cout << endl;
    }

    void _BFS(queue<int>& q,vector<bool>& visited)
    {
        while (!q.empty())
        {
            size_t index = q.front();
            q.pop();
            if (visited[index] == true)
                continue;
            cout << _v[index] << " ";
            visited[index] = true;
            pNode pCur = _linkEdges[index];
            while (pCur)
            {
                if (visited[pCur->_dst] == false)
                    q.push(pCur->_dst);
                pCur = pCur->_pNext;
            }
        }
    }

还是上面图中的两个图为例:

  上图中,左侧为有向图,红色数字表示遍历的顺序,箭头表示遍历时是怎么走的,所以它的深度优先搜索结果为:A D E B C , 右侧为无向图,深度优先搜索的结果为:A D E C B。可以发现,遍历的结果并不是唯一的,跟你的图中边的存储有关系。

void DFS(const V& v)
    {
        size_t index = GetIndex(v);
        vector<bool> visited(_v.size(),false);
        _DFS(index,visited);

        for (size_t i = 0; i < _v.size(); i++)
        {
            if (visited[i] == false)
                _DFS(i,visited);
        }
        cout << endl;
    }

    void _DFS(int index,vector<bool>& visited)
    {
        cout << _v[index] << " ";
        visited[index] = true;
        pNode pCur = _linkEdges[index];
        while (pCur)
        {
            if (visited[pCur->_dst] == false)
                _DFS(pCur->_dst,visited);
            pCur = pCur->_pNext;
        }
    }

最小生成树

连通图中的每一颗生成树,都是原图的一个极大无环子图,即:从其中删去任何一条边,生成树就不必再连通;反之,在其中假如一条边,就会形成回路。
若连通图由n个顶点构成,则生成树必有n个顶点和n-1条边。最小生成树是各边权值之和最小的生成树,因此,构成最小生成树有以下三点需要遵守:

  1. 只能使用图中的边来构成最小生成树
  2. 只能使用恰好n-1条边来链接图中的n个顶点
  3. 选用的n-1条边不嫩个构成回路

构成最小生成树有两种算法:Kruskal算法和Prim算法。

Kruskal算法

任给一个有n个顶点的连通网络N,首先构造一个有n个顶点组成,不含任何边的图G,其中,每个顶点自成一个连通分量,不断从N的边中找最小的一条,若该边的两个顶点来自不同的连通分量,则将此边假如到G中。如此重复,直至所有顶点在同一个连通分量上为止。

我们还是举一个例子:

  1. 新建一个图,只有顶点,没有边
  2. 将原图中边的权值,从小到大排序
  3. 我们有5个顶点,因此需要4条边
  4. 借助之前写过的并查集,如果两个顶点不在一个集合,那么将该边假如到新图中;否则,继续看下一条边。
    具体布置如下:

    代码:
typedef Graph<V,W,IsDirect> Self;
    typedef Node LinkEdge;
    Self Kruskal()
    {
        Self g;
        g._v = _v;//新建一个图
        g._linkEdges.resize(_v.size());
        vector<LinkEdge*> edges;
        for (size_t i = 0; i < _v.size(); i++)
        {
            LinkEdge* pCur = _linkEdges[i];     
            while (pCur)
            {
                if (IsDirect || (!IsDirect && pCur->_src < pCur->_dst))//保存边的权值。
                                                   //无向图只需要保存一次,保存src<dst
                    edges.push_back(pCur);
                pCur = pCur->_pNext;
            }       
        }

        class Compare
        {
        public:
            bool operator()(const LinkEdge* left,const LinkEdge* right)
            {
                return left->_weight < right->_weight;
            }
        };
        sort(edges.begin(),edges.end(),Compare());//将保存的边的权值,从小到大排序

        size_t count = _v.size() - 1;//从前往后取n-1条边
        UnionFind u(_v.size());
        for (size_t i = 0; i < edges.size(); i++)
        {
            LinkEdge* pCur = edges[i];
            size_t srcRoot = u.FindRoot(pCur->_src);
            size_t dstRoot = u.FindRoot(pCur->_dst);
            if (srcRoot != dstRoot)//若两个顶点不在同一个集合,才将边加上
            {
                g._Add(pCur->_src,pCur->_dst,pCur->_weight);
                if (!IsDirect)//false为无向图
                    g._Add(pCur->_dst,pCur->_src,pCur->_weight);

                u.Union(pCur->_src,pCur->_dst);//合并
                count--;
                if (count == 0)
                    break;
            }
        }
        if (count > 0)
            cout << "最小生成树非法" << endl;
        return g;
    }

Prime算法

1. 给出一个只有顶点的空图; 2. 权值最小的10,将10加入到生成树中; 3. 原图中A,B相关联的边权值最小的是30,将30加入到生成树中; 4. 原图中A,B,E有关的边,权值最小的是30,将30加入到生成树中; 5. 以此类推,直至所有顶点连通。

【数据结构】图的遍历及最小生成树的更多相关文章

  1. 初识Swift集合之字典集合

    这个函数也会返回被替换或者增加的值。

  2. swift的一些知识点演练

    表示可以有值,也可以没有值//?如果对象为空,就不会调用后面的方法,感觉上和oc中给nil发送消息类似varstr:Nsstring?str="hello"//打印可选项的时候,同时会输出一个Optional,提示开发者,这是一个可选项println(str?.length)letl=10//目前的代码存在什么风险?如果str没有设置初始值,会直接崩溃//苹果把判断对象是否有内容的工作交给了程序员//letlen=l+str!用来快速判断对象是否为nilletlen2=l+(str?0)//以下代码和上面

  3. swift 基础笔记四数组

  4. swift篇第一期:简单的数据结构

    首先我们可以去使用Playground来编码,并且会实时的显示对应的编码信息,这样我们就不用每次都去运行程序来显示输出的东西了哦,也方便了我们对某些语句的验证,这个是比较赞的var与let前者为可变修饰符,后者为不可变从字面意思我们就可以很好的区分了常用的类型呢,跟其他语言基本相同啦,主要有几种:1.int类型2.Float,Double类型3.String类型4.Boolean类型当我们去声明一

  5. Swift值字典使用

    字典是一种用来存放相同类型的数据项的集合。Swift中字典的概念和现实世界中的字典的概念很相似,都是通过索引来查里面特定的值。修改一个值5、删除字典键值对四、字典遍历同数组一样,字典遍历也需要使用forin循环。

  6. Swift学习笔记十三——区间运算符和for-in循环

    区间运算符RangeOperator也是Swift的一个比较突出的特点。可以用来表示一段数据的区域。区间运算符主要可以分为以下两类:ClosedRangeOperator:闭区间[a,b]a...b:注意:a和b之间是三个点Half-ClosedRangeOperator:前闭后开区间a..

  7. Swift遍历数组的三种方式

    1.forindexin0..

  8. Swift入门五——数组Array

    集合集合的定义Swift中提供了两种数据结构用于存放数据的集合,分别是数组和字典。一共有三种方法来定义数组的类型:第一种是数组类型的完整定义,即Array关键字加上一对尖括号,括号内写上数组元素的类型。1]其实是一个SubArray,在Swift中它的类型叫做ArraySlice,即Int类型的数组切片,而右边是一个Array类型变量,根据Swift类型安全的特性,这样的操作自然是被禁止的。

  9. swift-07-使用for-in 遍历数组

    //for-in/*for迭代变量in集合变量{使用迭代变量便利所有数据}*///遍历数组vararr=["a","b","c","d"]fortempinarr{printprint}//vararray:[]=[,("王三",30,("张浩",50,"女")]forvinarr{ifv.0=="王三"{print(v)break}}

  10. Swift 字典的常用方法

    /***要正确使用字典,也需要一些条件*1,字典键值对的键和值的类型必须明确,可以直接指定,也可以类似数组直接赋值由编译器自动识别*2,字典必须要初始化*3,键的类型必须是可以被哈希Hashable的**///字典的几种声明方式常用方法见下方代码苹果开发群:414319235欢迎加入欢迎讨论

随机推荐

  1. 【数据结构】单调栈

    显然,每个发射站发来的能量有可能被0或1或2个其他发射站所接受,特别是为了安全,每个发射站接收到的能量总和是我们很关心的问题。由于数据很多,现只需要你帮忙计算出接收最多能量的发射站接收的能量是多少。输入输出格式输入格式:第1行:一个整数N;第2到N+1行:第i+1行有两个整数Hi和Vi,表示第i个人发射站的高度和发射的能量值。输入输出样例输入样例:34235610输出样例:7题解中有讲解代码实现

  2. BZOJ 1798 [Ahoi2009] Seq 维护序列seq [线段树+多重标记下传]【数据结构】

    有长为N的数列,不妨设为a1,a2,…Input第一行两个整数N和P。第二行含有N个非负整数,从左到右依次为a1,aN,。表示把所有满足t≤i≤g的ai改为ai×c。操作2:“2tgc”。同一行相邻两数之间用一个空格隔开,每行开头和末尾没有多余空格。Output对每个操作3,按照它在输入中出现的顺序,依次输出一行一个整数表示询问结果。SampleInput7431234567512553242379313347SampleOutput2358HINT初始时数列为。对第5次操作,和为29+34+15+16=

  3. 陈越《数据结构》第一讲 基本概念

    陈越《数据结构》第一讲基本概念1什么是数据结构1.1引子例子:如何在书架上摆放图书?数据结构是:1.数据对象在计算机中的组织方式;2.数据对象必定与一系列加在其上的操作相关联;3.完成这些操作所用的方法就是算法。抽象数据类型数据类型-数据对象集;-数据集合相关联的操作集。抽象-与存放数据的机器无关;-与数据存储的物理结构无关;-与实现操作的算法和编程语言均无关。

  4. 陈越《数据结构》第二章 线性结构

    表中元素个数称为线性表的长度;线性表没有元素时,称为空表;表起始位置称表头,表结束位置称表尾。插入和删除操作只能在链栈的栈顶进行。

  5. 【数据结构】

    非线性结构:线性结构的元素之间具有线性关系,非线性结构中的元素之间不再是序列的关系,他们呈现的是更复杂的层次关系,即一个数据元素有且仅有一个直接前驱,但可有另个或者多个直接后继,显然比序列关系复杂常见非线性结构:树,图散列表PHP中的hashtable就是哈希表就是由数组和链表组成,一个长度为16的数组中,每个元素存储的是一个链表的头结点。

  6. 【数据结构】【C++STL】FIFO队列&amp;优先队列

    首先都需要打头文件queueFIFO队列是先进先出的就好像排队一样STL定义FIFO队列优先队列的话是有优先级存在的STL定义优先队列定义方式都是大根堆FIFO队列和优先队列都有一些操作COYG

  7. 【数据结构】 堆

    自底向上://增加/减少已有节点值Heap_Increase_Key//向堆插入新的节点HeapInsert自顶向下://替换堆顶后,维持堆函数KeepHeap//弹出堆顶函数Pop

  8. 【数据结构】链表

    线性表的顺序存储结构有存储密度高及能够随机存取等优点,但存在以下不足:线性表的链式存储(单链表)的实现单向循环链表的实现

  9. 伸展树(SPLAY)个人总结+模板 [平衡树]【数据结构】【模板】

    前言最近3个月内,无论是现场赛还线上赛中SPLAY出现的概率大的惊人啊啊啊!!!然而不会的我就GG了,同时发现大家都会SPLAY,,,,然后就学习了一波。——————————————————————————-附上整体代码-md贴上来太卡了,去题解里看吧维护序列的维护一堆数的

  10. BZOJ 1895 &amp; POJ 3580 supermemo [SPLAY]【数据结构】

    Ay}Ttimes.Forexample,performing“REVOLVE242”on{1,5}INSERTxP:insertPafterAx.Forexample,performing“INSERT24”on{1,5}DELETEx:deleteAx.Forexample,performing“DELETE2”on{1,5}MINxy:querytheparticipantwhatistheminimumnumberinsub-sequence{Ax…Ay}.Forexample,thecorrec

返回
顶部