1.16-1.25力扣排序刷题

【1】349. 两个数组的交集

日期:1.16

1.题目链接:349. 两个数组的交集 - 力扣(LeetCode)https://leetcode.cn/problems/intersection-of-two-arrays/description/?envType=problem-list-v2&envId=sorting

2.类型:哈希表,双指针,排序

3.方法:两个集合(半解)

计算两个数组的交集,直观的方法是遍历数组 nums1,对于其中的每个元素,遍历数组 nums2 判断该元素是否在数组 nums2 中,如果存在,则将该元素添加到返回值。

核心思想:使用哈希集合去重和查找

去重:使用unordered_set存储数组元素,自动去除重复元素

快速查找:unordered_set基于哈希表实现,查找操作平均O(1)时间复杂度

优化:总是遍历较小的集合,减少比较次数

关键代码:

cpp 复制代码
 vector<int> intersection(vector<int>& nums1, vector<int>& nums2){
        // 使用两个unordered_set分别存储两个数组的元素
        unordered_set<int> set1, set2;        
        // 将nums1中的元素插入set1中
        for(auto& num:nums1){
            set1.insert(num);
        }        
        // 将nums2中的元素插入set2中
        for(auto& num:nums2){
            set2.insert(num);
        }        
        return getIntersection(set1, set2);
    }

【2】350. 两个数组的交集 II

日期:1.17

1.题目链接:350. 两个数组的交集 II - 力扣(LeetCode)https://leetcode.cn/problems/intersection-of-two-arrays-ii/description/?envType=problem-list-v2&envId=sorting

2.类型:哈希表,双指针,排序

3.方法:两个集合(半解)

记录频率:使用unordered_map记录较小数组中每个数字出现的次数

匹配删除:遍历第二个数组,如果数字在哈希映射中存在且计数大于0,则加入结果,并减少计数

优化:总是用较小的数组构建哈希映射,减少内存使用

与交集I的区别:

交集I:结果中每个元素只出现一次(去重)

交集II:结果中元素出现的次数等于两个数组中该元素出现次数的最小值

关键代码:

cpp 复制代码
    if(nums1.size()>nums2.size()){
            return intersect(nums2,nums1);
        }       
        unordered_map<int,int>m;        
        // 遍历第一个数组,记录每个数字的出现次数
        for(int num:nums1){
            ++m[num];
        }        
        vector<int> intersection;        
        for(int num:nums2){
            // 如果当前数字在哈希映射中存在且计数大于0
            if(m.count(num)){
                intersection.push_back(num);
                --m[num];
                // 如果计数减为0,则从哈希映射中删除该键,以避免后续重复判断
                if(m[num]==0){
                    m.erase(num);
                }
            }
        }

【3】354. 俄罗斯套娃信封问题

日期:1.18

1.题目链接:354. 俄罗斯套娃信封问题 - 力扣(LeetCode)https://leetcode.cn/problems/russian-doll-envelopes/description/?envType=problem-list-v2&envId=sorting

2.类型:动态规划,排序

3.方法:动态规划(官方题解)

核心思路:

降维处理:将二维问题转换为一维问题

排序技巧:先按宽度升序,宽度相同则按高度降序

动态规划:转换为高度序列的最长递增子序列问题

状态定义:f[i]:以第i个信封为结尾的最长递增子序列的长度

状态转移方程:

f[i] = max(f[j] + 1) 对于所有 j < i 且 envelopes[j][1] < envelopes[i][1]

初始值:f[i] = 1(每个信封自身形成一个序列)

关键代码:

cpp 复制代码
        // 排序规则:先按宽度升序,如果宽度相同,则按高度降序
        sort(envelopes.begin(), envelopes.end(), [](const auto& e1, const auto& e2){
            return e1[0]<e2[0]||(e1[0]==e2[0]&&e1[1]>e2[1]);
        });
        // f[i] 表示以第i个信封为结尾的最长递增子序列的长度
        vector<int> f(n, 1);        
        for(int i=1;i<n;++i){
            for(int j=0;j<i;++j){
                // 因为已经按宽度排序,所以只需要检查高度
                // 如果信封j的高度小于信封i的高度,且可以形成更长的序列
                if(envelopes[j][1]<envelopes[i][1]){
                    f[i]=max(f[i],f[j]+1);
                }
            }
        }        
        return *max_element(f.begin(),f.end());

