Leetcode区间类问题

区间问题 

区间问题,我们需要根据题意,其判断以区间左边界还是右边界为指标,进行排序,然后从第一个开始,观察什么时候符合题目要求,什么时候更新位置。

排序:对内容进行排序,所以二维数组是对一维内容排序,一维是对元素内容排序

升序:

    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;}
        );

剑指 Offer 45. 把数组排成最小的数

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;}//降序,大的在前面
        );

目录

区间问题 

求相交区间

452. 用最少数量的箭引爆气球

435. 无重叠区间

合并重复区间

56. 合并区间

计算最多几个区间重叠

253. 会议室 II

406. 根据身高重建队列 


求相交区间

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/

参考解答:

https://leetcode-cn.com/problems/minimum-number-of-arrows-to-burst-balloons/solution/tan-xin-suan-fa-python-dai-ma-by-liweiwei1419/

https://leetcode-cn.com/problems/minimum-number-of-arrows-to-burst-balloons/solution/yong-zui-shao-shu-liang-de-jian-yin-bao-qi-qiu-b-2/

 

面对区间相交问题,我们一般想到的是先排序,完成排序后,我们再进行筛选符合题意的内容

一般排序是按照什么样的方式呢?以区间的开头吗?如果区间很长,开始的早结束的晚,和其他区间的覆盖面积就很大

一般会选择以区间结尾升序排列,然后不断更新。

下面两道题目非常相似,都是关于区间问题的。

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/ 

 

算法参考:https://leetcode-cn.com/problems/meeting-rooms-ii/solution/253-hui-yi-shi-ii-c-shang-xia-che-wen-ti-by-gao-yu/

本质是求同时重叠的区间个数的最值

根据算法参考:我们把这个过程理解为上下车

开会开始时间就是上车,开会结束时间就是下车,最大重叠区间就是在车上人数最多的时候 

上下车版本:记录开会的开始时间结点,记录开会的结束时间结点

上车就增加人,下车就是减少人,看看这个过程中什么时候人最多

我们只关心开会的开始时间和结束时间,分别按照开始和结束时间升序排序,如此才能看得出会议使用情况!

新会议的开始时间,大于旧会议的结束时间,说明新会议开始前,旧会议已经结束,会议室有空闲

其余情况就是旧会议还没结束,新会议已经开始,那么必然要再次开辟新的房间

完整代码如下:

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/

 

算法参考:https://leetcode-cn.com/problems/queue-reconstruction-by-height/solution/gen-ju-shen-gao-zhong-jian-dui-lie-by-leetcode/

还是找规律的问题,首先排身高高的人,按照身高高低,降序排列。

身高相同,按照位置升序。

完成排列后,我们开始插入工作:

每个人在新队列中的位置(索引),就是他本身的位置,我们第一步排序只是为了更好的按照顺序插入:

 

整个过程如上:我们排序是为了后续按照顺序插入。

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;
    }
};

下面的解释对理解本题很有帮助:

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值