函数的强连通性判定(C++实现)
这是最近做的一个C++课程设计,之前在做这个题目过程中,也上网查找了很多资料,只查到图的连通性,以及中缀表达式转换为后缀表达式的资料,发现并没有接收键盘输入的函数式识别与其结果的强连通性判定的资料,也就是没有将两者结合的资料。所以将这次所做的分享到博客中,供有需要的伙伴参考。
实现代码下载链接:
一、题目要求:
1.现给你一个函数式y=f(x)和N(N在1到8间取值),对于所有可能x(x在0到2N-1间取值)输出对应的结果y(y在0到2N-1间取值)。
输入只有一行,给你一个函数式,式子中只含有字母,数字,‘+’、‘-’、‘*’、‘/’、‘(’、‘)’,表达式长度不超过 100,式子中的数均不为负且为0到2N-1间的int型整数,保证数据合法,输入的值以及结果都为0到2N-1间的int型整数。
‘+’表示两正整数相加,结果再模2N
‘-’表示两正整数相减的绝对值,结果再模2N
‘*’表示两正整数相乘,结果再模2N
‘/’表示两正整数相除,结果再模2N
‘(’、‘)’括号里面的运算优先级更高
2.根据1.的结果判断其输入输出构成的有向图的强连通性(不需画出有向图,只需判断强连通性,)。
输入输出构成的有向图有2N个顶点,如果有b=f(a)表明从顶点a到顶点b有有向路径。
强连通(Strongly Connected)是指一个有向图(Directed Graph)中任意两点v1、v2间存在v1到v2的路径(path)及v2到v1的路径。
在有向图中, 如果任何两顶点之间均有有向路径, 则称该有向图是强连通图。
现有的判定方法有从某个结点出发后借助对有向图的正向和逆向深度优先搜索来进行的,有通过求其可达矩阵来判定,有将有向图中形成回路的结点进行收缩的方法来判断等。从降低时间复杂度和空间复杂度考虑,找出结点较多时能快速判断出一个有向图是否为强连通的方法,并编程实现。
示例:
输入N: 2
输入表达式:y=(x+1)*3
输出 x :0 1 2 3
y :3 2 1 0
输出 该函数的有向图不是强连通图
注释:由于N=2,x取值0到3 。
当x=0时,y=(((0+1)%4)*3) %4=3
当x=1时,y=(((1+1)%4)*3) %4=2
当x=2时,y=(((2+1)%4)*3) %4=1
当x=3时,y=(((3+1)%4)*3) %4=0
得到对应的有向图为
输入的是M维函数,输出相应结果
示例:
输入N: 2
输入M:2
输入表达式:w=(x+y+1)*3
v=(x +1)*3+y
输出 :(x,y) :(0,0) (0,1) (0,2) (0,3) (1,0) (1,1) (1,2) (1,3) (2,0) (2,1) (2,2) (2,3) (3,0) (3,1) (3,2) (3,3)
(w,v) : (3,3) (2,0) (1,1) (0,2) (2,2) (1,3) (0,0) (3,1) (1,1) (0,2) (3,3) (2,0) (0,0) (3,1) (2,2) (1,3)
输出:该函数的有向图不是强连通图
二、设计分析
本设计是函数的强连通性判定系统。本系统主要是采用中缀表达式转换成后缀表达式算法与有向图的强连通性的判定算法来实现系统的功能。系统的功能主要是对用户的输入N值与任意函数式的计算函数值与对函数值构成的有向图判断强连通性,N值是用来构建定义域与表达式求模使用。本文不但实现了本系统对一维函数的计算与强连通图的判定,还实现了M维函数的求解与组成的有向图的强连通性判定。同时用户界面添加了简单的人机交互。系统的设计是采用模块化设计,将各大功能分成大模块,大模块里再成各个小模块,再一个一个小模块的函数实现功能,之后组合起来实现所需功能。测试与数据分析的实验结果完全符合设计需求,功能实现也完全达到要求。
系统可以分为三大模块:函数式解析与计算,强连通图的判定以及最后的页面显示部分。
函数式解析与计算使用的相关算法是使用中缀表达式转换为后缀表达式计算结果。算法应用到栈的数据结构。首先将接收的函数式字符串处理出等号右边含有未知数的表达式。然后利用循环体将定义域中每个值替换含有未知数的表达式中未知数,替换后的表达式只含有数字与运算符,这时的表达式叫做中缀表达式。再通过中缀转换后缀算法将其转换为后缀表达式,最后进行计算值。
强连通图的判定使用到图论的知识,将函数定义域作为顶点,定义域与值域的映射关系作为顶点之间边的信息,使用邻接矩阵来表示有向图。利用定义域与值域的映射关系构建邻接矩阵,再利用深度优先搜索算法(DFS)对邻接矩阵进行正反向深度搜索,根据搜索的顶点数比较来判定强连通。程序将判定结果返回主程序,再由显示程序进行显示到页面上。
最后的页面显示部分使用输入输出流,实现简单的人机交互功能,使整个系统看起来简单易懂。首先创建变量从输入流来接收用户输入的N、M以及函数式。将这些变量传入其他模块去处理,再将处理后的结果显示到页面中。显示部分的设计有printX()打印函数定义域的函数,printResult()结果集的函数等等。
三、相关算法
3.1中缀表达式转换为后缀表达式
假如中缀表达式为:12*(3+4)-6+8/2
在遇到数字时,我们直接输出,遇到符号,则入栈。但在入栈时,我们要判断栈内已有的操作符的优先级和需要判断的操作符的优先级的大小。
栈内<栈外:入栈操作符
栈内>栈外:出栈操作符
栈内=栈外:出栈 栈内的操作符并入栈 栈外的操作,
遇到 ‘(’:直接入栈
遇到 ‘)’:出栈所有操作符直达遇到 ‘(’
我们先初始化一个空的字符串(String)和栈(Stack),将这个中缀表达式中的数字和运算符(包括加减乘除及左右括号)分割成一个个字符串存进一个新的数组中,运算对象就直接放进刚才初始化的String中,栈中存放的是运算符。如果是空栈,加减乘除或左括号就直接进栈,如果栈不为空,此时扫描到的运算符与栈顶运算符做优先级比较,如果栈顶运算符优先级低于扫描的运算符,则当前扫描运算符入栈,否则栈顶运算符弹栈并连接在String后,并与弹栈后的站定运算符再做比较,直到站定运算符低于当前扫描的运算符或遇到左括号,并将当前运算符进栈;当前运算符若是右括号,则将栈顶运算符依次弹出并依次连接到String后,直到遇到左括号,并将左括号弹出(但是不连接在String后的,注意后缀表达式中是没有括号的)。如果中缀表达式遍历完毕后栈中还有运算符则将栈中剩下的运算符依次弹出并连接在String后面;最终得到的就是字符串String后缀表达式。
运算符的优先级:
0–>’(‘;
1–>’+’;
1–>’-‘;
2–>’*’;
2–>’/’;
3–>’)’;
后缀表达式的计算
先初始化一个栈,这个栈是用来存放运算对象的,然后定义一个变量存放最后的结果。遍历到数字则依次进栈,遍历到运算符的时候将栈顶运算符弹出(假设赋给变量a),再将此时的栈顶运算符弹出(假设赋给变量b),用后者对前者做该运算符对应的运算(假设遍历当前的运算符为+,则做计算b+a),然后将计算结果入栈。以此方式遍历整个字符串,最终的结果即为运算结果。
3.2有向图的强连通判定算法
强连通判定算法描述 :
假设 给定 图 G , 其反 向图为 G‘ , 顶点个数 为 n。
(1 ) 在 图 C 中从任意一个顶点V出发 ,进行深度优先搜索(DFS) ,搜索到M个顶点 。
(2 ) 在 图 G 中从同一顶点V出发 , 进行深度优先搜索(DF S) ,搜索 到 N个顶点。
(3 ) 若 M = N = n, 则图 G 是强连通的,否则 ,不是强连通的。
如 下图中的有向 图 G,从任意一个顶点 (不妨设为 Vo) 分别 在图 G 及其反
向 图中进行搜索 的结果如表 1 所示,由此可知 ,它不是强连通 的 。
在实现该算法时 ,可采用邻接矩阵的方式进行存储图G 。由于其反向图的邻接矩阵是 图 G 的邻接矩阵的转置,不再专门存储 ,反向图的信息从图 G 的邻接矩阵中得到。
四、实现结果
一维示例:
M维示例: