区间问题
区间问题,我们需要根据题意,其判断以区间左边界还是右边界为指标,进行排序,然后从第一个开始,观察什么时候符合题目要求,什么时候更新位置。
排序:对内容进行排序,所以二维数组是对一维内容排序,一维是对元素内容排序
升序:
vector<int>Res;
Res.push_back(1);
Res.push_back(3);
Res.push_back(4);
Res.push_back(6);
Res.push_back(2);
sort(Res.begin(),Res.end(),[](int & i,int & j)
{return i<j;}
);
for(auto item:Res) cout<<item;
降序:
vector<int>Res;
Res.push_back(1);
Res.push_back(3);
Res.push_back(4);
Res.push_back(6);
Res.push_back(2);
sort(Res.begin(),Res.end(),[](int & i,int & j)
{return i>j;}
);
for(auto item:Res) cout<<item;
二维数组:
//对二维数组进行排序
sort(begin(points), end(points),
[](const vector<int> &o1, const vector<int> &o2) {
return (o1[1] < o2[1]);
});
多条件排序:
//按照身高降序,但是身高相同时,我们按照位置升序
sort(begin(people),end(people),[]
(const vector<int> & o1,const vector<int> & o2)
{return o1[0] == o2[0]?o1[1] <= o2[1]:o1[0] > o2[0];});
就排序,我们再看看相关的内容:
820. 单词的压缩编码 https://leetcode-cn.com/problems/short-encoding-of-words/
全部反转,然后按照顺序进行排序,之后找不包含相同元素且最长的字符串,计算长度即可
本题技巧性非常强,从原地翻转,到判断字符串之间的包含关系,绝了
class Solution {
public:
int minimumLengthEncoding(vector<string>& words) {
//翻转
for(auto &item:words) reverse(item.begin(),item.end());
//原地翻转 这样反转一定要加引用
//排序
sort(words.begin(),words.end());
//合并
int res=0;
for(int i=0;i<words.size()-1;i++){
int size=words[i].size();
if(words[i]==words[i+1].substr(0,size)) continue;
res+=size+1;
}
return res+words.back().size()+1;
}
};
49. 字母异位词分组https://leetcode-cn.com/problems/group-anagrams/
典型的排序题目,需要我们对内容进行排序
class Solution {
public:
vector<vector<string>> groupAnagrams(vector<string>& strs) {
if(strs.empty()) return {};
vector<vector<string>>Res;
int index = 0;//不同数字的下标
unordered_map<string,int>M;
for(auto item:strs)
{
string temp = item;
sort(temp.begin(),temp.end());
if(M.find(temp)!=M.end()) Res[M[temp]].push_back(item);
else
{
vector<string> vec(1,item);//新建一维数组,并插入结构
Res.push_back(vec);
M[temp] = index;//标记该字符串的索引
index++;
}
}
return Res;
}
};
class Solution {
public:
string largestNumber(vector<int>& nums) {
if(nums.empty()) return "";
vector<string> temp;
for(auto item:nums) temp.push_back(to_string(item));
sort(begin(temp),end(temp),[](const string & o1,const string & o2)
{return o1+o2>o2+o1;}
);
string Res;
for(auto item:temp) Res += item;
//提出多余的0
int begin = 0;
while(begin<Res.size()&&Res[begin] =='0') begin++;
if(begin == Res.size() ) return "0";
Res = Res.substr(begin,Res.size() - begin);
return Res;
}
};
此类型的题目,核心就是排序这一句话
sort(begin(temp),end(temp),[](const string & o1,const string & o2)
{return o1+o2>o2+o1;}
);
class Solution {
public:
string minNumber(vector<int>& nums) {
vector<string> Temp;
for(auto item:nums) Temp.push_back(to_string(item));
sort(begin(Temp),end(Temp),[](const string & o1,const string & o2)
{return o1+o2<o2+o1;});
string Res;
for(auto item:Temp) Res += item;
int begin = 0;
while(begin>Res.size()&&Res[begin] == '0') begin++;
if(begin == Res.size()) return "0";
Res = Res.substr(begin,Res.size()-begin);
return Res;
}
};
合理运用排序
字符串排序:
sort(begin(temp),end(temp),[](const string & o1,const string & o2)
{return o1+o2>o2+o1;}//降序,大的在前面
);
目录
求相交区间
347前 K 个高频元素
二维数组,记录出现频率和元素,然后以出现频率进行排序,然后选取相应的值即可。
class Solution {
public:
vector<int> topKFrequent(vector<int>& nums, int k) {
if(nums.empty()||k == 0) return {};
unordered_map<int,int>M;//Hash表统计频率
for(auto item:nums) M[item]++;
vector<vector<int>>Helper;
for(auto item:M) {
vector<int>Temp;
Temp.push_back(item.second);
Temp.push_back(item.first);
Helper.push_back(Temp);
}
//helper的first是元素值,second是key值
//以频率为排序指标,进行排序
sort(Helper.begin(),Helper.end(),[](vector<int> & t1,vector<int> & t2)
{
return t1[0]>t2[0];
});
vector<int>Res;
int index = 0;
while(k--){
Res.push_back(Helper[index++][1]);
}
return Res;
}
};
452. 用最少数量的箭引爆气球
https://leetcode-cn.com/problems/minimum-number-of-arrows-to-burst-balloons/
参考解答:
面对区间相交问题,我们一般想到的是先排序,完成排序后,我们再进行筛选符合题意的内容
一般排序是按照什么样的方式呢?以区间的开头吗?如果区间很长,开始的早结束的晚,和其他区间的覆盖面积就很大
一般会选择以区间结尾升序排列,然后不断更新。
下面两道题目非常相似,都是关于区间问题的。
class Solution {
public:
int findMinArrowShots(vector<vector<int>>& points) {
int size = points.size();
if(size == 0) return 0;
//按照要求排序(按照末尾元素大小进行排序)
sort(begin(points), end(points),
[](const vector<int> &o1, const vector<int> &o2) {
return (o1[1] < o2[1]);
});
int Res = 1;
int begin = 0,end = 0,FristEnd = points[0][1];
for(auto item:points)
{
begin = item[0];end = item[1];
if(FristEnd<begin)//不相交,那么就要计数,同时更新FristEnd数值
{
Res++;
FristEnd = end;
}
}
return Res;
}
};
435. 无重叠区间
https://leetcode-cn.com/problems/non-overlapping-intervals/
class Solution {
public:
int eraseOverlapIntervals(vector<vector<int>>& intervals) {
if(intervals.empty()) return 0;
sort(begin(intervals), end(intervals),
[](const vector<int> &o1, const vector<int> &o2) {
return (o1[1] < o2[1]);
});//
int size = 0;
int Begin = 0,End = 0;
int FristEnd = intervals[0][1];
for(int i = 1;i<intervals.size();++i)
{
Begin = intervals[i][0];End = intervals[i][1];
if(Begin<FristEnd)//若相交,那么就是要删除一个区间,删除后来的区间,然后FristEnd极限保持,看看下面还有没有再相交的
size++;
else//如果不相交,更新FristEnd
FristEnd = End;
}
return size;
}
};
合并重复区间
56. 合并区间
https://leetcode-cn.com/problems/merge-intervals/submissions/
本题不同于上面两道题目,需要以区间的首个元素,也就是区间的左极限为指标,进行排序。
因为有特殊情况的存在:
我们需要维护一个区间右极限最大值,一旦新的区间左值小于这个极限值,说明有交集,说明要合并数组
完整代码如下:
class Solution {
public:
vector<vector<int>> merge(vector<vector<int>>& intervals) {
if(intervals.empty()) return {};
vector<vector<int>>Res;
sort(begin(intervals),end(intervals),
[](const vector<int>& P1,const vector<int>& P2)
{return P1[0]<P2[0];});//以第二个元素为指标,升序排列
int begin = 0,size = intervals.size();
while(begin<size)
{
vector<int> Temp(2,0);
Temp[0] = INT_MAX,Temp[1] = INT_MIN;//因为要比大小,此处初值需要特别设置
int end = intervals[begin][1];//记录
while(begin+1<=size-1&&end>=intervals[begin+1][0])//可以合并
{
cout<<intervals[begin][1]<<" "<<intervals[begin+1][0]<<endl;
Temp[0] = min(min(intervals[begin][0],intervals[begin+1][0]),Temp[0]);//取所有区间的最小值
Temp[1] = max(max(intervals[begin][1],intervals[begin+1][1]),Temp[1]);//取所有区间的最大值
end = Temp[1];//实时更新最值
begin++;
}
if(Temp[1] == INT_MIN&&Temp[0] == INT_MAX)//不相交,那么单纯的赋值即可
{
Temp = intervals[begin];
}
begin++;
cout<<Temp[0]<<endl;
Res.push_back(Temp);
}
return Res;
}
};
class Solution {
public:
vector<vector<int>> merge(vector<vector<int>>& intervals) {
if(intervals.empty()) return {};
if(intervals.size() == 1) return intervals;
vector<vector<int>>Res;
sort(intervals.begin(),intervals.end(),[](const vector<int> & s1,const vector<int> & s2)
{return s1<s2;}
);
int Limit_L = intervals[0][0],Limit_R = intervals[0][1];
int index = 1;
while(index<intervals.size()){
vector<int> temp;
while(index<intervals.size()&&intervals[index][0]<=Limit_R){
Limit_L = min(Limit_L,intervals[index][0]);
Limit_R = max(Limit_R,intervals[index][1]);
index++;
}
temp.push_back(Limit_L);temp.push_back(Limit_R);
Res.push_back(temp);
if(index == intervals.size() - 1) {Res.push_back(intervals[index]);break;}
else if(index<intervals.size())
{Limit_L = intervals[index][0],Limit_R = intervals[index][1];index++;}
}
return Res;
}
};
计算最多几个区间重叠
253. 会议室 II
https://leetcode-cn.com/problems/meeting-rooms-ii/
本质是求同时重叠的区间个数的最值
根据算法参考:我们把这个过程理解为上下车
开会开始时间就是上车,开会结束时间就是下车,最大重叠区间就是在车上人数最多的时候
上下车版本:记录开会的开始时间结点,记录开会的结束时间结点
上车就增加人,下车就是减少人,看看这个过程中什么时候人最多
我们只关心开会的开始时间和结束时间,分别按照开始和结束时间升序排序,如此才能看得出会议使用情况!
新会议的开始时间,大于旧会议的结束时间,说明新会议开始前,旧会议已经结束,会议室有空闲
其余情况就是旧会议还没结束,新会议已经开始,那么必然要再次开辟新的房间
完整代码如下:
class Solution {
public:
int minMeetingRooms(vector<vector<int>>& intervals) {
//典型的区间问题
if(intervals.empty()) return 0;
//按照开始时间排序-升序
sort(begin(intervals),end(intervals),[]
(const vector<int>o1,const vector<int>o2){return o1[0]<o2[0];}
);
//按照结束时间排序-降序
auto intervals_end = vector<vector<int>>(intervals.begin(), intervals.end());
sort(begin(intervals_end),end(intervals_end),[]
(const vector<int>o1,const vector<int>o2){return o1[1]<o2[1];}
);
int size = intervals.size();
int used_room = 0;
int s_point=0, e_point=0,MAX = 0;
while(s_point < size&&e_point < size)
{
//新会议的开始时间,大于旧会议的结束时间,说明新会议开始前,旧会议已经结束
//此时会议室空闲出来一件
if(intervals[s_point][0] >= intervals_end[e_point][1])
{
used_room --;//有会议结束,减少房间
e_point++;//查看下一个结束时间点
}
//其余情况就是旧会议还没结束,新会议已经开始,那么必然要再次开辟新的房间
// intervals[s_point][0] < intervals_end[e_point][1]
used_room ++;
s_point ++;
MAX = max(MAX,used_room);
}
return MAX;
}
};
406. 根据身高重建队列
https://leetcode-cn.com/problems/queue-reconstruction-by-height/
还是找规律的问题,首先排身高高的人,按照身高高低,降序排列。
身高相同,按照位置升序。
完成排列后,我们开始插入工作:
每个人在新队列中的位置(索引),就是他本身的位置,我们第一步排序只是为了更好的按照顺序插入:
整个过程如上:我们排序是为了后续按照顺序插入。
class Solution {
public:
vector<vector<int>> reconstructQueue(vector<vector<int>>& people) {
if(people.empty()) return {};
//按照身高降序,但是身高相同时,我们按照位置升序
sort(begin(people),end(people),[]
(const vector<int> & o1,const vector<int> & o2)
{return o1[0] == o2[0]?o1[1] <= o2[1]:o1[0] > o2[0];});
int size = people.size();
vector<vector<int>>Res;
// 循环插入
for(int i = 0; i < size; ++i)
{
Res.insert(Res.begin() + people[i][1], people[i]);//此时务必使用插入,而不是替换
}
return Res;
}
};
下面的解释对理解本题很有帮助: