缩减搜索空间(11. 盛最多水的容器、167. 两数之和 II、240. 搜索二维矩阵 II)[C++]

缩减搜索空间

笔者先通过描述第一道简单的例题后,对算法进行详细的剖析。

11. 盛最多水的容器

题目描述

给你 n 个非负整数 a1,a2,...,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画n条垂直线,垂直线 i 的两个端点分别为 (i, ai)(i, 0)。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。

说明:你不能倾斜容器。

示例:

image-20201201111151918

输入:[1,8,6,2,5,4,8,3,7]
输出:49 
解释:图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。

提示:

  • n = height.length
  • 2 <= n <= 3 * 104
  • 0 <= height[i] <= 3 * 104

分析与题解

最简单粗暴的方法肯定是暴力枚举所有可能的组合,使用for循环嵌套即可实现,需要注意下标的范围选择,大致框架如下:

for (int i = 0; i < height.size()-1; i++) {
	for (int j = i+1; j < height().size(); j++){
		// 详细讨论代码
	}
}

小心嵌套下标不能重合,并且对于外层下标进行一位的缩减即可,因为不是本章重点不再深入讨论。

上述方法可以预见算法复杂度为O(n^2),执行效率并不理想。我们尝试使用双指针实现缩减搜索空间的策略,要点在于指针每移动一次,都意味着排除掉了一根柱子。可能刚开始理解起来比较抽象,这里以一个小例子帮助读者体会:

image-20201201112232235

如下图所示,在一开始,我们考虑相距最远的两个柱子所能容纳水的面积。水的宽度是两根柱子之间的距离 d = 8;水的高度取决于两根柱子之间较短的那个,即左边柱子的高度 h = 3。水的面积就是 3 * 8 = 24

image-20201201111923037

如果选择固定一根柱子,另外一根柱子变化。水的面积会有如下变化:

  • 当前柱子是最两侧的柱子,水的宽度 d为最大,其他的组合,水的宽度都比这个小。
  • 左边柱子较短,决定了水的高度为3。如果移动左边的柱子,新的水面高度不确定,但一定不会超过右边的柱子高度 7
  • 如果移动右边的柱子,新的水面高度一定不会超过左边的柱子高度 3,也就是不会超过现在的水面高度,而水池宽度却在减小,容量不增反减。

image-20201201113144306

由此可见,如果固定左边的柱子,移动右边的柱子,那么水的高度一定不会增加,且宽度一定减少,所以水的面积一定减少。较短的柱子(左边的柱子)和任意一个其他柱子的组合其实都可以进行排除。也就是我们可以排除掉左边的柱子了。

这个排除掉左边柱子的操作,就是双指针代码里的 i++。i 和 j 两个指针中间的区域都是还未排除掉的区域。随着不断的排除,i 和 j 都会往中间移动。当 i 和 j 相遇,算法就结束了。

详细代码如下:

class Solution {
public:
    int maxArea(vector<int>& height) {
        // 先设立双指针
        int size = height.size();
        int i = 0, j =  size - 1;
        int res = 0;
        while (i < j) {
            int minHeight = height[i] < height[j] ? height[i++] : height[j--];
            int area = (j - i + 1) * minHeight;
            res = max(res, area);
        }
        return res;
    }
};

需要注意的是,代码进行了一定程度的简化。再比较得出minHeight时,已经对双指针其中之一的下标进行更改,所以计算当前水池深度时需要在下标差值的基础上加1

167. 两数之和 II

题目描述

给定一个已按照升序排列 的有序数组,找到两个数使得它们相加之和等于目标数。

函数应该返回这两个下标值 index1 和 index2,其中 index1 必须小于 index2。

说明:

  • 返回的下标值(index1 和 index2)不是从零开始的。
  • 你可以假设每个输入只对应唯一的答案,而且你不可以重复使用相同的元素。

示例:

输入: numbers = [2, 7, 11, 15], target = 9
输出: [1,2]
解释: 2 与 7 之和等于目标数 9 。因此 index1 = 1, index2 = 2 。

分析与题解

需要注意的是,有序数组已经是升序排列。初始状态我们选择首位和末位元素就行求和,并与目标值进行比较:

  • 和大于目标值,更改末尾元素。此时如果进位首位元素,由于升序排列和只会更大,因此末尾元素与其他元素求和的组合均可舍弃。
  • 和小于目标值,更改首位元素。此时如果缩进末尾元素,由于升序排列和只会更小,因此首位元素与其他元素求和的组合均可舍弃。
  • 和等于目标值,直接返回即可。

代码如下:

class Solution {
public:
    vector<int> twoSum(vector<int>& numbers, int target) {
        int size = numbers.size();
        int i = 0, j = size - 1;
        while ( i < j) {
            int sum = numbers[i] + numbers[j];
            // 因为数组是升序排列,将和与target比较
            // 1、sum < target 则抛弃较小的左边界
            // 2、sum > target 则抛弃较大的有边界
            if (sum == target) {
                return vector<int>{i+1, j+1};
            }
            else if (sum < target) {
                i++;
            } else {
                j--;
            }
        }
        return vector<int>{-1, -1};
    }
};

注意返回结果封装从vector形式,并且要求返回的下标值不是从0开始的。

240. 搜索二维矩阵 II

题目描述

编写一个高效的算法来搜索m x n矩阵 matrix 中的一个目标值 target 。该矩阵具有以下特性:

  • 每行的元素从左到右升序排列。
  • 每列的元素从上到下升序排列。

示例 1:

image-20201201203040078

输入:matrix = [[1,4,7,11,15],[2,5,8,12,19],[3,6,9,16,22],[10,13,14,17,24],[18,21,23,26,30]], target = 5
输出:true

示例2:

image-20201201203138242

输入:matrix = [[1,4,7,11,15],[2,5,8,12,19],[3,6,9,16,22],[10,13,14,17,24],[18,21,23,26,30]], target = 20
输出:false

提示:

  • m == matrix.length
  • n == matrix[i].length
  • 1 <= n, m <= 300
  • -10^9 <= matix[i][j] <= 10^9
  • 每行的所有元素从左到右升序排列
  • 每列的所有元素从上到下升序排列
  • -10^9 <= target <= 10^9

分析与题解

仔细观察矩阵左下角或者右上角,对于左下角,往右走数字变大,往上走数字变小,
那么我们从左下角出发,target比当前值大,我们就往右走,target比当前值小,我们就往上走,若target存在一定会找到。

class Solution {
public:
    bool searchMatrix(vector<vector<int>>& matrix, int target) {
        //注意判断空值
        int row  = matrix.size(), col = matrix[0].size();
        if (row == 0 || col == 0) return false;
        //初始化起点下标
        int i = row - 1, j = 0;
        while (i >= 0 && j < col) {
            if (matrix[i][j] < target) 
                j++;
            else if (matrix[i][j] > target)
                i--;
            else return true;
        }
        return false;
    }
};
posted @ 2020-12-01 20:36  脱线森林`  阅读(197)  评论(0编辑  收藏  举报