【4】378. 有序矩阵中第 K 小的元素

378. 有序矩阵中第 K 小的元素 - 力扣(LeetCode)https://leetcode.cn/problems/kth-smallest-element-in-a-sorted-matrix/description/?envType=problem-list-v2&envId=sorting

将这个二维数组转成一维数组,并对该一维数组进行排序。最后这个一维数组中的第 k 个数即为答案。

关键代码:

cpp 复制代码
        for(auto& row:matrix){
            for(int it:row){
                rec.push_back(it);
            }
        }        
        // 对一维数组进行排序(升序)
        sort(rec.begin(),rec.end());        
        return rec[k - 1];

4.方法二:归并排序(半解)

最小堆存储候选元素:将每行的第一个元素(最小值)加入堆中

逐步弹出最小元素:每次从堆中弹出当前最小的元素

补充后续元素:当弹出一个元素后,将其所在行的下一个元素加入堆中

重复k-1次:弹出前k-1个最小元素后,堆顶就是第k小的元素

关键代码:

cpp 复制代码
       struct point{
            int val,x,y;              
            point(int val,int x,int y):val(val),x(x),y(y){}            
            // 重载大于运算符,用于构建最小堆
            bool operator>(const point& a)const{ 
                return this->val>a.val; 
            }
        };        
        // 创建最小堆(优先队列),存储point对象
        priority_queue<point, vector<point>, greater<point>> que;        
        int n=matrix.size();  // 矩阵的大小(n×n)        
        for(int i=0;i<n;i++){
            que.emplace(matrix[i][0],i,0);
        }       
        // 执行k-1次弹出操作
        // 因为我们要找第k小的元素,所以需要弹出前k-1个最小的元素
        for(int i=0;i<k-1;i++){
            point now=que.top();  
            que.pop();  
            // 如果当前元素不是它所在行的最后一个元素
            if(now.y!=n-1){
                // 将同一行的下一个元素(右侧元素)加入堆中
                que.emplace(matrix[now.x][now.y + 1], now.x, now.y + 1);
            }
        }        
        return que.top().val;

【5】406. 根据身高重建队列

日期:1.20

1.题目链接:406. 根据身高重建队列 - 力扣(LeetCode)https://leetcode.cn/problems/queue-reconstruction-by-height/description/?envType=problem-list-v2&envId=sorting

2.类型:排序

3.方法一:从低到高考虑(半解)

每个人由一对整数 (h, k) 表示,其中:

h:这个人的身高

k:排在这个人前面且身高大于或等于 h 的人数

需要重新排列这些人,使得排列后的队列满足每个人的 k 值条件。

核心思想:贪心算法 + 从低到高插入

排序策略:先按身高升序,身高相同则按k值降序

插入策略:从最矮的人开始,按照k值找到合适的空位插入

官方题解:

cpp 复制代码
        sort(people.begin(),people.end(),[](const vector<int>& u,const vector<int>& v){
            return u[0] < v[0] || (u[0] == v[0] && u[1] > v[1]);
        });        
        int n=people.size();  
        vector<vector<int>> ans(n);          
        // 按照排序后的顺序,将每个人插入到正确的位置
        for(const vector<int>& person:people){
            int spaces=person[1]+1; 
            for(int i=0;i<n;++i){
                // 如果当前位置为空,说明可以放置(或为空位)
                if(ans[i].empty()){
                    --spaces;  
                    // 当空位减少到0时,说明找到了插入位置
                    if(!spaces){
                        ans[i]=person;  
                        break;
                    }
                }
            }
        }

【6】435. 无重叠区间

日期:1.21

1.题目链接:406. 根据身高重建队列 - 力扣(LeetCode)https://leetcode.cn/problems/queue-reconstruction-by-height/description/?envType=problem-list-v2&envId=sorting

