zl程序教程

您现在的位置是:首页 >  其他

当前栏目

【算法】递归算法 ② ( 使用递归实现二分法 | if else 编码优化 )

编码算法递归 实现 优化 if Else 二分法
2023-09-27 14:28:11 时间





一、使用递归实现二分法



https://leetcode.cn/problems/binary-search/

典型的二分查找题目 : 从一个 有序数组 中查找某个 目标值 , 返回 该目标元素在数组中的索引值 , 如果 数组中没有该 目标值 , 则返回 -1 ;

如 : 从 [1 , 2 , 4 , 5 , 6] 中查找 目标值 2 , 返回 2 对应的数组元素索引 为 1 ; 如果从上述数组中查找 3 , 数组中没有该元素 , 则返回 -1 ;


使用 递归 实现 二分法 , 目的是 不断 缩小二分区间 , 每次 进行递归的操作 是 取一个 中点 , 进行比较 , 确定向左收缩还是向右收缩 ;

二分法 的 时间复杂度是 log ⁡ ( n ) \log(n) log(n) , 递归的深度也是 O ( log ⁡ n ) O(\log n) O(logn) , 基本不会出现栈溢出 ;

如果递归深度是 O ( n ) O(n) O(n) 则出现栈溢出风险较大 ;


1、递归三要素分析


分析 使用递归实现二分法递归三要素 :

  • 递归定义 : 分析函数的 参数 , 返回值 , 以及代表的含义 ;
    • 参数分析 : 二分法需要接收四个参数 ,
      • 数组集合 ,
      • 查找元素的区间范围 , 起始索引 和 终止索引 , 这是 2 个参数 ,
      • 进行对比的目标值
    • 返回值分析 : 返回值就是 获取的 目标值 在数组中的索引 ;
  • 递归拆解 : 分析每次 递归需要执行的操作 , 就是递归函数的具体内容 ;
    • 缩小查找规模 : 二分法的核心操作就是 不断缩小 查找元素的区间范围 , 判断 区间中心元素 与 目标值的 大小关系
      - 如果 中心元素 = 目标值 , 直接返回该索引值 ;
      - 如果 中心元素 < 目标值 , 则需要去 该查找区间的 右侧继续查找 ;
      - 如果 中心元素 > 目标值 , 则需要去 该查找区间的 左侧继续查找 ;
  • 递归出口 : 每个递归都需要一个 停止条件 , 递归不断循环会造成栈内存溢出 ;
    • 没有找到 : 如果查找的区间范围出现 起始位置 > 结束位置 , 说明没有找到该元素 , 直接返回 -1 ;
    • 成功找到 : 如果查找过程中出现 中心元素 = 目标值 , 直接返回该索引值 ;

2、代码示例


class Solution {
    public int search(int[] nums, int target) {
        return binarySearch(nums, 0, nums.length - 1, target);
    }
	
	// 1. 递归定义 : 分析函数的 参数 , 返回值 , 以及代表的含义
	// 二分法需要接收四个参数 : 
	// 数组集合
	// 查找元素的区间范围 , 起始索引 和 终止索引 , 这是 2 个参数
	// 进行对比的目标值
	// 返回值 : 进行对比的目标值
    private int binarySearch(int[] nums, int start, int end, int target) {
        // 3. 递归出口 : 
        // 没有找到 : 如果查找的区间范围出现 起始位置 > 结束位置 , 
        //           说明没有找到该元素 , 直接返回 -1 ;
        if (start > end) {
            return -1;
        }
		// 成功找到 : 如果查找过程中出现 中心元素 = 目标值 , 直接返回该索引值
        int mid = (start + end) / 2;
        if(nums[mid] == target) {
            return mid;
        }

		// 2. 递归拆解 : 分析每次 递归需要执行的操作 , 就是递归函数的具体内容
		// 缩小查找规模 : 二分法的核心操作就是不断缩小 查找元素的区间范围 ,  
		//               判断区间中心元素 与 目标值的 大小关系 
		// 如果 中心元素 < 目标值 , 则需要去 该查找区间的 右侧继续查找
        if(nums[mid] < target) {
            return binarySearch(nums, mid + 1, end, target);
        }
        // 如果 中心元素 > 目标值 , 则需要去 该查找区间的 左侧继续查找
        return binarySearch(nums, start, mid - 1, target);
    }
}




二、if else 编码优化



在代码中 使用 if 进行判定时 , 推荐少用 else 语句 ;


使用了 else 关键字有以下坏处 :

  • 代码不美观 : 一旦使用了 else , 则在 else 中的 所有语句 都会缩进一行 , 如果使用层数过多 , 会有多层缩进 ;
  • 可读性差 : 在代码中 , 使用的 else 越多 , 代码的 可读性越差 ;

如果要使用 if else 语句 , 推荐将这个逻辑单独封装到一个函数中 , 在函数内部 使用 if return 来替代 if else ;

下面的代码的判定方式 判定 nums[mid] 和 target 的大小 , 有三种情况 :

  • 等于
  • 小于
  • 大于

可以 使用 if else 语句 进行编码 , 但是此处 使用了 if return 的形式 , 代码要更加简洁 , 易读 ;

        if(nums[mid] == target) {
            return mid;
        }
        if(nums[mid] < target) {
            return binarySearch(nums, mid + 1, end, target);
        }
        return binarySearch(nums, start, mid - 1, target);