<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
	<channel>
		<atom:link href="http://gentoo-zh.org/extern.php?action=feed&amp;tid=610&amp;type=rss" rel="self" type="application/rss+xml" />
		<title><![CDATA[Gentoo-zh / 拓扑排序之变量序列]]></title>
		<link>http://www.gentoo-zh.org/viewtopic.php?id=610</link>
		<description><![CDATA[拓扑排序之变量序列 最近发表的帖子。]]></description>
		<lastBuildDate>Fri, 09 Dec 2022 08:58:43 +0000</lastBuildDate>
		<generator>FluxBB</generator>
		<item>
			<title><![CDATA[拓扑排序之变量序列]]></title>
			<link>http://www.gentoo-zh.org/viewtopic.php?pid=653#p653</link>
			<description><![CDATA[<p>拓扑排序之变量序列<br />巧若拙（欢迎转载，但请注明出处：http://blog.csdn.net/qiaoruozhuo）</p><p>题目描述：<br />假设有n个变量（1&lt;=n&lt;=26，变量名用单个小写字母表示），还有m个二元组（u,v），分别表示变量u小于v。那么，所有变量从小到大排列起来应该是什么样子的呢？<br />&#160; &#160; 例如有4个变量a,b,c,d，若以知a&lt;b,c&lt;b,d&lt;c，则这4个变量的排序可能是a&lt;d&lt;c&lt;b。尽管还有可能其他的可能，你只需找出其中的一个即可。<br />输入：<br />输入为一个字符串，其中包含N+N个字符，依次表示N个关系式（1&lt;=N&lt;=100000），例如序列&quot;abcbdc&quot;表示a&lt;b,c&lt;b,d&lt;c.<br />输出：<br />给出一个字符串，其中存储了一个符合要求的变量序列，例如，字符串&quot;adcb&quot;表示a&lt;d&lt;c&lt;b。<br />&#160; &#160;<br />算法分析：<br />&#160; &#160; 这是典型的拓扑排序问题。先简单科普一下，所谓拓扑排序，是指将一个有向无环图G中所有顶点排成一个线性序列，使得图中任意一对顶点u和v，若&lt;u，v&gt; ∈E(G)，则u在线性序列中出现在v之前。通常，这样的线性序列称为满足拓扑次序(TopoiSicai Order)的序列，简称拓扑序列。<br />&#160; &#160; 我们先将变量作为顶点输入到图数据结构中。表示图的数据结构有多种，由于本题对应的是稀疏图，应该以边为主要研究对象，所以可以把数据结构设置为邻接表或边表集。<br />&#160; &#160; 我们先来看邻接表数据结构：<br />typedef char VertexType; //顶点类型由用户自定义<br />typedef int EdgeType; //边上的权值类型由用户自定义</p><p>typedef struct EdgeNode{ //边表结点<br />&#160; &#160; int adjvex;&#160; //邻接点域，存储该顶点对应的下标<br />//&#160; &#160; EdgeType weight; //权值，对于非网图可以不需要<br />&#160; &#160; struct EdgeNode *next; //链域，指向下一个邻接点<br />} EdgeNode;</p><p>typedef struct VertexNode{ //顶点表结点<br />&#160; &#160; VertexType data; //顶点域，存储顶点信息<br />&#160; &#160; int in;&#160; &#160;//存储顶点入度的数量<br />&#160; &#160; EdgeNode *firstEdge; //边表头指针<br />} VertexNode;</p><p>由于变量名用单个小写字母表示，我们可以设置一个大小为26的数组存储顶点；又由于26个字母不见得都会出现，故我们设置一个全局变量int book[MAXM] = {0}; 用来标记某字母是否出现。<br />首先创建一个图，读入顶点和边信息。代码如下：<br />/*<br />函数名称：CreateGraph<br />函数功能：把顶点和边信息读入到表示图的邻接表中<br />输入变量：char *data：存储了N个关系式的字符串<br />&#160; &#160; &#160; &#160; &#160; VertexNode *GL ： 顶点表数组<br />输出变量：表示图的顶点表数组<br />返回值：int ：顶点数量<br />*/<br />int CreateGraph(char *data, VertexNode *GL)<br />{<br />&#160; &#160; int i, u, v;<br />&#160; &#160; int count = 0;//记录顶点数量<br />&#160; &#160; EdgeNode *e;<br />&#160; &#160;<br />&#160; &#160; for (i=0; i&lt;MAXM; i++)//初始化图<br />&#160; &#160; {<br />&#160; &#160; &#160; &#160; GL[i ].data = i + &#039;a&#039;;<br />&#160; &#160; &#160; &#160; GL[i ].in = 0;<br />&#160; &#160; &#160; &#160; GL[i ].firstEdge = NULL;<br />&#160; &#160; &#160; &#160; book[i ] = 0;<br />&#160; &#160; }<br />&#160; &#160;<br />&#160; &#160; for (i=0; data[i ]!=&#039;\0&#039;; i+=2)//每次读取两个变量&#160; <br />&#160; &#160; {<br />&#160; &#160; &#160; &#160; u = data[ i] - &#039;a&#039;; //字母转换为数字，&#039;a&#039;对应0，&#039;b&#039;对应1，以此类推<br />&#160; &#160; &#160; &#160; v = data[i+1] - &#039;a&#039;;<br />&#160; &#160; &#160; &#160; book[u ] = book[v] = 1;<br />&#160; &#160; &#160; &#160; <br />&#160; &#160; &#160; &#160; e = (EdgeNode*)malloc(sizeof(EdgeNode)); //采用头插法插入边表结点<br />&#160; &#160; &#160; &#160; if (!e)<br />&#160; &#160; &#160; &#160; {<br />&#160; &#160; &#160; &#160; &#160; &#160; puts(&quot;Error&quot;);<br />&#160; &#160; &#160; &#160; &#160; &#160; exit(1);<br />&#160; &#160; &#160; &#160; }<br />&#160; &#160; &#160; &#160; e-&gt;adjvex = v;<br />&#160; &#160; &#160; &#160; e-&gt;next = GL[u ].firstEdge;<br />&#160; &#160; &#160; &#160; GL[u ].firstEdge = e;<br />&#160; &#160; &#160; &#160; <br />&#160; &#160; &#160; &#160; GL[v].in++;<br />&#160; &#160; }<br />&#160; &#160;<br />&#160; &#160; for (i=0; i&lt;MAXM; i++)//计算顶点数量<br />&#160; &#160; {<br />&#160; &#160; &#160; &#160; if (book[i ] != 0)<br />&#160; &#160; &#160; &#160; &#160; &#160; count++;<br />&#160; &#160; }<br />&#160; &#160;<br />&#160; &#160; return count;<br />}</p><p>拓扑排序算法其实非常简单，只需要搜索入度为0的弧尾顶点，然后将其对应的弧头顶点入度减1，如果该弧头顶点入度也变成了0，就将其存储到栈（或队列）中。搜索的方法有深度优先和广度优先两种。代码分别如下：<br />/*<br />函数名称：TopoLogicalSort_DFS<br />函数功能：拓扑排序，采用深度优先搜索获取拓扑序列<br />输入变量：char *topo：用来存储拓扑序列的字符串<br />&#160; &#160; &#160; &#160; &#160; VertexNode *GL ： 顶点表数组<br />&#160; &#160; &#160; &#160; &#160; int n：顶点个数<br />输出变量：用来存储拓扑序列的字符串<br />返回值：int ：拓扑排序成功返回真，若存在环则返回假<br />*/<br />int TopoLogicalSort_DFS(char *topo, VertexNode *GL, int n)<br />{<br />&#160; &#160; int i, u, v, top;<br />&#160; &#160; int count = 0; //用于统计输出顶点的个数<br />&#160; &#160; EdgeNode *e;<br />&#160; &#160; int Stack[MAXM];<br />&#160; &#160;<br />&#160; &#160; for (top=i=0; i&lt;MAXM; i++)//将入度为0的顶点入栈<br />&#160; &#160; {<br />&#160; &#160; &#160; &#160; if (book[i ] != 0 &amp;&amp; GL[i ].in == 0)<br />&#160; &#160; &#160; &#160; {<br />&#160; &#160; &#160; &#160; &#160; &#160; Stack[top++] = i;<br />&#160; &#160; &#160; &#160; }<br />&#160; &#160; }<br />&#160; &#160;<br />&#160; &#160; while (top &gt; 0)//采用深度优先搜索获取拓扑序列<br />&#160; &#160; {<br />&#160; &#160; &#160; &#160; u = Stack[--top];<br />&#160; &#160; &#160; &#160; topo[count++] = u + &#039;a&#039;;<br />&#160; &#160; &#160; &#160; <br />&#160; &#160; &#160; &#160; for (e=GL[u ].firstEdge; e!=NULL; e=e-&gt;next)//将u的邻接点入度减1，并将入度为0的顶点入栈<br />&#160; &#160; &#160; &#160; {<br />&#160; &#160; &#160; &#160; &#160; &#160; v = e-&gt;adjvex;<br />&#160; &#160; &#160; &#160; &#160; &#160; if (--GL[v].in == 0)<br />&#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; Stack[top++] = v;<br />&#160; &#160; &#160; &#160; }<br />&#160; &#160; }<br />&#160; &#160; topo[count] = &#039;\0&#039;;<br />&#160; &#160;<br />&#160; &#160; return (count == n);//如果count小于顶点数，说明存在环<br />}</p><p>/*<br />函数名称：TopoLogicalSort_BFS<br />函数功能：拓扑排序，采用广度优先搜索获取拓扑序列<br />输入变量：char *topo：用来存储拓扑序列的字符串<br />&#160; &#160; &#160; &#160; &#160; VertexNode *GL ： 顶点表数组<br />&#160; &#160; &#160; &#160; &#160; int n：顶点个数<br />输出变量：用来存储拓扑序列的字符串<br />返回值：int ：拓扑排序成功返回真，若存在环则返回假<br />*/<br />int TopoLogicalSort_BFS(char *topo, VertexNode *GL, int n)<br />{<br />&#160; &#160; int i, u, v, front, rear;<br />&#160; &#160; EdgeNode *e;<br />&#160; &#160;<br />&#160; &#160; front = rear = 0;<br />&#160; &#160; for (i=0; i&lt;MAXM; i++)//将入度为0的顶点入栈<br />&#160; &#160; {<br />&#160; &#160; &#160; &#160; if (book[i ] != 0 &amp;&amp; GL[i ].in == 0)<br />&#160; &#160; &#160; &#160; {<br />&#160; &#160; &#160; &#160; &#160; &#160; topo[rear++] = i + &#039;a&#039;;<br />&#160; &#160; &#160; &#160; }<br />&#160; &#160; }<br />&#160; &#160;<br />&#160; &#160; while (front &lt; rear)//采用广度优先搜索获取拓扑序列<br />&#160; &#160; {<br />&#160; &#160; &#160; &#160; u = topo[front++] - &#039;a&#039;;<br />&#160; &#160; &#160; &#160; <br />&#160; &#160; &#160; &#160; for (e=GL[u ].firstEdge; e!=NULL; e=e-&gt;next)//将u的邻接点入度减1，并将入度为0的顶点入栈<br />&#160; &#160; &#160; &#160; {<br />&#160; &#160; &#160; &#160; &#160; &#160; v = e-&gt;adjvex;<br />&#160; &#160; &#160; &#160; &#160; &#160; if (--GL[v].in == 0)<br />&#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; topo[rear++] = v + &#039;a&#039;;<br />&#160; &#160; &#160; &#160; }<br />&#160; &#160; }<br />&#160; &#160; topo[rear] = &#039;\0&#039;;<br />&#160; &#160;<br />&#160; &#160; return (rear == n);//如果count小于顶点数，说明存在环<br />}</p><p>我们也可以用边表集来表示图，数据结构如下：<br />typedef struct Edge{ //边集数组<br />&#160; &#160; int u, v; //弧尾和弧头<br />&#160; &#160; int next; //指向同一个弧尾的下一条边<br />//&#160; &#160; EdgeType weight; //权值，对于非网图可以不需要<br />} EdgeLib;</p><p>为了表示顶点信息，我们还需要设置两个数组：int In[MAXM], first[MAXM]; //分别存储顶点的入度和第一条边信息。<br />边表集实现拓扑排序的算法和邻接表非常相似，也是先读入图的顶点和边信息，然后进行拓扑排序。代码如下：<br />/*<br />函数名称：CreateGraph_2<br />函数功能：把顶点和边信息读入到表示图的边表集中<br />输入变量：char *data：存储了N个关系式的字符串<br />&#160; &#160; &#160; &#160; &#160; int In[]：存储了顶点的入度信息<br />&#160; &#160; &#160; &#160; &#160; int first[]：指向以该顶点为弧尾的第一条边<br />&#160; &#160; &#160; &#160; &#160; EdgeLib edge[]：存储了边信息的边表集<br />输出变量：表示图的边表集数组<br />返回值：int ：顶点数量<br />*/<br />int CreateGraph_2(char *data, int In[], int first[], EdgeLib edge[])//创建一个图<br />{<br />&#160; &#160; int i, j;<br />&#160; &#160; int count = 0;//记录顶点数量<br />&#160; &#160;<br />&#160; &#160; for (i=0; i&lt;MAXM; i++)//初始化图<br />&#160; &#160; {<br />&#160; &#160; &#160; &#160; first[ i ] = -1;<br />&#160; &#160; &#160; &#160; book[ i ] = 0;<br />&#160; &#160; &#160; &#160; In[ i ] = 0;<br />&#160; &#160; }<br />&#160; &#160;<br />&#160; &#160; for (j=i=0; data[ i ]!=&#039;\0&#039;; i+=2,j++)//每次读取两个变量&#160; <br />&#160; &#160; {<br />&#160; &#160; &#160; &#160; edge[j].u = data[i ] - &#039;a&#039;; //字母转换为数字，&#039;a&#039;对应0，&#039;b&#039;对应1，以此类推<br />&#160; &#160; &#160; &#160; edge[j].v = data[i+1] - &#039;a&#039;;<br />&#160; &#160; &#160; &#160; book[edge[j].u] = book[edge[j].v] = 1;<br />&#160; &#160; &#160; &#160; <br />&#160; &#160; &#160; &#160; edge[j].next = first[edge[j].u];<br />&#160; &#160; &#160; &#160; first[edge[j].u] = j;<br />&#160; &#160; &#160; &#160; In[edge[j].v]++;<br />&#160; &#160; }<br />&#160; &#160;<br />&#160; &#160; for (i=0; i&lt;MAXM; i++)//计算顶点数量<br />&#160; &#160; {<br />&#160; &#160; &#160; &#160; if (book[i ] != 0)<br />&#160; &#160; &#160; &#160; &#160; &#160; count++;<br />&#160; &#160; }<br />&#160; &#160;<br />&#160; &#160; return count;<br />}</p><p>/*<br />函数名称：TopoLogicalSort<br />函数功能：拓扑排序，采用广度优先搜索获取拓扑序列<br />输入变量：char *topo：用来存储拓扑序列的字符串<br />&#160; &#160; &#160; &#160; &#160; EdgeLib edge[]：存储了边信息的边表集<br />&#160; &#160; &#160; &#160; &#160; int In[]：存储了顶点的入度信息<br />&#160; &#160; &#160; &#160; &#160; int first[]：指向以该顶点为弧尾的第一条边<br />&#160; &#160; &#160; &#160; &#160; int n：顶点个数<br />输出变量：用来存储拓扑序列的字符串<br />返回值：int ：拓扑排序成功返回真，若存在环则返回假<br />*/<br />int TopoLogicalSort(char *topo, EdgeLib edge[], int In[], int first[], int n)<br />{<br />&#160; &#160; int i, u, front, rear;<br />&#160; &#160;<br />&#160; &#160; front = rear = 0;<br />&#160; &#160; for (i=0; i&lt;MAXM; i++)//将入度为0的顶点入栈<br />&#160; &#160; {<br />&#160; &#160; &#160; &#160; if (book[i ] != 0 &amp;&amp; In[i ] == 0)<br />&#160; &#160; &#160; &#160; {<br />&#160; &#160; &#160; &#160; &#160; &#160; topo[rear++] = i + &#039;a&#039;;<br />&#160; &#160; &#160; &#160; }<br />&#160; &#160; }<br />&#160; &#160;<br />&#160; &#160; while (front &lt; rear)//采用广度优先搜索获取拓扑序列<br />&#160; &#160; {<br />&#160; &#160; &#160; &#160; u = topo[front++] - &#039;a&#039;;<br />&#160; &#160; &#160; &#160; for (i=first[u ]; i!=-1; i=edge[i ].next)<br />&#160; &#160; &#160; &#160; {<br />&#160; &#160; &#160; &#160; &#160; &#160; if (--In[edge[i ].v] == 0)<br />&#160; &#160; &#160; &#160; &#160; &#160; &#160; &#160; topo[rear++] = edge[i ].v + &#039;a&#039;;<br />&#160; &#160; &#160; &#160; }<br />&#160; &#160; }<br />&#160; &#160; topo[rear] = &#039;\0&#039;;<br />&#160; &#160;<br />&#160; &#160; return (rear == n);//如果count小于顶点数，说明存在环<br />}</p><p>这里只给出了相关函数，完整的测试代码请到巧若拙的博客（http://blog.csdn.net/qiaoruozhuo）查看。</p>]]></description>
			<author><![CDATA[dummy@example.com (batsom)]]></author>
			<pubDate>Fri, 09 Dec 2022 08:58:43 +0000</pubDate>
			<guid>http://www.gentoo-zh.org/viewtopic.php?pid=653#p653</guid>
		</item>
	</channel>
</rss>
