LeetCode热题100
一.哈希表专题
题目1:两数之和
给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
你可以按任意顺序返回答案。
- 暴力解
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
int len=nums.size();
for(int i=0;i<len-1;i++)
{
for(int j=i+1;j<len;j++)
{
if(nums[i]+nums[j]==target)
{
return {i,j};
}
}
}
return {};
}
};
- 哈希表
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
//哈希表
unordered_map<int,int>map;
for(int i=0;i<nums.size();i++)
{
//先查找再添加新数据
auto it=map.find(target-nums[i]);
if(it!=map.end())
{
return {i,it->second};
}
//存储数据
map[nums[i]]=i;
}
return {};
}
};
- 知识点补充:unordered_map用法
要使用 unordered_map 容器,需要包含头文件 <unordered_map>,然后可以按照以下步骤进行操作:
声明和初始化 unordered_map:
#include <unordered_map>
unordered_map<int, int> hash; // 声明一个空的 unordered_map,键值为 int 类型
插入元素:
hash[1] = 10; // 插入 key 为 1,值为 10 的元素
hash[2] = 20; // 插入 key 为 2,值为 20 的元素
访问元素:
int value = hash[1]; // 访问 key 为 1 的元素的值
查找元素:
auto it = hash.find(2); // 查找 key 为 2 的元素,返回指向该元素的迭代器
if (it != hash.end()) {
int value = it->second; // 获取 key 为 2 的元素的值
}
删除元素:
hash.erase(1); // 删除 key 为 1 的元素
遍历 unordered_map:
for (auto& kv : hash) {
int key = kv.first; // 获取遍历到的键
int value = kv.second; // 获取遍历到的值
cout << "Key: " << key << ", Value: " << value << endl;
}
通过以上操作,你可以使用 unordered_map 实现哈希表的功能,快速地查找、插入和删除键值对。unordered_map 是 C++ 标准库中常用的容器之一,可以在解决各种问题时发挥重要作用。
题目2:字母异位词分组
给你一个字符串数组,请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。
字母异位词 是由重新排列源单词的所有字母得到的一个新单词。
- 哈希表
class Solution {
public:
vector<vector<string>> groupAnagrams(vector<string>& strs) {
unordered_map<string,vector<string>>map;
for(int i=0;i<strs.size();i++)
{
string str=strs[i];
//将每个字符排序
sort(str.begin(),str.end());
//向vector的末尾插入元素
map[str].emplace_back(strs[i]);
}
vector<vector<string>>ans;
for(auto it =map.begin();it!=map.end();it++)
{
ans.emplace_back(it->second);
}
return ans;
}
};
- 补充知识:
emplace_back是在向vector中添加元素时的一种简便方法,它通过构造新元素并将其插入到vector的末尾来实现。在给定的代码中,mp[key]是一个vector,emplace_back用于在mp[key]中添加当前字符串str。
通过emplace_back将字符串str添加到对应的vector中,可以将具有相同键的字符串分组在一起。这样可以方便地将同一组异位词放在同一个vector中,并最终将这些vector放入另一个vector中以表示所有分组的异位词。
题目3:最长连续序列
给定一个未排序的整数数组 nums ,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。
请你设计并实现时间复杂度为 O(n) 的算法解决此问题。
- 暴力解
class Solution {
public:
int longestConsecutive(vector<int>& nums) {
if(nums.size()==0)
{
return 0;
}
if(nums.size()==1)
{
return 1;
}
int max_size=1;
int count=1;
//排序
sort(nums.begin(),nums.end());
//想要时间复杂度是O(n),就必须只是遍历一次
for(int i=1;i<nums.size();i++)
{
if(nums[i]-nums[i-1]==1)
{
//连续
count++;
if(max_size<count)
{
max_size=count;
}
}else if(nums[i]-nums[i-1]==0)
{
//不计入连续长度
continue;
}else
{
//不连续
count=1;
}
}
return max_size;
}
};
- 哈希表
class Solution {
public:
int longestConsecutive(vector<int>& nums) {
unordered_set<int> Set;
for (const int& num : nums) {
Set.insert(num);
}
int longestStreak = 0;
for (const int& num : Set) {
if (!Set.count(num - 1)) {
int currentNum = num;
int currentStreak = 1;
while (Set.count(currentNum + 1)) {
currentNum += 1;
currentStreak += 1;
}
longestStreak = max(longestStreak, currentStreak);
}
}
return longestStreak;
}
};
- 知识点补充 :unordered_set的用法
C++中的unordered_set是一种无序的关联容器,它存储唯一的元素,元素被存储在一个散列表中,因此元素的存储顺序是不确定的。以下是unordered_set的用法示例:
#include <iostream>
#include <unordered_set>
int main() {
// 创建一个unordered_set
std::unordered_set<int> mySet;
// 插入元素
mySet.insert(1);
mySet.insert(2);
mySet.insert(3);
// 遍历元素
for (auto it = mySet.begin(); it != mySet.end(); ++it) {
std::cout << *it << " ";
}
std::cout << std::endl;
// 检查元素是否存在
if (mySet.find(2) != mySet.end()) {
std::cout << "Element 2 exists in the set" << std::endl;
}
// 删除元素
mySet.erase(2);
//查询键值元素的数量
mySet.count(2);//查找key=2出现的次数
// 再次遍历元素
for (auto it = mySet.begin(); it != mySet.end(); ++it) {
std::cout << *it << " ";
}
std::cout << std::endl;
return 0;
}
在上面的示例中,我们创建了一个unordered_set存储整数,插入了几个元素并遍历了它们。我们还展示了如何检查元素是否存在并删除元素。请注意,unordered_set中的元素是唯一的,因此不会重复存储相同的元素。
二.双指针专题
题目1:移动零
给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
请注意 ,必须在不复制数组的情况下原地对数组进行操作。
- 暴力解
class Solution {
public:
void moveZeroes(vector<int>& nums) {
//双指针
int left=0;
int right=0;
while(right<nums.size())
{
if(nums[right])
{
//找到不为零的数,放再前面
//swap(nums[left],nums[right]);
int temp=nums[right];
nums[right]=0;
nums[left]=temp;
left++;
}
right++;
}
}
};
题目2:盛最多水的容器
给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。
找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
返回容器可以储存的最大水量。
说明:你不能倾斜容器。
- 双指针
class Solution {
public:
int maxArea(vector<int>& height) {
int left=0;
int right=height.size()-1;
int maxarea=0;
while(left<right)
{
int wide=right-left;
int high=min(height[left],height[right]);
int area=wide*high;
maxarea=max(maxarea,area);
if(height[left]<height[right])
{
left++;
}else
{
right--;
}
}
return maxarea;
}
};
- 算法详解
这段代码的作用是根据当前左右指针指向的高度大小,来决定移动哪一个指针。具体来说,如果左指针所指的高度小于右指针所指的高度,那么左指针向右移动一步;如果右指针所指的高度小于等于左指针所指的高度,那么右指针向左移动一步。通过这样的移动方式,我们可以不断地调整容器的宽度和高度,从而找到最大的容器面积。
这种移动方式的核心思想是:在每一步中,我们总是移动高度较小的指针,这样可以保留有可能产生更大面积的候选容器。最终在遍历完所有的可能情况后,我们就可以得到最大的容器面积。
这段代码块是双指针法解决容器盛水最多问题中的关键步骤之一,通过动态调整左右指针位置,不断地确认当前状态下的最优解
题目3:三数之和
给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请
你返回所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。
- 官方题解(较为复杂,不好想)
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
int n = nums.size();
sort(nums.begin(), nums.end());
vector<vector<int>> ans;
// 枚举 a
for (int first = 0; first < n; ++first) {
// 需要和上一次枚举的数不相同
if (first > 0 && nums[first] == nums[first - 1]) {
continue;
}
// c 对应的指针初始指向数组的最右端
int third = n - 1;
int target = -nums[first];
// 枚举 b
for (int second = first + 1; second < n; ++second) {
// 需要和上一次枚举的数不相同
if (second > first + 1 && nums[second] == nums[second - 1]) {
continue;
}
// 需要保证 b 的指针在 c 的指针的左侧
while (second < third && nums[second] + nums[third] > target) {
--third;
}
// 如果指针重合,随着 b 后续的增加
// 就不会有满足 a+b+c=0 并且 b<c 的 c 了,可以退出循环
if (second == third) {
break;
}
if (nums[second] + nums[third] == target) {
ans.push_back({nums[first], nums[second], nums[third]});
}
}
}
return ans;
}
};
三.滑动窗口
题目1:无重复字符的最长子串
给定一个字符串 s ,请你找出其中不含有重复字符的 最长 子串 的长度。
- 官方题解(待研究)
class Solution {
public:
int lengthOfLongestSubstring(string s) {
// 哈希集合,记录每个字符是否出现过
unordered_set<char> occ;
int n = s.size();
// 右指针,初始值为 -1,相当于我们在字符串的左边界的左侧,还没有开始移动
int rk = -1, ans = 0;
// 枚举左指针的位置,初始值隐性地表示为 -1
for (int i = 0; i < n; ++i) {
if (i != 0) {
// 左指针向右移动一格,移除一个字符
occ.erase(s[i - 1]);
}
while (rk + 1 < n && !occ.count(s[rk + 1])) {
// 不断地移动右指针
occ.insert(s[rk + 1]);
++rk;
}
// 第 i 到 rk 个字符是一个极长的无重复字符子串
ans = max(ans, rk - i + 1);
}
return ans;
}
};