LeetCode刷题之路-697. 两数相加
作者:互联网
LeetCode刷题之路-697. 两数相加
题目介绍
给定一个非空且只包含非负数的整数数组 nums
,数组的度的定义是指数组里任一元素出现频数的最大值。
你的任务是在 nums
中找到与 nums
拥有相同大小的度的最短连续子数组,返回其长度。
示例 1:
输入:[1, 2, 2, 3, 1]
输出:2
解释:
输入数组的度是2,因为元素1和2的出现频数最大,均为2.
连续子数组里面拥有相同度的有如下所示:
[1, 2, 2, 3, 1], [1, 2, 2, 3], [2, 2, 3, 1], [1, 2, 2], [2, 2, 3], [2, 2]
最短连续子数组[2, 2]的长度为2,所以返回2.
示例 2:
输入:[1,2,2,3,1,4,2]
输出:6
提示:
nums.length
在1到 50,000 区间范围内。nums[i]
是一个在 0 到 49,999 范围内的整数。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/degree-of-an-array
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
解题思路
-
这个题在LeetCode上被标记为简单题,思路确实也很容易想出来。题目的意思是让找出具有最大度的最短连续子数组的长度,最大度意思就是数组中某一元素x出现的最大次数。首先要明确一点就是:具有最大度的最短连续子数组,其两端肯定是x第一次出现的位置和最后一次出现的位置。那么思路很容易,我们首先统计出每个元素的出现频数、第一次出现位置及最后一次出现位置,然后得出频数最大元素的第一次出现位置及最后一次出现位置,将两者相减加1即可得出最短长度。代码如下:
class Solution { public: int findShortestSubArray(vector<int>& nums) { unordered_map<int, int> mv; // 统计nums数组中各个元素的频数 unordered_map<int, pair<int, int> > mse; // 统计nums数组中的各个元素的第一次出现位置和最后一次的出现位置 int maxv = 0; // 记录最大的频数 for(int i = 0; i < nums.size(); i++){ mv[nums[i]]++; // nums[i]的频数加1 if(maxv < mv[nums[i]]){ maxv = mv[nums[i]]; // 更新最大频数 } if(mse[nums[i]].first == 0) // 记录nums[i]第一次出现的位置(mp的int型的value一开始初始化为0) mse[nums[i]].first = i + 1; // 规避开value初始化的值0,所以nums[i]的出现位置加1,最小为1 mse[nums[i]].second = i + 1; // 更新nums[i]最后一次的出现位置 } int ans = nums.size(); // 最短连续子数组的长度最大可能为nums数组的长度 for(auto it = mv.begin(); it != mv.end(); it ++){ if(it -> second == maxv){ // 频数等于最大频数 if(mse[it->first].second - mse[it->first].first + 1 < ans) ans = mse[it->first].second - mse[it->first].first + 1; // 更新最短连续子数组的长度 } } return ans; } };
执行结果:
-
看了C++版的官方题解感觉思路差不多,但是官方题解更加简单,将我的两个map综合成一个,代码如下:
class Solution { public: int findShortestSubArray(vector<int>& nums) { unordered_map<int, vector<int> > mp; int n = nums.size(); for(int i = 0; i < n; i++){ if(mp.count(nums[i])){ // 检测nums[i]是否第一次出现 mp[nums[i]][0]++; // nums[i]的频数加1 mp[nums[i]][2] = i; // 更新nums[i]最后一次的出现位置 }else{ mp[nums[i]] = {1, i, i}; // nums[i]第一次出现,对value进行初始化 } } int maxNum = 0, minLen = 0; for(auto it = mp.begin(); it != mp.end(); it++){ if(maxNum < it->second[0]){ maxNum = it->second[0]; // 更新最大频数 minLen = it->second[2] - it->second[1] + 1; // 更新最短长度 }else if(maxNum == it->second[0]){ // 频数相等 if(minLen > it->second[2] - it->second[1] + 1){ // 频数相等但长度更短 minLen = it->second[2] - it->second[1] + 1; // 更新最短长度 } } } return minLen; } };
执行结果:
-
从其他题解中还学到了另一种解答方法,即
Map+滑动窗口
的方法,看来以后得考虑用一下滑动窗口了,这种方法确实编码起来比较简单。但这种方法实际上思路与上面两种方法大同小异,只是运用滑动窗口来求出最短子数组的两端的位置,对于效率的提升并不明显,甚至运行时间更长。代码如下:class Solution { public: int findShortestSubArray(vector<int>& nums) { unordered_map<int, int> mp; // 记录频数 int n = nums.size(); int maxNum = 0; // 记录最大的频数 for(int i = 0; i < n; i++){ mp[nums[i]]++; // nums[i]的频数加1 maxNum = max(maxNum, mp[nums[i]]); // 更新最大频数 } mp.erase(mp.begin(), mp.end()); // 重构map int left = 0, right = 0; // 定义窗口 int ans = nums.size(); // 满足频数的最短长度 while(right < n){ // 寻找满足频数的元素 mp[nums[right]]++; while(mp[nums[right]] == maxNum){ // 频数达到要求后,移动左边界到元素出现的第一个位置 ans = min(ans, right - left + 1); // 更新最短连续子数组的长度 mp[nums[left++]]--; } right++; } return ans; } };
执行结果:
结语
这道题只要能仔细理解题目的含义及熟练运用map便不难做出,以后要运用一下滑动窗口这种方法来解决问题,编码起来确实简易懂,这次算是简单地尝试了一下。
坚持不懈地努力才能成为大神!
标签:频数,697,nums,int,second,mp,数组,LeetCode,刷题 来源: https://www.cnblogs.com/yimeixiaobai1314/p/14429374.html