递归算法-条件式返回

递归算法–条件式返回

  1. 前言

函数递归的特点是有去有回,对于底层操作而言,实际上对应着入栈和出栈的过程,直观理解也就是递归过程中无法直接跳出递归,因为栈中仍然保留历史信息。 作为变通的方法,我们可以让某些条件下的递归值不参与返回操作,从而达到“出栈但不出力(状态更新)”的效果。

  1. 问题

直接看一个例子,问题属于线段树大的范畴,利用线段树的技巧求解问题过程中,很多时候需要忽略一些操作,本质上是忽略(改变)左子树或右子树的返回值。如果递归的返回值属于void类型,那么操作相对简单,直接return即可。 显示中经常面临的挑战,返回值属于整数或其它已知数据类型,这种情况下,我们需要采用“蛙跳”技巧,出栈操作必不可少,但是返回操作可以忽略(没有return,直接进行下一步操作)。

问题描述:

给定某一数组a[0…n],并且已知值x,现在要求在数组区间a[l…r]范围内,找到下标i最小对应的数字a[i] ,要求a[i]>x。

这道题可以采用常规的前缀线段树进行求解,二分法是算法通常合理的选择,算法的时间复杂度为(logn)*(logn),这个算法仍有改善的空间。

可以采用二分法遍历线段树,在遍历过程中,选择进入线段树的左子树或者右子树,最后求得某个最大区间完全位于所求解的区间[l…r]的范围内,然后再利用二叉遍历搜索具体的值。

  1. 代码分析

先给出相关的代码,紧接着对这些代码进一步分析,

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]

利用递归树能更形象说明此问题,对于F(8,0,0,1,3)和F(9,1,1,13)两个叶子结点,其返回的标志值都为-1,由于返回值为-1,ls==-1,我们就跳过左子树或右子树更新状态的机会,对其值的上升通道进行“狙击”,让更有效的值获得上升的通道。

递归算法-条件式返回_第1张图片

  1. 小结

本问题阐述了递归中的条件式返回的技巧,它相当于整体子树返回的值为空,从而达到遍历但是不更新其状态的效果。今后学习和实践中,也可以采用此技巧进行相关的问题解决。

参考资料:

Segment Tree - Algorithms for Competitive Programming (cp-algorithms.com)

你可能感兴趣的:(算法,数据结构)