2.类型:排序,贪心,动态规划

3.方法一:动态规划(官方题解)

核心思想:转化为最长不重叠区间序列问题

问题:移除最少的区间使剩余区间互不重叠

等价于:找到最多的互不重叠区间

所以:最少移除数 = 总区间数 - 最大不重叠区间数

动态规划定义:

先按左端点排序,这样我们可以按顺序考虑区间

f[i]:以第i个区间结尾的最长不重叠区间序列的长度

初始值:每个区间自身就是一个序列,所以f[i] = 1

状态转移:

对于第i个区间,检查所有j < i的区间:

如果区间j与区间i不重叠(即intervals[j][1] <= intervals[i][0]

那么可以将区间i接在区间j后面,形成更长的序列

更新:f[i] = max(f[i], f[j] + 1)

关键代码:

cpp 复制代码
        sort(intervals.begin(),intervals.end(),[](const auto& u,const auto& v){
            return u[0]<v[0];
        });
        int n=intervals.size();
        // 动态规划数组,f[i]表示以第i个区间结尾的最长不重叠区间序列的长度
        vector<int> f(n,1);        
        for(int i=1;i<n;++i){
            for(int j=0;j<i;++j){
                // 如果区间j与区间i不重叠(即区间j的右端点 <= 区间i的左端点)
                if(intervals[j][1]<=intervals[i][0]){
                    // 更新f[i]为f[j] + 1和当前f[i]的较大值
                    f[i]=max(f[i],f[j]+1);
                }
            }
        }
        // 需要移除的区间数量 = 总区间数 - 最长不重叠区间序列的长度
        return n-*max_element(f.begin(),f.end());

【7】436. 寻找右区间

日期:1.22

1.题目链接:451. 根据字符出现频率排序 - 力扣(LeetCode)https://leetcode.cn/problems/sort-characters-by-frequency/solutions/855833/gen-ju-zi-fu-chu-xian-pin-lu-pai-xu-by-l-zmvy/?envType=problem-list-v2&envId=sorting

2.类型:字符串,贪心,哈希表

3.方法一:按照出现频率排序(半解)

核心思想:统计 → 排序 → 重建

统计频率:使用哈希表统计每个字符出现的次数

排序:将哈希表转换为可排序的vector,按频率降序排序

重建字符串:按照排序结果重建字符串

cpp 复制代码
        unordered_map<char, int> mp; 
        int length=s.length();        
        for(auto &ch:s){
            mp[ch]++;  // 如果字符不存在,会自动创建并初始化为0,然后加1
        }        
        vector<pair<char, int>> vec;  // 存储(字符, 频率)对        
        for(auto &it:mp){
            // emplace_back直接构造pair,避免创建临时对象
            vec.emplace_back(it);
        }        
        sort(vec.begin(),vec.end(),[](const pair<char,int> &a,const pair<char,int> &b){
            // 按频率降序排序(a.second > b.second)
            return a.second>b.second;
        });        
        string ret;          
        for(auto &[ch,num]:vec){  
            for(int i=0;i<num;i++){
                ret.push_back(ch);
            }
        }        
        return ret;

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

日期:1.23

1.题目链接:452. 用最少数量的箭引爆气球 - 力扣(LeetCode)https://leetcode.cn/problems/minimum-number-of-arrows-to-burst-balloons/description/?envType=problem-list-v2&envId=sorting

2.类型:排序,贪心

3.方法一:排序 + 贪心(官方题解)

核心思想:贪心算法

排序:按照气球的结束位置升序排序

贪心选择:总是把箭射在第一个气球的结束位置,这样可以射中尽可能多的气球

跳过已覆盖:如果一个气球已经被当前的箭射中,跳过它

关键点:

一支箭可以射中所有开始位置 ≤ 箭的位置 ≤ 结束位置的气球

为了用最少的箭,每支箭应该射中尽可能多的气球

把箭射在一个气球的结束位置,可以保证射中这个气球,同时尽可能多地射中其他气球

关键代码:

cpp 复制代码
        // 按照气球的结束坐标升序排序
        // 即按照每个区间的右端点(结束位置)从小到大排序
        sort(points.begin(), points.end(), [](const vector<int>& u, const vector<int>& v){
            return u[1]<v[1];
        });        
        int pos=points[0][1];  
        int ans=1;                     
        for (const vector<int>& balloon:points){
            // 如果当前气球的开始位置大于箭的位置,说明当前箭无法射中这个气球
            // 需要一支新箭,射在当前气球的结束位置
            if(balloon[0]>pos){
                pos=balloon[1];  
                ++ans;            
            }
            // 如果当前气球的开始位置 ≤ 箭的位置,说明当前箭可以射中这个气球
            // 不需要增加新箭,继续检查下一个气球
        }

【9】506. 相对名次

日期:1.24

1.题目链接:506. 相对名次 - 力扣(LeetCode)https://leetcode.cn/problems/relative-ranks/description/?envType=problem-list-v2&envId=sorting

2.类型:排序,数组

3.方法一:排序(一次题解)

题目要求找到每个运动员的相对名次,并同时给前三名标记为 "Gold Medal", "Silver Medal", "Bronze Medal",其余的运动员则标记为其相对名次。

将所有的运动员按照成绩的高低进行排序,然后将按照名次进行标记即可。

关键代码:

cpp 复制代码
        for(int i=0;i<n;++i){
            arr.emplace_back(make_pair(-score[i],i));
        }
        sort(arr.begin(),arr.end());
        vector<string> ans(n);
        for(int i=0;i<n;++i){
            if(i>=3){
                ans[arr[i].second]=to_string(i + 1);
            }else{
                ans[arr[i].second]=desc[i];
            }
        }

【10】561. 数组拆分

日期:1.25

1.题目链接:561. 数组拆分 - 力扣(LeetCode)https://leetcode.cn/problems/array-partition/description/?envType=problem-list-v2&envId=sorting

2.类型:排序,数学

3.方法一:排序(一次题解)

核心思想:排序 + 贪心

排序:将数组按升序排序

贪心选择:将相邻的两个数配对,取每对中的较小值

求和:所有配对中较小值的和就是最大可能和

数学原理:

假设排序后的数组为:a₁ ≤ a₂ ≤ a₃ ≤ a₄ ≤ ... ≤ a₂ₙ

最优配对方式为:(a₁, a₂), (a₃, a₄), ..., (a₂ₙ₋₁, a₂ₙ)

此时,每对的最小值分别是:a₁, a₃, a₅, ..., a₂ₙ₋₁

总和 = a₁ + a₃ + a₅ + ... + a₂ₙ₋₁

关键代码:

cpp 复制代码
        sort(nums.begin(),nums.end());
        int ans=0;
        for(int i=0;i<nums.size();i+=2){
            ans+=nums[i];
        }
        return ans;
相关推荐
老鼠只爱大米2 小时前
LeetCode经典算法面试题 #138:随机链表的复制(节点交织法、哈希表法等五种实现方案解析)
算法·leetcode·链表·随机链表复制·节点交织法
TracyCoder1232 小时前
LeetCode Hot100(11/100)——56. 合并区间
算法·leetcode
a努力。2 小时前
中国邮政Java面试被问:MySQL的ICP(索引条件下推)优化原理
java·开发语言·数据仓库·面试·职场和发展·重构·maven
Hx_Ma162 小时前
Leecode题知识点(25,61,82)
算法·leetcode·链表
青槿吖2 小时前
【趣味图解】线程同步与通讯:从抢奶茶看透synchronized、ReentrantLock和wait/notify
java·开发语言·jvm·算法
2401_838472512 小时前
C++20概念(Concepts)入门指南
开发语言·c++·算法
点云SLAM2 小时前
似然函数(Likelihood Function)和最大似然估计
算法·机器学习·概率论·数理统计·最大似然估计·似然函数·概率分布
春日见2 小时前
Docker中如何删除镜像
运维·前端·人工智能·驱动开发·算法·docker·容器
郝学胜-神的一滴3 小时前
Python中的with语句与try语句:资源管理的两种哲学
linux·服务器·开发语言·python·程序人生·算法