您的位置:首页 > 其它

算法-图论_关键节点的判断

2014-04-04 12:30 155 查看

无向图的关节点

概述:

在网络中关节点的判断将成为影响网络连通性的主要因素。节点之间通过关键点传递信息,如在我们以太网中的网关。当网关节点失效,那么两个网络之间的节点就不能够进行通信。在无线传感器网络中,会造成不能及时监控部分区域的信息。这次程序主要实现一个简单的判断关键节点方法,当然关键节点不能单靠两个节点之间的连通与否简单判断。

算法思想:

符合关键节点的条件为:后继结点不能访问该节点的父节点。也就是说只能通过该节点才能实现网络节点间的连通性。

如何的判断后继节点不能访问父节点?父节点如何的定义?我们很熟悉的就是利用DFS能够判断网络的连通性。因此我们依旧采用这种方式。假设一个图为如下所示:



那么它的深度优先遍历将会如下图所示:



图中的实线为深度优先遍历产生的树,虚线为节点的后向边。标注的后向边为节点能访问到最前的后向边。

由此可以利用一个计数来表示访问当前节点顺序。由此在访问子节点时其访问顺序必定会大于树中的父节点。如此保证了每个节点的唯一的一个编号,就如ID一样。再加一个表示后向边指向的在树中最高的节点Pred。一旦这个pred的值大于它的父节点,那么就说明它可以绕过父节点访问树中上面的节点。在图中,如f节点,尽管是从c节点遍历访问的,但是因为有指向a的后向边,a的计数小于c的计数(num),因此我们把f的pred指向a。

如何在代码中判断c是否为关节点。因为其子节点的pred<c的计数,所以我们认为c不是关键节点。那么节点对应的数据结构如下:

//判断连通性的节点定义
struct ConnectNode
{
int key;//键值
int num;//
int pred;//前向边的索引
bool bKeyNode;
};
//解释:在识别连通性时,将连接节点的边分为前向边与后向边


其中bKeyNode是为了判断该节点是否为关节点,防止在第一副图中b判断一次d节点,e又判断一个d节点,结果输出两次的情况。

但是还要注意的一个小细节是:当从a开始访问,c节点的后向边为a,肯定的是c.pred值将为a.num。因此我们需要判断节点是否为根节点且其孩子的个数。由此下面为联通图判断关键节点的类。

//连通性的图
class ConnectGraph
{
public:
int nodeNum;//节点个数

int **Matrix;//利用邻接矩阵存储数据
ConnectNode* nodes;//节点
int *keynodes;//存放关键节点
int resultPos;
int count;
bool **visitedMatrix;
int headChild;
}

其中count用以为每个节点的num赋值,保证唯一性。而visitedMatrix为了防止c访问a,a访问c这种不断的循环。将对应的边访问置为true进行避免。

程序的代码如下:

void blockSearch()//将网络按照关键节点分为若干块
{
//主要思想:对每个节点存放前向边
connectGraph(&nodes[0],&nodes[0]);//循环是避免
cout<<endl<<"关节点为:";
for (int i=0;i<resultPos;i++)
{
cout<<"  "<<keynodes[i];
}
cout<<endl<<totalCount;
}
void connectGraph(ConnectNode* node,ConnectNode* headNode)//判断是否连通
{
node->num=++count;
node->pred=node->num;
for (int i=0;i<nodeNum;i++)
{
int nodeKey=node->key;
if(visitedMatrix[nodeKey][i]==false&&Matrix[nodeKey][i]==1)
{
if (nodes[i].num==0)
{
visitedMatrix[nodeKey][i]=visitedMatrix[i][nodeKey]=true;
if (node==headNode)
{
headChild++;
}
connectGraph(&nodes[i],headNode);
if (nodes[i].pred>=node->num&&!node->bKeyNode)
{
if (node==headNode&&headChild<2)
{
continue;//防止出现从a开始但是子节点的pred不能大于头节点判断头结点为关节点
}
else
{
node->bKeyNode=true;//设置节点的关键节点为true
keynodes[resultPos++]=node->key;
}
}
else
{
if (node->pred>nodes[i].pred)//将子节点更高的前驱赋予此节点
{
node->pred=nodes[i].pred;
}
}

}//有一种情况是当节点不能前向到父节点之前的边,但是其它的由父节点引出的边可以到达
else
{   //防止节点的前向边指向父节点,进而产生多次的赋值
if (node->pred>nodes[i].num)//将较小的值赋予
{
node->pred=nodes[i].num;//为什么这后面又是num了呢?
}
}
}
}
}


相信上面的代码很简单,首先要判断当前节点是否已经被访问过了,没有则必须进行DFS算法。当该子节点都访问了之后判断pred值。

if (node->pred>nodes[i].pred)//将子节点更高的前驱赋予此节点
{
node->pred=nodes[i].pred;
}


当其pred的值更小,那么将子节点的pred赋予该节点,表示父节点能够通过子节点访问到更高的节点,这里不用担心会不会跨过关键点。因为所遍历的是子节点的。

如果子节点已经被访问过了,我们看到这条语句是。

if (node->pred>nodes[i].num)//将较小的值赋予
{
node->pred=nodes[i].num;//为什么这后面又是num了呢?
}

仔细想想注释中的疑问。因为如果当前节点为关键节点的子节点,那么它的一个邻居节点很可能就是关键节点,而关键节点的前驱很可能就是另一个连通块中的点的num。如果进行像上一条一样的复制,就等于说关键节点的子节点能够绕过关键节点,访问另一个图。

程序的运行结果为:



小结:

1) 通过前面的图的连通性或者是树的遍历,我发现以一个全局变量进行描述,比如该程序中的num,以及在检测环时的int* num数组都是这样的一个辅助效果。

2) 算法要深入的分析,就如何的判断根节点是否为关键点,我想了很多钟方案,包括1、是不是已经被访问过,其上一次被访问是否为根节点,等等。其实一个技术headChild就可以解决。减少计算的复杂度。

3) 本算法的时间复杂度还是比较高的。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