图的邻接矩阵实现

用邻接矩阵存放图中顶点的关系,实现无向图的邻接矩阵存储。

1)图的建立,删除(添加,删除边/顶点)
2)广度和深度优先遍历
3)prim最小生成树


1,成员变量,构造函数,以及数组扩展

实现策略:维护一个顶点的数组,以及一个二维的数组来表示顶点之间的关系,维护2个基本变量记录顶点和边的数量。

重点是:1)可以动态扩展顶点数组,并保持数组的连续性,这意味着删除顶点时后面的顶点要前移,那么顶点的编号也变了,关系矩阵也要改变。    2)关系矩阵也动态维护,随时保持和顶点数组一样大。顶点数组的长度为VNodes.length,实际存放了顶点的位置只到了size()处,对应的,关系矩阵的大小为int[VNodes.length][VNodes.length],实际有效地区域也只在左上角的int[size()][size()]范围内

复制代码
  
  
/*总是将关系矩阵保持和顶点数组大小对应,顶点数组不一定放满,关系矩阵也只
* 在左上角放满,顶点数组放满的大小为size(),关系矩阵也只到size()
*/
private VNode[] VNodes;
private int[][] M;

private int nodeCount;
private int edgeCount;

public MatUnDirectedGraph()
{
VNodes
= new VNode[5];
M
= new int[5][5];
nodeCount
= 0;
edgeCount
= 0;
}

public void expand()
{
VNode[] larger
= new VNode[VNodes.length * 2];//顶点数组扩大
int[][] M_larger = new int[larger.length][larger.length];//关系矩阵也要扩展
for(int i = 0;i < VNodes.length;i++)
{
larger[i]
= VNodes[i];
for(int j = 0;j < VNodes.length;j++)
M_larger[i][j]
= M[i][j];
}
VNodes
= larger;
M
= M_larger;
}
复制代码




2,建图,删图相关方法分析


用邻接矩阵存储表示顶点之间的关系果然比邻接表在代码实现上简单很多

1)添加边,只需要在关系矩阵M的两个位置上赋值即可,而在邻接表实现中,要在2个顶点的边链表的最后都添加上一个边

2)删除边,1)的逆过程,将那2个位置的值置为0即可,而在邻接表的实现中,也是要到边链表中去删(还要记录被删边得前缀)

3)添加顶点,直接在顶点数组中添加一个,注意如果满的话,要扩展,在扩展方法中,已经实现了同时扩展关系矩阵(将矩阵变大了,左上角有效区域还是不变)。在邻接表中这个稍微简单点,因为邻接表直接扩展数组即可,关系不用动。

4)删除顶点,无论哪种方式,删除顶点都是最麻烦的,在邻接表的实现中,删除顶点要先删除所有跟顶点关联的边,然后后面的顶点前移以保证顶点数组连续,移动会导致原来的顶点的边链表的信息变化(边的端点),所以在移动前我们先将所有边链表中的边得信息调整好,再来移动数组使它保持连续。

在邻接矩阵中,删除了一个顶点也要将顶点数组前移来保持顶点数组连续,结果会导致关系矩阵也要变(假设删除的顶点下标是position,那么左上角M[position][position]大小范围内的矩阵不需要变,从这个边界到M[size][size]范围的矩阵值要向内紧缩---也是因为顶点移动导致顶点下标变了,具体怎么去向内收缩,见代码及注释)


添加和删除边:

复制代码
  
  
public void addEdge(int start,int end,int len){ //在两个指定下标的节点之间添加一条边
M[start][end] = len;
M[end][start]
= len;
edgeCount
++;
}

//在图中2个顶点之间删除一条边

public void removeEdge(int start,int end){ //删除两个指定下标顶点之间的边
M[start][end] = 0;
M[end][start]
= 0;
edgeCount
--;
}
复制代码


添加顶点:

复制代码
  
  
public void addVNode(Object element){ //添加顶点
VNode node = new VNode(element);
if(size() == VNodes.length)
expand();
//扩展方法时同时扩展了关系矩阵
VNodes[nodeCount++] = node;
}
复制代码


上面三个方法都很简单,最复杂的仍然是删除顶点:弄清楚矩阵是怎么向左上角紧缩的

复制代码
  
  
public Object removeVNode(int position){ //删除顶点
VNode result = VNodes[position];

//先调整关系矩阵
for(int i = 0;i < size();i++)
for(int j = 0;j < size();j++) //将关系矩阵向内紧缩(顶点将要移动)
{
if(i > position && j > position)
M[i
-1][j-1] = M[i][j];
else if(i > position)
M[i
-1][j] = M[i][j];
else if(j > position)
M[i][j
-1] = M[i][j];
}
for(int i = 0;i < size();i++) //紧缩以后,最后一个顶点已经没有意义了(移到倒数第二个了)
{
M[size()
-1][i] = 0;
M[i][size()
-1] = 0;
}

//再调整顶点数组
for(int i = position;i < size()-1;i++)//保证数组连续性
VNodes[i] = VNodes[i+1];
VNodes[size()
-1] = null;
nodeCount
--;

return result;
}
复制代码





3,广度优先遍历和深度优先遍历

拿广度优先遍历来说,顶点A每次从遍历队列出来的时候要将其未被访问的关联顶点添加到遍历队列,这就需要求得A在顶点数组里的位置,然后在关系矩阵里找跟它关联且没有被访问的顶点。

深度优先一样。

仅贴出广度优先,深度优先的实现只需要把遍历队列改成遍历栈即可:

