数据结构面试之七——图的常见操作

简介: 图的基本操作,包括:1.创建一个图,2.判断图是否为空,3.图的打印,4.图的遍历…..其中对于1,创建一个图,需要考虑图的存储结构,存储结构分为:邻接矩阵存储(数组),邻接表存储(数组链表)。而对于四,也是图的核心操作,主要分为:图的深度优先遍历(逐个结点递归),图的广度优先遍历(类似层次遍历)。

题注:《面试宝典》有相关习题,但思路相对不清晰,排版有错误,作者对此参考相关书籍和自己观点进行了重写,供大家参考。


七、图的常见操作


      图的基本操作,包括:1.创建一个图,2.判断图是否为空,3.图的打印,4.图的遍历…..


其中对于1,创建一个图,需要考虑图的存储结构,存储结构分为:邻接矩阵存储(数组),邻接表存储(数组链表)。而对于四,也是图的核心操作,主要分为:图的深度优先遍历(逐个结点递归),图的广度优先遍历(类似层次遍历)。


      此外,图的扩展操作:求最小生成树(Prim算法,kruskal算法),求最短路径的(Dijstra算法,kruskal算法)等下一节会详细介绍。


//下面实例中图采用邻接表的存储结构.


template<class vType, intsize>

class graphType : publiclinkedListGraph<vType>

{

public:

      graphType();

      ~graphType();

      bool isEmpty();

      void createGraph();

      void clearGraph();

      void printGraph() const;

      void depthFirstTraversal(); //深度优先遍历

      void dft(vType v, bool *visited);  //深度优先递归函数    

      void breadthFirstTraversal(); //广度优先遍历

protected:

      int maxSize;                //最大结点数

      int gSize;                  //当前结点数[输入后便知道]

      linkedListGraph<vType>* graph; //链表图结构的指针

};

template<class vType, intsize>

graphType<vType,size>::graphType()

{

      maxSize = size;

      gSize = 0;

      graph = new linkedListGraph<vType>[maxSize]; //构造结点数组链表...

}

template<class vType, intsize>

graphType<vType,size>::~graphType()

{

      clearGraph(); //调用销毁操作

      delete[] graph;

}


1.图判断空


template<class vType, intsize>

boolgraphType<vType,size>::isEmpty()

{

      return (gSize == 0); //根据当前节点数是否为0判断是否空

}


2.创建图


//第一行代表图中结点个数;


//第二行代表对于每一个顶点的邻接点;


template<class vType, intsize>

voidgraphType<vType,size>::createGraph()

{

      cout << "Input the nums of Vertex: ";

      cin >> gSize;

      cout << endl;

      vType adjacentVertex;

      cout << "Input the adjacent Vertex of every Vertex:(-999 End)" << endl;

      for( int index=0; index < gSize; ++index)

      {

             cout << "Input line " << index<< ": ";

             while(cin >> adjacentVertex, adjacentVertex !=-999) //-999作为结束符

             {

                    graph[index].insertLast(adjacentVertex);

             }//end while

      }//end for

}


3. 销毁操作,逐个节点调用对应的链表。


template<class vType, intsize>

voidgraphType<vType,size>::clearGraph()

{

      int index;

      for(index = 0; index < gSize; index++)

      {

             graph[index].destroyList();           //销毁链表...

      }

      gSize = 0;

}

4.打印图


template<class vType, intsize>

voidgraphType<vType,size>::printGraph() const

{

      cout << "The Graph is shown as below: "<< endl;

      int index;

      for(index = 0; index < gSize; index++)

      {

             cout << index << "\t";

             graph[index].print();           //打印每组链表

      }

      cout << endl;

}

5.图的深度优先遍历


     区别于二叉树的特点,图中即可能存在环路,又可能无法从一个结点遍历整个图。


     核心思路:1.从图中一个结点(自定义)开始访问,如果尚未访问,则访问它;2.然后对于邻接结点,用1同样的方法进行遍历。直到所有的结点都被遍历到。考虑:可以用递归实现。


     以下是深度优先递归函数dft &深度优先遍历函数depthFirstTraversal的实现。


template<class vType, intsize>

void  graphType<vType,size>::dft(vType v,bool *visited)

{

      visited[v] = true;

      cout << v << "\t";

      vType *adjacencyList = new vType[gSize]; //用于存储邻接点

      int nLength = 0; //out函数里会有值

      //找v结点的邻接点.

      graph[v].getAdjacentVertices(adjacencyList,nLength);

      //判断邻接点是否已经遍历

      for(int i = 0; i < nLength; i++)

      {

             if(!visited[adjacencyList[i]])

             {

                    dft(adjacencyList[i],visited);        //对邻接点进行递归操作

             }

      }

}

template<class vType, intsize>

void graphType<vType,size>::depthFirstTraversal()

{

      cout << "DepthFirstTraversal result is as below:";

      bool *visited;

      visited = new bool[gSize]; //定义结点访问标记数组,true已访问,false未访问。

   

      //初始化标记数组

      for(int index = 0; index < gSize; index++)

      {

             visited[index] = false;

      }

   

      for(int index = 0; index < gSize; index++)

      {

             if(!visited[index])

             {

                    dft(index,visited);

             }

      }

      cout << endl;

      delete []visited;

}


6. 图的广度优先遍历


       核心思想:对于所有的结点,对每一个结点及其该结点的所有邻接结点遍历完以后;再遍历下一个尚未遍历的结点。


       其遍历类似二叉树的层次遍历。由于对于每一个结点都要在遍历完一个结点后,再去遍历其所有的邻接节点。考虑用队列完成操作,先进先出。在进队的时候访问,如果队列非空,则完成出队,同时将其所有的邻接点依次进队。进队时访问,依次类推。


