学习链接:
题目来源:
翻转字符串里的单词
class Solution {
public:
string reverseWords(string s) {
// 反转整个字符串
reverse(s.begin(), s.end());
int n = s.size();
int idx = 0;
for (int start = 0; start < n; ++start) {
if (s[start] != ' ') {
// 填一个空白字符然后将idx移动到下一个单词的开头位置
if (idx != 0) s[idx++] = ' ';
// 循环遍历至单词的末尾
int end = start;
while (end < n && s[end] != ' ') s[idx++] = s[end++];
// 反转整个单词
reverse(s.begin() + idx - (end - start), s.begin() + idx);
// 更新start,去找下一个单词
start = end;
}
}
s.erase(s.begin() + idx, s.end());
return s;
}
};
一、差分数组
1.螺旋矩阵
题目来源:54. 螺旋矩阵
题解:
class Solution {
public:
vector<int> spiralOrder(vector<vector<int>>& matrix) {
vector <int> ans;
if(matrix.empty()) return ans; //若数组为空,直接返回答案
int u = 0; //赋值上下左右边界
int d = matrix.size() - 1;
int l = 0;
int r = matrix[0].size() - 1;
while(true)
{
for(int i = l; i <= r; ++i) ans.push_back(matrix[u][i]); //向右移动直到最右
if(++ u > d) break; //重新设定上边界,若上边界大于下边界,则遍历遍历完成,下同
for(int i = u; i <= d; ++i) ans.push_back(matrix[i][r]); //向下
if(-- r < l) break; //重新设定有边界
for(int i = r; i >= l; --i) ans.push_back(matrix[d][i]); //向左
if(-- d < u) break; //重新设定下边界
for(int i = d; i >= u; --i) ans.push_back(matrix[i][l]); //向上
if(++ l > r) break; //重新设定左边界
}
return ans;
}
};
class Solution {
public:
vector<vector<int>> generateMatrix(int n) {
vector<vector<int>> matrix(n,vector<int>(n));
int u=0,d=n-1,l=0,r=n-1;
int cnt=n*n;
int val=1;
while(val<=cnt){
if (u <= d) {
// 在顶部从左向右遍历
for(int i=l;i<=r;i++)
matrix[u][i]=val++;
// 上边界下移
u++;
}
if(l<=r)//向下
{
for(int i=u;i<=d;i++)
matrix[i][r]=val++;
r--;
}
//向左
if(u<=d){
for(int i=r;i>=l;i--)
matrix[d][i]=val++;
d--;
}
if(l<=r){//向上
for(int i=d;i>=u;i--)
matrix[i][l]=val++;
l++;
}
}
return matrix;
}
};
2.二维网格迁移
题目来源:
https://leetcode.cn/problems/shift-2d-grid/
二、滑动窗口框架
/* 滑动窗口算法框架 */
void slidingWindow(string s) {
unordered_map<char, int> window;
int left = 0, right = 0;
while (right < s.size()) {
// c 是将移入窗口的字符
char c = s[right];
// 增大窗口
right++;
// 进行窗口内数据的一系列更新
...
/*** debug 输出的位置 ***/
// 注意在最终的解法代码中不要 print
// 因为 IO 操作很耗时,可能导致超时
printf("window: [%d, %d)\n", left, right);
/********************/
// 判断左侧窗口是否要收缩
while (window needs shrink) {
// d 是将移出窗口的字符
char d = s[left];
// 缩小窗口
left++;
// 进行窗口内数据的一系列更新
...
}
}
}
3 最大连续1的个数III
//前缀和+二分法
class Solution {
public:
int longestOnes(vector<int>& nums, int k) {
int n = nums.size();
vector<int> P(n + 1);
for (int i = 1; i <= n; ++i) {
P[i] = P[i - 1] + (1 - nums[i - 1]);
}
int ans = 0;
for (int right = 0; right < n; ++right) {
//lower_bound():返回第一个大于等于 P[right + 1] - k的位置
int left = lower_bound(P.begin(), P.end(), P[right + 1] - k) - P.begin();
ans = max(ans, right - left + 1);
}
return ans;
}
};
//滑动窗口
class Solution {
public:
int longestOnes(vector<int>& nums, int k) {
int n=nums.size();
int left=0,lsum=0,rsum=0;
int ans=0;
for(int right=0;right<n;right++){
rsum+=1-nums[right];
while(lsum<rsum-k){
lsum+=1-nums[left];
left++;
}
ans=max(ans,right-left+1);
}
return ans;
}
};
4.绝对差不超过限制的最长连续子数组
题目来源:绝对差不超过限制的最长连续子数组
知识点:
- map容器:c++中map的相关函数及用法(整理)
c++中map详解 - map中的元素自动按key的升序排序。
- it->first访问key,it->second访问value;
函数名 | 功能 |
---|---|
my_map.insert()或按照数组直接赋值 | 插入 |
my_map.find() | 查找一个元素 |
my_map.clear() | 清空 |
my_map.erase() | 删除一个元素 |
my_map.size() | map的长度大小 |
my_map.begin() | 返回指向map头部的迭代器 |
my_map.end() | 返回指向map末尾的迭代器 |
my_map.rbegin() | 返回一个指向map尾部的逆向迭代器 |
my_map.rend() | 返回一个指向map头部的逆向迭代器 |
my_map.empty() | map为空时返回true |
swap() | 交换两个map,两个map中所有元素都交换 |
题解:
class Solution {
public:
int longestSubarray(vector<int>& nums, int limit) {
map<int,int> map;
int left=0,right=0;
int n=nums.size();
int ans=0;
while(right<n){
map[nums[right]]++;
if(map.rbegin()->first-map.begin()->first<=limit){
ans=max(ans,right-left+1);
}
else{
map[nums[left]]--;
if(map[nums[left]]==0)
map.erase(nums[left]);
left++;
}
right++;
}
return ans;
}
};
5.将x减到0的最小操作数
class Solution {
public:
int minOperations(vector<int>& nums, int x) {
int left=-1,right=0;
int lsum=0,rsum=0;
int n=nums.size();
for(int i=0;i<n;i++)
rsum+=nums[i];
if(rsum<x)
return -1;
int ans=n+1;
for(;left<n;++left){
if(left!=-1)
lsum+=nums[left];
while(right<n&&lsum+rsum>x){
rsum-=nums[right];
right++;
}
if(lsum+rsum==x)
ans=min(ans,(left+1)+(n-right));
}
return ans>n?-1:ans;
}
};
6.存在重复元素Ⅱ
题目来源:219.存在重复元素Ⅱ
知识点:C++中set用法详解
- s.lower_bound() 返回第一个大于或等于给定关键值的元素
- s.upper_bound() 返回第一个大于给定关键值的元素
- s.equal_range() 返回一对定位器,分别表示 第一个大于或等于给定关键值的元素 和 第一个大于给定关键值的元素,这个返回值是一个pair类型,如果这一对定位器中哪个返回失败,就会等于s.end()
题解:
class Solution {
public:
bool containsNearbyDuplicate(vector<int>& nums, int k) {
int n=nums.size();
unordered_set<int> s;
for(int left=0;left<n;left++){
if(left>k)
s.erase(nums[left-k-1]);
if(s.count(nums[left]))
return true;
s.emplace(nums[left]);
}
return false;
}
};
7.存在重复元素Ⅲ
题目来源:220.存在重复元素Ⅲ
题解:
class Solution {
public:
bool containsNearbyAlmostDuplicate(vector<int>& nums, int indexDiff, int valueDiff) {
int n=nums.size();
set<int> s;
for(int left=0;left<n;left++)
{
if(left>indexDiff)
s.erase(nums[left-indexDiff-1]);
auto iter=s.lower_bound(nums[left]-valueDiff);
if(iter!=s.end()&&*iter<=nums[left]+valueDiff)
return true;
s.emplace(nums[left]);
}
return false;
}
};
class Solution {
public:
bool containsNearbyAlmostDuplicate(vector<int>& nums, int k, int t) {
int n=nums.size();
set<int> s;
for(int i=0;i<n;i++){
if(i>k)
{
s.erase(nums[i-k-1]);
}
auto iter=s.lower_bound(max(nums[i],INT_MIN+t)-t);
if(iter!=s.end()&& *iter<=min(nums[i],INT_MAX-t)+t)//有时不这样会溢出
return true;
s.emplace(nums[i]);
}
return false;
}
};
8.至少有 K 个重复字符的最长子串
题目来源:395.至少有 K 个重复字符的最长子串
题解:【分治法】
class Solution {
public:
int longestSubstring(string s, int k) {
int n=s.size();
int res=0;
vector<int> num(26);
for(int i=0;i<n;i++){
num[s[i]-'a']++;
}
for(int i=0;i<n;i++){
if(num[s[i]-'a']<k){
int len1=longestSubstring(s.substr(0,i),k);
int len2=longestSubstring(s.substr(i+1,n),k);
return max(len1,len2);
}
}
return n;
}
};
9. 和至少为K的最短子数组【没懂】
题目来源:和至少为K的最短子数组
题解:https://cloud.tencent.com/developer/article/1505345
C++ STL deque容器(详解版)
class Solution {
public:
int shortestSubarray(vector<int>& nums, int k) {
int n=nums.size();
vector<long> preSum(n+1);
preSum[0]=0;
for(int i=1;i<=n;i++)
{
preSum[i]=preSum[i-1]+nums[i-1];
}
int res=n+1;
deque<int> dq;
for(int i=0;i<=n;i++){
long curSum=preSum[i];
while(!dq.empty()&&curSum-preSum[dq.front()]>=k){
res=min(res,i-dq.front());
dq.pop_front();
}
while(!dq.empty()&&preSum[dq.back()]>=curSum){
dq.pop_back();
}
dq.push_back(i);
}
return res<n+1?res:-1;
}
};
10. 数组中第k大的数字【*****】
题目来源:数组中第k大的数字
题解:【用到了大顶堆的方法:建堆、调整、删除】
class Solution {
public:
void maxHeapify(vector<int>& a,int i,int heapSize){
int l = i * 2 + 1, r = i * 2 + 2, largest = i;
if (l < heapSize && a[l] > a[largest]) {
largest = l;
}
if (r < heapSize && a[r] > a[largest]) {
largest = r;
}
if (largest != i) {
swap(a[i], a[largest]);
maxHeapify(a, largest, heapSize);
}
}
void buildMaxHeap(vector<int>& a, int heapSize) {
for (int i = heapSize / 2; i >= 0; --i) {
maxHeapify(a, i, heapSize);
}
}
int findKthLargest(vector<int>& nums, int k) {
int heapSize = nums.size();
buildMaxHeap(nums, heapSize);
for (int i = nums.size() - 1; i >= nums.size() - k + 1; --i) {
swap(nums[0], nums[i]);
--heapSize;
maxHeapify(nums, 0, heapSize);
}
return nums[0];
}
};
未解决:
- 187. 重复的DNA序列
- 28. 找出字符串中第一个匹配项的下标
- 9. 回文数(使用滑动窗口法)