- 1、原创力文档(book118)网站文档一经付费(服务费),不意味着购买了该文档的版权,仅供个人/单位学习、研究之用,不得用于商业用途,未经授权,严禁复制、发行、汇编、翻译或者网络传播等,侵权必究。。
- 2、本站所有内容均由合作方或网友上传,本站不对文档的完整性、权威性及其观点立场正确性做任何保证或承诺!文档内容仅供研究参考,付费前请自行鉴别。如您付费,意味着您自己接受本站规则且自行承担风险,本站不退款、不进行额外附加服务;查看《如何避免下载的几个坑》。如果您已付费下载过本站文档,您可以点击 这里二次下载。
- 3、如文档侵犯商业秘密、侵犯著作权、侵犯人身权等,请点击“版权申诉”(推荐),也可以打举报电话:400-050-0827(电话支持时间:9:00-18:30)。
- 4、该文档为VIP文档,如果想要下载,成为VIP会员后,下载免费。
- 5、成为VIP后,下载本文档将扣除1次下载权益。下载后,不支持退款、换文档。如有疑问请联系我们。
- 6、成为VIP后,您将拥有八大权益,权益包括:VIP文档下载权益、阅读免打扰、文档格式转换、高级专利检索、专属身份标志、高级客服、多端互通、版权登记。
- 7、VIP文档为合作方或网友上传,每下载1次, 网站将根据用户上传文档的质量评分、类型等,对文档贡献者给予高额补贴、流量扶持。如果你也想贡献VIP文档。上传文档
查看更多
利用树结构求解经栈操作的所有序列到达问题
浙江省慈溪实验中学 张利波 315300
第八届信息学初赛普及组问题求解第一题: 如下图,有一个无穷大的栈S,在栈的右边排列着1,2,3,4,5共五个车厢。其中每个车厢可以向左行走,也可以进入栈S让后面的车厢通过。现已知第一个到达出口的是3号车厢,请写出所有可能的到达出口的车厢排列总数(不必给出每种排列)。
出口← ← 1 2 3 4 5 S↓ 【模拟分析】
这是一例典型的车辆调度题目,其本质是求经过栈操作的到达出口的所有序列,若单纯求数量,可以套用公式C(2n,n)/(n+1),但要进一步排列所有序列,则需要运用其他方法或结构实现,以前有文章提到运用0、1数字串实现(具体可参看《全国青少年信息学联赛培训教材(复赛篇)》(主编:李建江 马茂年)P88 例15火车转轨问题),今天笔者将采用树结构来实现某一状态下(已知首个到达元素)所有符合的输出序列。
以上题为例分析,原始序列1、2、3、4、5,第一个到达出口的是3。我们不妨以当前到达出口元素为界限,分成左右两部分。
左边:根据原始序列,结合当前到达出口元素,说明该元素左边的所有元素目前处在栈中,若出栈,根据“先进后出”特点,只能选择最近一次入栈的元素2。对于元素较多的一般情况,左边范围的元素作为下一个到达出口的元素,是在其左边,距其最近允许访问的元素,元素若存在,仅有一个。
右边:根据当前到达出口的元素,其右元素可以入栈,也可以不入栈(或入栈后马上出栈)。如果选择下一个到达出口的元素,那么可以是右边元素集合中的任一个,且不应遗漏。
显然,从左右两边分析得到的下一个到达元素在时间上必滞后于当前到达元素,可以将下一个到达的所有元素,作为当前元素的孩子,以发展下一层结点,首个到达的元素就是这棵树的根,这样就产生了扩展中第一层结点,如图1所示。
那么后继结点,该如何再扩展呢?即在一般情况下,某种状态下到达出口的元素,同样适合于上面分析,按左右分开,左边选择最近的一个未访问的元素(相当于最后一次入栈元素),右边选择所有未访问过的元素,之所以要求未访问,主要避免重复访问。如图1,若当前元素为2,其下一个到达的元素中,应剔除3,即剔除该结点的祖先(可以通过逐层剔除父结点办法实现),而其他后继子结点运用同样方法,以此继续发展当前元素的下一层结点。由于涉及到是否访问,在编程实现中需要将原始序列的每个元素添加一个访问标记flag。
根据以上分析,我们可以把发展后继子结点的方法看成深度和宽度结合的遍历方式,对于某个元素,先产生其下一层的所有子结点,再在子结点中选择一个,继续产生其下一层的子结点,直到树的深度(到达序列个数)等于原始序列的数量。这个过程总的方向是先深度,后宽度,但在发展深度的一层中先满足该层上的宽度。
现在的问题是在深度优先完成后,需进行回溯,在宽度上依次扩充树的结点。回溯时,由底向顶依次扩充,先扩展自己的子孙(子孙扩展同理),待完成后再扩展其兄弟的子孙,直到其所有兄弟结点都已经扩展,则回溯到其父亲的兄弟结点,……,如此,直到回溯到根为止,即此时所有的结点完成扩展。以引子为例,可以将所有的序列用多叉树表示如图2所示,不难发现所有序列的总数即为叶子结点的个数,具体的序列只要由叶子结点遍历其祖先,构成遍历顺序的逆序即为一个输出序列。根据图2,可排列到达元素的序列有9种状态,分别为:32145,32154,32415,32451,32541,34215,34251,34521,35421。
【编程分析】
本程序运用数组a[]进行结点存储,结构自定义,data表访问元素,pre表该元素的前驱,dep表示该元素目前处在树中的深度。由于扩展过程中要对原始序列不断进行访问,这里也采用了一个数组d[]存储,包含data表示元素值,flag该元素的允许访问标记。
原始序列初始状态下用数组表示如右:
其中flag=true表示该元素允许访问。
初始状态下,先由3写入数组,填写其相关参数,pre=0,dep=0,引入指针变量p、q、succ,其中p指向当前扩展的元素下标(此时为1),q指向填写扩充元素的下标(此时为2),succ指向扩展的第1个结点下标,用于深度扩展时,将该结点作为扩展时的当前点执行循环操作。然后根据原始序列表格,从左边找到最近的元素2,写入数组q位置,填写参数,a[q].data:=d[j].data,a[q].pre:=p,a[q].dep:=a[p].dep+1,此时,q后移一位,即q:=q+1,左边结束。同样从右边找到所有未访问的元素4,5,可以用循环依次写入a[q]位置,填写参数,同时q后移一位,循环结束,表示左右两边元素已经全部写入数组,即当前元素的下一层结点扩展完成。为避免后继结点对祖先的重复访问
文档评论(0)