template<class vType, intsize>

voidgraphType<vType,size>::breadthFirstTraversal()

{

      cout << "BreathFirstTraversal result is as below:";

      bool *visited;

      visited = new bool[gSize]; //定义结点访问标记数组,true已访问,false未访问。

      vType *adjacencyList = new vType[gSize]; //用于存储邻接点

      int nLength = 0; //out函数里会有值

      linkedQueueType<vType> queue;            //用于结点入队操作.

      vType w;                                //弹出结点

   

      //初始化标记数组

      for(int index = 0; index < gSize; index++)

      {

             visited[index] = false;

      }

      for(int index = 0; index < gSize; index++)

      {

             if(!visited[index])

             {

                    queue.addQueue(index);

                    visited[index] = true;

                    cout << index << "\t";  //访问

                    //找v结点的邻接点.

                    while(!queue.isEmptyQueue())

                    {

                           queue.dequeue(w);

                           graph[w].getAdjacentVertices(adjacencyList,nLength);

                           //判断邻接点是否已经遍历

                           for(int i = 0; i < nLength; i++)

                           {

                                  if(!visited[adjacencyList[i]])

                                  {

                                         queue.addQueue(adjacencyList[i]); //!此处注意,易落下。

                                         visited[adjacencyList[i]] =true;

                                         cout <<adjacencyList[i] << "\t";

                                  }//end if

                           }//end for

                    }//end while  

             }

      }

      cout << endl;

      delete []visited;

}

#endif


7. 获取邻接结点,并将结点放入数组中。


//length记录数组长度。LinkedListGraph 派生自 LinkedList(链表部分已讲)。


template<class vType>

voidlinkedListGraph<vType>::getAdjacentVertices(vType adjacencyList[],int& length)

{

      nodeType<vType> *current;

      length = 0;

      current = first;

      while(current != NULL)

      {

             adjacencyList[length++] = current->info; //将链表的元素存入数组.

             current = current->link;

      }

}


相关文章
|
3月前
|
存储 安全 Java
Java 集合面试题从数据结构到 HashMap 源码剖析详解及长尾考点梳理
本文深入解析Java集合框架,涵盖基础概念、常见集合类型及HashMap的底层数据结构与源码实现。从Collection、Map到Iterator接口,逐一剖析其特性与应用场景。重点解读HashMap在JDK1.7与1.8中的数据结构演变,包括数组+链表+红黑树优化,以及put方法和扩容机制的实现细节。结合订单管理与用户权限管理等实际案例,展示集合框架的应用价值,助你全面掌握相关知识,轻松应对面试与开发需求。
185 3
【Java集合类面试二十六】、介绍一下ArrayList的数据结构?
ArrayList是基于可动态扩展的数组实现的,支持快速随机访问,但在插入和删除操作时可能需要数组复制而性能较差。
|
11月前
|
算法 前端开发 Java
数据结构与算法学习四:单链表面试题,新浪、腾讯【有难度】、百度面试题
这篇文章总结了单链表的常见面试题,并提供了详细的问题分析、思路分析以及Java代码实现,包括求单链表中有效节点的个数、查找单链表中的倒数第k个节点、单链表的反转以及从尾到头打印单链表等题目。
120 1
数据结构与算法学习四:单链表面试题,新浪、腾讯【有难度】、百度面试题
|
10月前
|
存储 NoSQL Redis
Redis常见面试题:ZSet底层数据结构,SDS、压缩列表ZipList、跳表SkipList
String类型底层数据结构,List类型全面解析,ZSet底层数据结构;简单动态字符串SDS、压缩列表ZipList、哈希表、跳表SkipList、整数数组IntSet
|
算法 Python
逆袭之路!用 Python 玩转图的 DFS 与 BFS,让数据结构难题无处遁形
【7月更文挑战第12天】图的遍历利器:DFS 和 BFS。Python 中,图可表示为邻接表或矩阵。DFS 沿路径深入,回溯时遍历所有可达顶点,适合找路径和环。BFS 层次遍历,先近后远,解决最短路径问题。两者在迷宫、网络路由等场景各显神通。通过练习,掌握这些算法,图处理将游刃有余。
157 3
|
存储 算法 Python
“解锁Python高级数据结构新姿势:图的表示与遍历,让你的算法思维跃升新高度
【7月更文挑战第13天】Python中的图数据结构用于表示复杂关系,通过节点和边连接。常见的表示方法是邻接矩阵(适合稠密图)和邻接表(适合稀疏图)。图遍历包括DFS(深度优先搜索)和BFS(广度优先搜索):DFS深入探索分支,BFS逐层访问邻居。掌握这些技巧对优化算法和解决实际问题至关重要。**
146 1
|
存储 测试技术
【数据结构】操作受限的线性表,栈的具体实现
【数据结构】操作受限的线性表,栈的具体实现
140 5
|
存储 测试技术
【数据结构】操作受限的线性表,队列的具体实现
【数据结构】操作受限的线性表,队列的具体实现
101 4
数据结构篇:旋转操作在AVL树中的实现过程
数据结构篇:旋转操作在AVL树中的实现过程
147 0
|
Java Android开发 Kotlin
Android面试题:App性能优化之Java和Kotlin常见的数据结构
Java数据结构摘要:ArrayList基于数组,适合查找和修改;LinkedList适合插入删除;HashMap1.8后用数组+链表/红黑树,初始化时预估容量可避免扩容。SparseArray优化查找,ArrayMap减少冲突。 Kotlin优化摘要:Kotlin的List用`listOf/mutableListOf`,Map用`mapOf/mutableMapOf`,支持操作符重载和扩展函数。序列提供懒加载,解构用于遍历Map,扩展函数默认参数增强灵活性。
152 0

热门文章

最新文章