复制代码
  
  
//广度优先遍历图

public Iterator GraphBFS(int position){ //可以遍历多个联通分量的BFS
LinkedQueue queue = new LinkedQueue();
BFSorder(position,queue);
for(int i = 0;i < size();i++) //注意是size,不是VNodes.length,下面的BFSorder也是
if(VNodes[i].getVisited() == false)
BFSorder(i,queue);
return queue.iterator();
}

public Iterator SingleBFS(int position){ //只遍历position所在连通域的BFS
LinkedQueue queue = new LinkedQueue();
BFSorder(position,queue);
return queue.iterator();
}

private void BFSorder(int position,LinkedQueue queue){//按照广度规则从position开始将position所在连通分量顶点进队
LinkedQueue tempqueue = new LinkedQueue();
tempqueue.enqueue(VNodes[position]);
VNodes[position].setVisited(
true);

while(!tempqueue.isEmpty())
{
VNode node
= (VNode) tempqueue.dequeue();
queue.enqueue(node);
int index = 0;
for(int i = 0;i < size();i++)
if(VNodes[i] == node)
index
= i;//node在数组里的下标

for(int i = 0;i < size();i++)
if(M[index][i] != 0 && VNodes[i].getVisited() == false)
{
tempqueue.enqueue(VNodes[i]);
VNodes[i].setVisited(
true);
}
}
}
复制代码





4,prim最小生成树


用邻接表写过之后,prim的思想也就很熟悉了,用邻接矩阵写也比较顺利,很容易就写成功了。这里可以直接在关系矩阵M里去找最小值,简单一点,不像邻接表实现需要那么多的支持方法。

直接贴出代码:

复制代码
  
  
1 //prim最小生成树
2 public Edge[] getEdges(int position){
3 Edge[] Edges = new Edge[size()-1];
4 VNodes[position].setVisited(true);
5 for(int i = 0;i < Edges.length;i++)
6 Edges[i] = getMinEdge(VNodes);
7 return Edges;
8 }
9
10 private Edge getMinEdge(VNode[] VNodes){ //从当前分割的两个顶点集合之间找最小边
11
12 //直接在关系矩阵中找就可以了(逻辑简单点,也可以从顶点数组去循环查找)
13 Edge min = null;
14 for(int i = 0;i < size();i++)
15 for(int j = 0;j < size();j++)
16 if(VNodes[i].getVisited() && !VNodes[j].getVisited() && M[i][j] != 0)
17 {
18 if(min == null)
19 min = new Edge(i,j,M[i][j]);
20 else
21 if(M[i][j] < min.getLen())
22 min = new Edge(i,j,M[i][j]);
23 //VNodes[j].setVisited(true);
24 }
25 VNodes[min.getEnd()].setVisited(true);
26 return min;
27 }
28
复制代码




5,完整清单及测试


MatUnDirectedGraph


只贴出最小生成树的结果:

仍然构造上节中的最小生成树---

关系矩阵为: 
0 6 1 5 0 0 
6 0 5 0 5 0 
1 5 0 5 6 4 
5 0 5 0 0 2 
0 5 6 0 0 6 
0 0 4 2 6 0 

输出最小生成树的边: 

边: V1---V3   长度:1
边: V3---V6   长度:4
边: V6---V4   长度:2
边: V3---V2   长度:5
边: V2---V5   长度:5
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
下面是基于邻接矩阵实现的各种基本操作的完整代码,包括创建、插入边、删除边、检查边、打印等。 ```c++ #include <iostream> #include <vector> using namespace std; // 定义邻接矩阵表示的类 class Graph { private: int V; // 的顶点数 vector<vector<int>> adj; // 邻接矩阵,用于存储边的信息 public: // 构造函数 Graph(int v) { V = v; adj.resize(V, vector<int>(V, 0)); } // 添加一条边 void addEdge(int u, int v) { adj[u][v] = 1; adj[v][u] = 1; } // 删除一条边 void removeEdge(int u, int v) { adj[u][v] = 0; adj[v][u] = 0; } // 检查是否存在一条边 bool hasEdge(int u, int v) { return adj[u][v] == 1; } // 打印 void printGraph() { for (int i = 0; i < V; i++) { cout << "Vertex " << i << " is connected to: "; for (int j = 0; j < V; j++) { if (adj[i][j] == 1) { cout << j << " "; } } cout << endl; } } }; int main() { // 创建一个有5个顶点的 Graph g(5); // 添加一些边 g.addEdge(0, 1); g.addEdge(0, 4); g.addEdge(1, 2); g.addEdge(1, 3); g.addEdge(1, 4); g.addEdge(2, 3); g.addEdge(3, 4); // 检查某些边是否存在 cout << "Does edge (1, 4) exist? " << g.hasEdge(1, 4) << endl; cout << "Does edge (2, 4) exist? " << g.hasEdge(2, 4) << endl; // 删除一些边 g.removeEdge(1, 4); g.removeEdge(3, 4); // 打印 g.printGraph(); return 0; } ``` 运行上述代码,输出结果如下: ``` Does edge (1, 4) exist? 1 Does edge (2, 4) exist? 0 Vertex 0 is connected to: 1 4 Vertex 1 is connected to: 0 2 3 Vertex 2 is connected to: 1 3 Vertex 3 is connected to: 1 2 Vertex 4 is connected to: 0 ``` 可以看到,该代码实现了基本的操作,并且能够正确地检查边、添加边、删除边和打印

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值