Leetcode刷题:空间缩减思想

文章目录

    • 167. 两数之和 II - 输入有序数组
    • 11. 盛最多水的容器
    • 240. 搜索二维矩阵 II
    • 11. 盛最多水的容器
      • 暴力
      • 空间缩减
        • 证明
      • 二分


167. 两数之和 II - 输入有序数组

167. 两数之和 II - 输入有序数组

Leetcode刷题:空间缩减思想_第1张图片
1. 二分

class Solution {
public:
    vector<int> twoSum(vector<int>& numbers, int target) {

        for (int i = 0; i < numbers.size(); i++) {
            int left = i + 1, right = numbers.size() - 1;
            while (left <= right) {
                int mid = (right + left) / 2;
                if (numbers[mid] < (target - numbers[i])) {
                    left = mid + 1;
                } else if (numbers[mid] > (target - numbers[i])) {
                    right = mid - 1;
                } else{
                    return {i+1, mid+1};
                }
            }
        }
        return {-1, -1};
    }
};

2. 双指针
空间缩减的思想:
一张图告诉你 O(n) 的双指针解法的本质原理(C++/Java) - 两数之和 II - 输入有序数组 - 力扣(LeetCode)

class Solution {
public:
    vector<int> twoSum(vector<int>& numbers, int target) {
        int left = 0, right = numbers.size() - 1;
        while (left < right){
            if (numbers[left] + numbers[right] > target) {
                right--;
            } else if(numbers[left] + numbers[right] < target) {
                left++;
            } else {
                return {left + 1, right + 1};
            }
        }
        return {-1, -1};
    }
};

11. 盛最多水的容器

11. 盛最多水的容器

Leetcode刷题:空间缩减思想_第2张图片


240. 搜索二维矩阵 II

240. 搜索二维矩阵 II

Leetcode刷题:空间缩减思想_第3张图片

我的答案

class Solution {
public:
    int position(vector<vector<int>>& matrix, int target, int r1, int r2, int c1, int c2, int situation) { //用b标记取大于等于还是小于等于
        int pos = -1;
        int mid = 0;
        if (r1 == -1 or r2 == -1 or c1 == -1 or c2 == -1) return -1;
        if (situation == 1) {
            int left = r1, right = r2;
            while (left <= right) {
                mid = (left + right) / 2;
                if (matrix[mid][c1] <= target && mid >= r1 && mid <= r2) {
                    left = mid + 1;
                    pos = mid;
                }
                else {
                    right = mid - 1;
                }
            }
        }
        else if (situation == 2) {
            int left = r1, right = r2;
            while (left <= right) {
                mid = (left + right) / 2;
                if (matrix[mid][c2] < target && mid >= r1 && mid <= r2) {
                    left = mid + 1;
                }
                else {
                    right = mid - 1;
                    pos = mid;
                }
            }
        }
        else if (situation == 3) {
            int left = c1, right = c2;
            while (left <= right) {
                mid = (left + right) / 2;
                if (matrix[r1][mid] <= target && mid >= c1 && mid <= c2) {
                    left = mid + 1;
                    pos = mid;
                }
                else {
                    right = mid - 1;
                }
            }
        }
        else {
            int left = c1, right = c2;
            while (left <= right) {
                mid = (left + right) / 2;
                if (matrix[r2][mid] < target && mid >= c1 && mid <= c2) {
                    left = mid + 1;
                }
                else {
                    right = mid - 1;
                    pos = mid;
                }
            }
        }
        return pos;
    }

    bool searchMatrix(vector<vector<int>>& matrix, int target) {
        if (matrix.size() == 1 && matrix[0].size() == 1) {
            if (matrix[0][0] == target) {
                return true;
            }
            else {
                return false;
            }
        }
        int r1 = 0, r2 = matrix.size() - 1, c1 = 0, c2 = matrix[0].size() - 1;
        int i = 0;
        while (i <= 300) {
            r1 = position(matrix, target, r1, r2, c1, c2, 2);
            r2 = position(matrix, target, r1, r2, c1, c2, 1);
            c1 = position(matrix, target, r1, r2, c1, c2, 4);
            c2 = position(matrix, target, r1, r2, c1, c2, 3);
            i++;
            if (r1 == -1 or r2 == -1 or c1 == -1 or c2 == -1) break;
            if (r1 >= r2 && c1 >= c2) break;
        }
        return (r1 >=0 && r2 >=0 &&c1 >=0 &&c2 >=0&& matrix[r1][c2] == target) ? true : false;
    }

};

