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

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

题注

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

图的基本操作,包括: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;
       }
}

作者:铭毅天下
来源:CSDN
原文:https://blog.csdn.net/laoyang360/article/details/7897685
版权声明:本文为博主原创文章,转载请附上博文链接!

相关文章
|
1月前
|
Java
【Java集合类面试二十六】、介绍一下ArrayList的数据结构?
ArrayList是基于可动态扩展的数组实现的,支持快速随机访问,但在插入和删除操作时可能需要数组复制而性能较差。
|
3月前
|
存储 算法
数据结构===图
数据结构===图
|
2月前
|
算法 Python
逆袭之路!用 Python 玩转图的 DFS 与 BFS,让数据结构难题无处遁形
【7月更文挑战第12天】图的遍历利器:DFS 和 BFS。Python 中,图可表示为邻接表或矩阵。DFS 沿路径深入,回溯时遍历所有可达顶点,适合找路径和环。BFS 层次遍历,先近后远,解决最短路径问题。两者在迷宫、网络路由等场景各显神通。通过练习,掌握这些算法,图处理将游刃有余。
50 3
|
2月前
|
存储 算法 Python
“解锁Python高级数据结构新姿势:图的表示与遍历,让你的算法思维跃升新高度
【7月更文挑战第13天】Python中的图数据结构用于表示复杂关系,通过节点和边连接。常见的表示方法是邻接矩阵(适合稠密图)和邻接表(适合稀疏图)。图遍历包括DFS(深度优先搜索)和BFS(广度优先搜索):DFS深入探索分支,BFS逐层访问邻居。掌握这些技巧对优化算法和解决实际问题至关重要。**
27 1
|
3月前
|
存储 测试技术
【数据结构】操作受限的线性表,栈的具体实现
【数据结构】操作受限的线性表,栈的具体实现
49 5
|
3月前
|
存储 测试技术
【数据结构】操作受限的线性表,队列的具体实现
【数据结构】操作受限的线性表,队列的具体实现
37 4
|
3月前
|
存储 JavaScript 前端开发
JavaScript中的数组是核心数据结构,用于存储和操作序列数据
【6月更文挑战第22天】JavaScript中的数组是核心数据结构,用于存储和操作序列数据。创建数组可以使用字面量`[]`或`new Array()`。访问元素通过索引,如`myArray[0]`,修改同样如此。常见方法包括:`push()`添加元素至末尾,`pop()`移除末尾元素,`shift()`移除首元素,`unshift()`添加到开头,`join()`连接为字符串,`slice()`提取子数组,`splice()`进行删除、替换,`indexOf()`查找元素位置,`sort()`排序数组。还有其他如`reverse()`、`concat()`等方法。
126 2
|
3月前
|
NoSQL Java Redis
如何在 Java 中操作这些 Redis 数据结构的基本方法
如何在 Java 中操作这些 Redis 数据结构的基本方法
33 2
|
3月前
数据结构篇:旋转操作在AVL树中的实现过程
数据结构篇:旋转操作在AVL树中的实现过程
25 0
|
3月前
|
Java Android开发 Kotlin
Android面试题:App性能优化之Java和Kotlin常见的数据结构
Java数据结构摘要:ArrayList基于数组,适合查找和修改;LinkedList适合插入删除;HashMap1.8后用数组+链表/红黑树,初始化时预估容量可避免扩容。SparseArray优化查找,ArrayMap减少冲突。 Kotlin优化摘要:Kotlin的List用`listOf/mutableListOf`,Map用`mapOf/mutableMapOf`,支持操作符重载和扩展函数。序列提供懒加载,解构用于遍历Map,扩展函数默认参数增强灵活性。
42 0