大家好,欢迎来到IT知识分享网。
递归算法–条件式返回
- 前言
函数递归的特点是有去有回,对于底层操作而言,实际上对应着入栈和出栈的过程,直观理解也就是递归过程中无法直接跳出递归,因为栈中仍然保留历史信息。 作为变通的方法,我们可以让某些条件下的递归值不参与返回操作,从而达到“出栈但不出力(状态更新)”的效果。
- 问题
直接看一个例子,问题属于线段树大的范畴,利用线段树的技巧求解问题过程中,很多时候需要忽略一些操作,本质上是忽略(改变)左子树或右子树的返回值。如果递归的返回值属于void类型,那么操作相对简单,直接return即可。 显示中经常面临的挑战,返回值属于整数或其它已知数据类型,这种情况下,我们需要采用“蛙跳”技巧,出栈操作必不可少,但是返回操作可以忽略(没有return,直接进行下一步操作)。
问题描述:
给定某一数组a[0…n],并且已知值x,现在要求在数组区间a[l…r]范围内,找到下标i最小对应的数字a[i] ,要求a[i]>x。
这道题可以采用常规的前缀线段树进行求解,二分法是算法通常合理的选择,算法的时间复杂度为(logn)*(logn),这个算法仍有改善的空间。
可以采用二分法遍历线段树,在遍历过程中,选择进入线段树的左子树或者右子树,最后求得某个最大区间完全位于所求解的区间[l…r]的范围内,然后再利用二叉遍历搜索具体的值。
- 代码分析
先给出相关的代码,紧接着对这些代码进一步分析,
int get_first(int v, int tl, int tr, int l, int r, int x) {
if(l>tr || r<tl) //l.tl...tr..r {
return -1; } int ls; int rs; //when tl...tr is in the range of l..tl..tr..r we can move forward if(l<=tl && tr<=r) {
if(t[v]<=x) {
return -1; } int mid; while(tl!=tr) {
mid=(tl+tr)/2; if(t[2*v]>x) {
v=2*v; tr=mid; } else {
v=2*v+1; tl=mid+1; } } return tl; } int tm=(tl+tr)/2; ls=get_first(2*v,tl,tm,l,r,x); if(ls!=-1) {
return ls; } rs=get_first(2*v+1,tm+1,tr,l,r,x); return rs; }
看到上面的代码,我们利用-1作为条件判断值,如果ls==-1,那么就跳过左子树的值的返回,直接进入右子树;同理如果t[v]<x,此时我们直接返回-1,在ls/rs=-1的条件下,不予返回值,相当于我们直接跳过了左子树或右子树的值的判断,从而进入下一轮递归。
利用递归树能更形象说明此问题,对于F(8,0,0,1,3)和F(9,1,1,13)两个叶子结点,其返回的标志值都为-1,由于返回值为-1,ls==-1,我们就跳过左子树或右子树更新状态的机会,对其值的上升通道进行“狙击”,让更有效的值获得上升的通道。
- 小结
本问题阐述了递归中的条件式返回的技巧,它相当于整体子树返回的值为空,从而达到遍历但是不更新其状态的效果。今后学习和实践中,也可以采用此技巧进行相关的问题解决。
参考资料:
Segment Tree – Algorithms for Competitive Programming (cp-algorithms.com)
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/147703.html