哈希
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
//构建hash表
unordered_map<int,int>hash;
//遍历每个元素数据
for(int i = 0 ; i < nums.size();++i)
{
//目标 - 当前数据 == 与当前设备匹配的元素数据
auto r = target - nums[i];
//查询hash表中是否有记载
if(hash.count(r))return {hash[r] , i};
//hash表中无记载,把当前数据元素加入到hash表
hash[nums[i]] = i;
}
//没有任何结果返回
return {};
}
};
class Solution {
public:
vector<vector<string>> groupAnagrams(vector<string>& strs) {
//构建哈希表
unordered_map<string,vector<string>>hash;
// 遍历每一个元素
for (auto &str : strs)
{
//将当前元素赋值给新变量
string nstr = str;
//重新排序新的变量
sort(nstr.begin(),nstr.end());
//将排序后的变量作为key,原始变量作为value
hash[nstr].push_back(str);
}
//上面的操作把每个元素数据进行归一化,下面输出归一化后的结果
vector<vector<string>>res;
for(auto &item : hash)res.push_back(item.second);
return res;
}
};
首先将所有数字放入哈希表,遍历哈希表中的元素,因为要找连续的数字序列,因此可以通过向后枚举相邻的数字(即不断加一),判断后面一个数字是否在哈希表中即可。
为了保证O(n)的复杂度,为了避免重复枚举序列,因此只对序列的起始数字向后枚举(例如[1,2,3,4],只对1枚举,2,3,4时跳过),因此需要判断一下是否是序列的起始数字(即判断一下n-1是否在哈希表中)。
class Solution {
public:
int longestConsecutive(vector<int>& nums) {
//构建哈希表,无关顺序
unordered_set<int> hash(nums.begin(), nums.end());
int len = 0;
for (auto num : hash) {
if (hash.find(num - 1) == hash.end()) { // 只考虑连续序列的起始元素,避免重复遍历序列
//依次枚举,计算当前长度
int end = num;
while (hash.find(end) != hash.end())
end += 1;
len = max(end - num, len);
}
}
return len;
}
};
双指针
class Solution {
public:
void moveZeroes(vector<int>& nums) {
//索引位置
int k = 0;
//遍历数组,将非0的数移动到数组前面
for(auto x : nums)
{
if(x){
nums[k++] = x;
}
}
//将剩余的部分补0
while(k < nums.size()) nums[k++] = 0;
}
};
class Solution {
public:
int maxArea(vector<int>& height) {
//存储全局区间最大值
int res = 0;
//使用双指针进行从两边进行扫描处理
for (int i = 0, j = height.size() - 1; i < j; )
{
//使用区间值与当前值做比较,取其中最大值作为最终结果
res = max(res,
min(height[i], height[j]) * (j - i));
//木桶容量由短板决定, 移动长板的话, 水面高度不可能再上升, 而宽度变小了, 所以只有通过移动短板, 才有可能使水位上升.
if (height[i] > height[j]) j -- ;
else i ++ ;
}
return res;
}
};
木桶原理,从当前节点往左找最高的高度,往右找最高的高度,这两个高度我们可以看做是木桶的两个木板,能接的雨水由最短的那块决定,累加每个位置能存的雨水量即可。
class Solution {
public:
int trap(vector<int>& height) {
//定义两个从两端的指针
int l = 0, r = height.size() - 1;
//定义全局标记结果
int ans = 0, lmax = 0, rmax = 0;
while (l < r) {
//提前更新左右最大值
lmax = max(lmax, height[l]);
rmax = max(rmax, height[r]);
if (lmax < rmax) {
ans += lmax - height[l];
l++;
} else {
ans += rmax - height[r];
r--;
}
}
return ans;
}
};
滑动窗口
class Solution {
public:
int lengthOfLongestSubstring(string s) {
unordered_map<char,int>hash;
int res = 0;
for (int i = 0, j = 0; j < s.size(); j ++ )
{
hash[s[j]] ++ ;
//只有出现重复情况才会执行while循环
while (hash[s[j]] > 1)
{
//此处--修改string中各位置的对应value减少1,当while停止的时候,i为与j处重复的地方
hash[s[i ++ ]] -- ;
}
/*
*此处记录当前字符串中的最大字串值,因为数组下标从0开始所以需要加1,这一步每次for都需要执
*行,用来实时更新不重复子串的长度
*/
res = max(res, j - i + 1);
}
return res;
}
};
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
deque<int> q; //双端队列
vector<int> res;//存储滑动窗口结果
for(int i = 0; i < nums.size(); i++) //遍历所有元素
{
// 处理正常划出队列的情况
// i 是右端点,那么以k为长度窗口的左端点是i-k+1;
// 元素淘汰:太靠右的元素 —— 如果左端点比队列的队头要大的话。
// 说明队头已经过时了,需要划出窗口了。队头就要被删掉
if(q.size() && i - k + 1 > q.front()) q.pop_front(); // 队列存储的数组的下标
// 队列内修正的情况
// 加入当前元素之前,预处理队列
// 当前元素大于等于队尾元素
// 删掉队尾
while (q.size() && nums[i] >= nums[q.back()]) q.pop_back(); //对头插入,队尾删除
q.push_back(i); //加入当前元素
if( i >= k -1) res.push_back(nums[q.front()]); //窗口长度满足要求,加入当前的最大元素
}
return res;
}
};