240. 搜索二维矩阵 II
编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target 。该矩阵具有以下特性:
每行的元素从左到右升序排列。
每列的元素从上到下升序排列。
示例 1:
输入: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:
输入: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
原始思路:
从左下角开始,比较上和右,上为小,右为大 2.根据target值选择上或右,知道找到target为止
当前数比目标元素小,当前列就不可能存在目标值,“指针”就向右移一格;
如果当前数比目标元素大,当前行就不可能存在目标值,“指针”就向上移一格
class Solution {
public:
bool searchMatrix(vector<vector<int>>& matrix, int target) {
int m = matrix.size();
if (m == 0) {
return false;
}
int n = matrix[0].size();
if (n == 0) {
return false;
}
// 起点:左下角
int x = m - 1;
int y = 0;
// 不越界的条件是:行大于等于 0,列小于等于 cols - 1
while (x >= 0 && y < n) {
if (matrix[x][y] > target) {
x--;
} else if (matrix[x][y] < target) {
y++;
} else {
return true;
}
}
return false;
}
};//88 ms 14.6MB
其他思路:
1.暴力法
找到了,返回 true
否则,对于搜索到末尾都没有返回的循环,返回 false
class Solution {
public:
bool searchMatrix(vector<vector<int>>& matrix, int target) {
for(int i=0;i<matrix.size();i++){
for(int j=0;j<matrix[0].size();j++){
if(matrix[i][j]==target){
return true;
}
}
}
return false;
}
};
2.二分查找
一行一行的进行二分查找即可
/*某一行的第一个元素大于了 target ,当前行和后边的所有行都不用考虑了,直接返回 false。
某一行的最后一个元素小于了 target ,当前行就不用考虑了,换下一行。*/
class Solution {
int m, n;
public:
bool searchMatrix(vector<vector<int>>& matrix, int target) {
m = matrix.size(), n = matrix[0].size();
for(int i = 0; i < m; ++i)
if(binarySearch(matrix, i, target)) return true;
return false;
}
bool binarySearch(vector<vector<int>>& matrix, int i, int target)
{
int left = 0, right = n;
while(left < right)
{
int mid = left + (right - left)/2;
if(target > matrix[i][mid]) left = mid + 1;
else if(target < matrix[i][mid]) right = mid;
else return true;
}
return false;
}
};
3.单二分
行 是单调递增的特性
利用STL库函数 binary_search 做查找
class Solution {
public:
bool searchMatrix(vector<vector<int>>& matrix, int target) {
for (auto &i : matrix)
if (binary_search(i.begin(), i.end(), target)) return true;
return false;
}
};
4.双二分
把 行/列 单调递增的特性都用到
class Solution {
public:
bool searchMatrix(vector<vector<int>>& matrix, int target) {
return searchMatrix(matrix, target, 0, matrix.size() - 1);
}
private:
bool searchMatrix(vector<vector<int>>& matrix, int target, int t, int d) {
if (t > d) return false;
int mid = t + (d - t) / 2;
return searchTarget(matrix[mid], target) || // 查找中间
searchMatrix(matrix, target, t, mid - 1) || // 查找上半部
searchMatrix(matrix, target, mid + 1, d) ; // 查找下半部
}
bool searchTarget(vector<int> &m, int target) {
int l = 0, r = m.size() - 1;
while (l <= r) {
int mid = l + (r - l) / 2;
if (m[mid] == target) return true;
m[mid] > target ? r = --mid : l = ++mid;
}
return false;
}
};
435. 无重叠区间
给定一个区间的集合,找到需要移除区间的最小数量,使剩余区间互不重叠。
注意:
可以认为区间的终点总是大于它的起点。
区间 [1,2] 和 [2,3] 的边界相互“接触”,但没有相互重叠。
示例 1:
输入: [ [1,2], [2,3], [3,4], [1,3] ]
输出: 1
解释: 移除 [1,3] 后,剩下的区间没有重叠。
示例 2:
输入: [ [1,2], [1,2], [1,2] ]
输出: 2
解释: 你需要移除两个 [1,2] 来使剩下的区间没有重叠。
示例 3:
输入: [ [1,2], [2,3] ]
输出: 0
解释: 你不需要移除任何区间,因为它们已经是无重叠的了。
原始思路:
输出需要移除的区间个数(最少去除多少个)贪心
如何判别重叠?去掉重复区间;给区间设置left和right指针 1.首先按照左区间排序
2.选择左区间和有区间最小的那个区间作为开始区间
3.选择>=右区间的左区间作为下一个区间,且区间长度最短(该区间的right最小)
思路更正:
按照左边界排序,就要从右向左遍历,因为左边界数值越大越好(越靠右),这样就给前一个区间的空间就越大,所以可以从右向左遍历。
从左向右记录非交叉区间的个数。最后用区间总数减去非交叉区间的个数就是需要移除的区间个数了
每次取非交叉区间的时候,都是可右边界最小的来做分割点
题目只是要求移除区间的个数,没有必要去真实的模拟删除区间
难点:
难点一:有感觉需要排序,但究竟怎么排序,按左边界排还是右边界排。
难点二:排完序之后如何遍历,如果没有分析好遍历顺序,那么排序就没有意义了。
难点三:直接求重复的区间是复杂的,转而求最大非重复区间个数。
难点四:求最大非重复区间个数时,需要一个分割点来做标记。
class Solution{
public:
//按照右边界排序,1代表右
static bool cmp(const vector<int>&a,const vector<int>&b){
return a[1]<b[1];
}
int eraseOverlapIntervals(vector<vector<int>>& intervals){
if(intervals.size()==0) return 0;
sort(intervals.begin(),intervals.end(),cmp);//按右边界排序
int count=1;//记录非交叉区间个数
int end=intervals[0][1];//记录区间分割点
for(int i=1;i<intervals.size();i++){ //遍历
if(end<=intervals[i][0]){ //分割点<左区间
end=intervals[i][1];//其右区间等于新的分割点
count++;//非交叉区间加一
}
}
return intervals.size()-count;
}
}
};
其他思路:
动态规划
先将所有的n个区间按照左端点(或者右端点)从小到大进行排序,随后使用动态规划的方法求出区间数量的最大值
在所有满足要求的j中,选择fj最大的那一个进行状态转移,如果找不到满足要求的区间,那么状态转移方程中min 这一项就为0,fi就为1
最终的答案即为所有fi中的最大值
状态转移方程:fi=max{fj}+1 fi表示以区间i为最后一个区间,选出区间数量最大值
class Solution {
public:
int eraseOverlapIntervals(vector<vector<int>>& intervals) {
if(intervals.empty()){
return 0;
}
sort(intervals.begin(),intervals.end(),[](const auto&u, const auto& v){
return u[0]<v[0];
});//将比较方法和比较函数写在了一起
int n=intervals.size();
vector<int>f(n,1);
for(int i=1;i<n;++i){
for(int j=0;j<i;++j){
if(intervals[j][1]<=intervals[i][0]){
f[i]=max(f[i],f[j]+1);//动态规划方程
}
}
}
return n-*max_element(f.begin(),f.end());
}
};