从右上角开始单调性扫描

class Solution {
public:
    bool searchMatrix(vector<vector<int>>& matrix, int target) {
        int m = 0, n = matrix[0].size() - 1;
        while (m < matrix.size() && n >= 0) {
            if (matrix[m][n] < target) {
                m++;
            } else if (matrix[m][n] > target) {
                n--;
            } else {
                return true;
            }
        }
        return false;
    }
};

时间复杂度分析:

每一步会排除一行或者一列,矩阵一共有 n n n 行, m m m 列,所以最多会进行 n + m n+m n+m步。所以时间复杂度是 O ( n + m ) O(n+m) O(n+m)


11. 盛最多水的容器

11. 盛最多水的容器

暴力

class Solution {
public:
    int maxArea(vector& height) {
        int m = 0;
        for (int i = 0; i < height.size(); i++) {
            for (int j = i + 1; j < height.size(); j++) {
                if ((j - i) * min(height[i], height[j]) > m) {
                    m = (j - i) * min(height[i], height[j]);
                }
            }
        }
        return m;
    }
};

空间缩减

class Solution {
public:
    int maxArea(vector<int>& height) {
        int l = 0, r = height.size() - 1;
        int m = (height.size() - 1) * min(height[l], height[r]);
        while (l < r) {
            if (height[l] < height[r]){
                l++;
                m = max(m, (r - l) * min(height[l], height[r]));
            } else {
                r--;
                m = max(m, (r - l) * min(height[l], height[r]));
            }
        }
        return m;
    }
};
证明

设有 l l l r r r l l l 位于 r r r 的左边,则:

l l l 位于最左, r r r 位于最右时:

  • h [ l ] < h [ r ] h[l] h[l]<h[r] ,则此时的面积取决于 h [ l ] h[l] h[l] l , r l,r l,r 之间的距离,无论 r r r 如何移动,得到的面积都会小于最初的面积

  • h [ l ] > h [ r ] h[l] >h[r] h[l]>h[r],则此时的面积取决于 h [ r ] h[r] h[r] l , r l,r l,r 之间的距离,无论 l l l 如何移动,得到的面积都会小于最初的面积

一般情况下,面积为 m i n ( h [ l ] , h [ r ] ) ∗ ( r − l ) min(h[l],h[r])*(r-l) min(h[l],h[r])(rl),若将较长边向内移动,只会改变 ( r − l ) (r-l) (rl),不会改变 m i n ( h [ l ] , h [ r ] ) min(h[l],h[r]) min(h[l],h[r]),而如果改变较小的边,就有机会使 m i n ( h [ l ] , h [ r ] ) min(h[l],h[r]) min(h[l],h[r]) 变大。

也就是说,对于任意 l , r l,r l,r,将较长边向中间移动时,都不会出现面积变大的情况,可以被直接排除掉。只需要移动短边即可。

如果使面积最大的位置为 ( l , r ) (l, r) (l,r),则从 ( l , q ) , q > r (l,q),q>r (l,q),q>r移动到 ( l , r ) (l, r) (l,r)的过程中,所有 l l l右移得到的结果都被排除了

二分

对于每一个确定的水面高度,可以得到容器的最大宽度。使用二分法求最大宽度的位置

从左往右找到第一个大于等于h的位置;从右往左找到第一个大于等于h的位置

以求右端点为例:if 从mid到n这一段区间里的所有h[i][都比h矮,那么这一段都不可能成为答案,所以 r i g h t = m i d − 1 right = mid - 1 right=mid1,else (这一段存在大于h的值)答案在这个区间内 l e f t = m i d + 1 left = mid + 1 left=mid+1

如何得到单调数组:求右端点时新建向量存放后缀最大值,如果某个位置高度小于h,其右所有高度都小于,可以直接排除

求左端点时存放前缀最大值

你可能感兴趣的:(c++,leetcode,算法,c++)