查询区间内距离标尺最近且不大于最大值的元素

一个混杂数组中,要求找到指定标尺相差最小的元素并且该元素不得超出规定的最大值,如何查找呢?

比如 在数组  array(1,2,34,5,6,7,10,31,40,32,36,58,83,4,3) 中,要求找到距离20最近但是又不得大于30的元素

那么这道题很明显,最容易想到的方案是排序,排序后进行查找比对20左右的元素即可。(或者可以把20也放入数组中,查找20 找到它的前后两位进行比较即可)

但是事实上这个数组是变化的,标尺和最大值也是变化的,甚至都不知道标尺和最大值谁更大,数组中是否有重复值,所以这个代码还是要写的更加复杂和全面一些才行。

代码如下:

<?php
function find_nearest_number($array,$stand,$max)
{
    $find_stand_index=array_search($stand,$array);//找到标尺所在位置
    $find_max_index=array_search($max,$array);//找到最大值所在位置
    if($max>$stand)
    {
        //最大值大于标尺 要寻找的元素就在标尺左右 最大值左边
        if($find_stand_index!==false)
        {
            // 数组里面本来就有标尺
            return $stand;
        }
        //数组里面没有标尺
        // 没有标尺要上  制造标尺也要上
        $array[]=$stand;
        sort($array);//进行排序
        $find_stand_index=array_search($stand,$array);//找到标尺所在位置
        //现在再次找到标尺所在位置 找标尺左右两元素进行比对
        $prev=isset($array[$find_stand_index-1])?$array[$find_stand_index-1]:null;//前一个
        $next=isset($array[$find_stand_index+1])?$array[$find_stand_index+1]:null;//后一个
        if($find_max_index!==false)
        {
            //说明最大值就在这个数组中 标尺前面有个 标尺后面一个和最大值 都有可能是最终结果
            if($prev===null)
            {
                //标准值就是最小的 取它后面一个和最大值比较
                return $next;//此时的next必然小于等于最大值
            }
            if($next===null)
            {
                //说明标尺就是最大值 不可能存在这种情况
                return null;
            }
            //前后值都存在 取前后距离标尺最近的
            return $stand-$prev>$next-$stand?$next:$prev;//两段距离做比较
        }
        //此时说明最大值不在数组中 要获取前后值和最大值做比较
        if($prev===null)
        {
            //标准值就是最小的 判断后面的和前面的比较大小        
            if($next>$max)
            {
                //找不到符合要求的了
                return null;
            }
            return $next;
        }
        if($next===null)
        {
            //如果标准值是最大的 直接取前面一个
            return $prev;
        }
        if($next>$max)
        {
            //后面的大于最大值 返回前面的
            return $prev;
        }
        return $stand-$prev>$next-$stand?$next:$prev;//两段距离做比较
    }elseif($max==$stand)
    {
        //最大值就是标尺 要寻找的元素在最大值左边
        if($find_stand_index!==false)
        {
            // 数组里面本来就有标尺 此时标尺就是最大值 返回该标尺 即最接近标尺而且没有超出最大值的元素
            return $stand;
        }
        //说明数组中没有标尺 找标尺左边的一个元素即可
        $array[]=$stand;
        sort($array);//进行排序
        $find_stand_index=array_search($stand,$array);//找到标尺所在位置
        //现在再次找到标尺所在位置 找标尺左边元素进行比对
        $prev=isset($array[$find_stand_index-1])?$array[$find_stand_index-1]:null;//前一个
        if($prev===null)
        {
            //说明标尺就是最小值 而数组中原本没有标尺 则返回null
            return null;
        }
        return $prev;//此时直接返回前一个左边元素即可
    }else
    {
        //最大值小于标尺 要寻找的元素在最大值左边
        // 这个时候我们发现最大值还在标尺的左边 那么直接找最大值的左边元素即可
        if($find_max_index!==false)
        {
            // 数组里面本来就有最大值 直接返回最大值即可
            return $max;
        }
        //数组中没有最大值 则把最大值加入数组并排序
        $array[]=$max;
        sort($array);
        $find_max_index=array_search($max,$array);//找到最大值所在位置
        //现在再次找到最大值所在位置 找最大值左边元素进行比对
        $prev=isset($array[$find_max_index-1])?$array[$find_max_index-1]:null;//前一个
        if($prev===null)
        {
            //说明最大值本身已经是数组中的最小值了 这种情况没有符合要求的元素了
            return null;
        }
        return $prev;
    }
}
?>

调用如下:

结果如图:

找到17是符合要求的,那么如果出现标尺本身就在数组中呢?

或者说最大值小于标尺呢?

那么说明这段代码还是能经得起考验的。

接下来我们可以考虑用另外一种方法解决这个问题

我们设想,实际上就是用标尺去和每一个元素做差值运算,其绝对值越小,则越符合条件,但是也不能大于最大值,那么我们看看下面代码:

<?php
function find_nearest_number_by_each($array,$stand,$max)
{
    $diff=null;//最接近的差值
    $nearest=null;//最接近的元素
    $tmp_diff=null;//临时差值
    foreach ($array as $v)
    {
        if($v>$max){continue;}//如果大于最大值 显然不符合条件
        if($diff===null && $nearest===null)
        {
            //第一次
            $nearest=$v;//将当前元素认定为最接近元素
            $diff=abs($stand-$v);//计算当前元素差值认定为最接近差值
        }else
        {
            //不是第一次了
            $tmp_diff=abs($stand-$v);
            if($tmp_diff<$diff)
            {
                $nearest=$v;
                $diff=$tmp_diff;
            }
        }
    }
    return $nearest;
}
?>

调用如下:

如果最大值大于标尺

我们可以明显看到 使用sort排序加上search查找方法,明显逻辑要复杂的多,而直接foreach循环一趟,反而容易理解和解决这个问题,这也是告诉我们一些道理。

有时候,最简单的也是最有效的!

posted @ 2017-10-31 11:25  李照耀  阅读(461)  评论(0编辑  收藏  举报