167. 两数之和 II - 输入有序数组
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. 盛最多水的容器
240. 搜索二维矩阵 II
我的答案
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. 盛最多水的容器
暴力
class Solution {
public:
int maxArea(vector<int>& 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[r] 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])∗(r−l),若将较长边向内移动,只会改变 ( r − l ) (r-l) (r−l),不会改变 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=mid−1,else (这一段存在大于h的值)答案在这个区间内 l e f t = m i d + 1 left = mid + 1 left=mid+1
如何得到单调数组:求右端点时新建向量存放后缀最大值,如果某个位置高度小于h,其右所有高度都小于,可以直接排除
求左端点时存放前缀最大值