一,基础算法(leetcode和acwing)
一,快速排序
模板:![image](https://i-blog.csdnimg.cn/blog_migrate/14e477e634efd3f8f51c41f03991dd6a.jpeg)
void quick_sort(int q[],int l,int r)
{
if (l >= r) return;
int i = l - 1; int j = r + 1; int x = q[l + r >> 1];
while (i < j)
{
do(i++); while (q[i] < x);
do(j--); while (q[j] > x);
if (i < j) swap(q[i], q[j]);
}
quick_sort(q, l, j); quick_sort(q, j + 1, r);
}
习题1:912. 排序数组 - 力扣(LeetCode)
ac模板代码:
class Solution {
public:
void quick_sort(vector<int>& q, int l, int r)
{
if (l >= r) return;
int i = l - 1, j = r + 1, x = q[l + r >> 1];
while (i < j)
{
do i ++ ; while (q[i] < x);
do j -- ; while (q[j] > x);
if (i < j) swap(q[i], q[j]);
}
quick_sort(q, l, j), quick_sort(q, j + 1, r);
}
vector<int> sortArray(vector<int>& nums)
{
quick_sort(nums,0,(int)nums.size()-1);
return nums;
}
};
随机数优化模板代码:
class Solution {
public:
int Get_Random(vector<int>& nums,int left,int right)
{
int r=rand();
return nums[r%(right-left+1)+left];//r%(right-left+1)表示区间0,n-1 其中+left是指加上偏移量
}
void quick_sort(vector<int>& q, int l, int r)
{
if (l >= r) return;
//数组分三块
int x=Get_Random(q,l,r);
int i = l - 1, j = r + 1;
while (i < j)
{
do i ++ ; while (q[i] < x);
do j -- ; while (q[j] > x);
if (i < j) swap(q[i], q[j]);
}
quick_sort(q, l, j), quick_sort(q, j + 1, r);
}
vector<int> sortArray(vector<int>& nums)
{
srand(time(NULL));//start_rand:开始随机,种随机数的种子
quick_sort(nums,0,(int)nums.size()-1);
return nums;
}
};
基于分治思想的代码(看看就好):
class Solution {
public:
int Get_Random(vector<int>& nums,int left,int right)
{
int r=rand();
return nums[r%(right-left+1)+left];//r%(right-left+1)表示区间0,n-1 其中+left是指加上偏移量
}
void quick_sort(vector<int>& nums, int l, int r)
{
if (l >= r) return;
//数组分三块
int x=Get_Random(nums,l,r);
int i=l,left = l - 1, right = r + 1;
while (i < right)
{
if(nums[i]<x) swap(nums[++left],nums[i++]);
else if(nums[i]==x) i++;
else swap(nums[--right],nums[i]);
}
//[l,left][left+1,right-1][right,r]
quick_sort(nums, l, left), quick_sort(nums, right, r);
}
vector<int> sortArray(vector<int>& nums)
{
srand(time(NULL));//start_rand:开始随机,种随机数的种子
quick_sort(nums,0,(int)nums.size()-1);
return nums;
}
};
习题2:75. 颜色分类 - 力扣(LeetCode)
ac模板
class Solution {
public:
void quick_sort(vector<int>& q, int l, int r)
{
if (l >= r) return;
int i = l - 1, j = r + 1, x = q[l + r >> 1];
while (i < j)
{
do i ++ ; while (q[i] < x);
do j -- ; while (q[j] > x);
if (i < j) swap(q[i], q[j]);
}
quick_sort(q, l, j), quick_sort(q, j + 1, r);
}
void sortColors(vector<int>& nums)
{
quick_sort(nums,0,(int)nums.size()-1);
}
};
三路分治
class Solution {
public:
void sortColors(vector<int>& nums)
{
int left=-1,right=nums.size(),i=0;
while(i<right)
{
if(nums[i]==0)
{
left++;
swap(nums[i],nums[left]);
i++;//这里++因为确保了前面都是0或者1了
}
else if(nums[i]==1) i++;
else if(nums[i]==2)
{
right--;
swap(nums[i],nums[right]);
//这里i不能加加,因为i依旧是一个代扫描的元素,比如交换到这里的是0
}
}
}
};
习题3:Topk问题----215. 数组中的第K个最大元素 - 力扣(LeetCode)
class Solution {
public:
int quick_sort(vector<int>& q,int l,int r,int k)
{
if (l >= r) return q[l];
int i = l - 1; int j = r + 1; int x = q[l + r >> 1];
while (i < j)
{
do(i++); while (q[i] < x);
do(j--); while (q[j] > x);
if (i < j) swap(q[i], q[j]);
}
//[l,j] [j+1,r]
int c=r-j;
if(c>=k) return quick_sort(q,j+1,r,k);
else return quick_sort(q,l,j,k-c);
}
int findKthLargest(vector<int>& nums, int k)
{
return quick_sort(nums,0,nums.size()-1,k);
}
};
[外链图片转存中…(img-L7kdubqs-1711020432944)]
class Solution {
public:
int quick_sort(vector<int>& q,int l,int r,int k1)
{
if (l >= r) return q[k1];
int i = l - 1; int j = r + 1; int x = q[(l + r) >> 1];
while (i < j)
{
do(i++); while (q[i] < x);
do(j--); while (q[j] > x);
if (i < j) swap(q[i], q[j]);
}
//[l,j] [j+1,r]
//k1
if(k1>=j+1) return quick_sort(q,j+1,r,k1);
else return quick_sort(q,l,j,k1);
}
int findKthLargest(vector<int>& nums, int k)
{
return quick_sort(nums,0,nums.size()-1,nums.size()-k);
}
};
[外链图片转存中…(img-QCKHEupA-1711020432945)]
习题4:Topk前问题—LCR 159. 库存管理 III - 力扣(LeetCode)
注意这里返回的写法值得学习
class Solution {
public:
int get_rand(vector<int>& nums,int l,int r)
{
return nums[rand()%(r-l+1)+l];
}
void quick_select(vector<int>& nums,int l,int r,int k)
{
if(l>=r) return;
int i=l-1,j=r+1,x=get_rand(nums,l,r);
while(i<j)
{
do i++;while(nums[i]<x);
do j--;while(nums[j]>x);
if(i<j) swap(nums[i],nums[j]);
}
int c=j-l+1;
if(k<=c) quick_select(nums,l,j,k);
else quick_select(nums,j+1,r,k-c);
}
vector<int> inventoryManagement(vector<int>& stock, int cnt)
{
srand(time(NULL));
quick_select(stock,0,stock.size()-1,cnt);
return{stock.begin(),stock.begin()+cnt};
}
};
二,归并排序(基于分治思想的归并排序)
[外链图片转存中…(img-3zoQq91N-1711020432946)]
非常类似于二叉树的后序遍历
每一次确定分界点,分组递出,最后回归合并。
模板:
助记:
-
出去条件+中间值
-
分组递归
-
进行排序,1+3+2
-
复刻
const int N = 100001;
int q[N], tmp[N];
void merge_sort(int q[], int l, int r)
{
if (l >= r) return;
//1.选择中间点来划分区间
int mid = l + r >> 1;
//分组递归
merge_sort(q, l, mid);
merge_sort(q, mid + 1, r);
//进行排序(合并两个有序数组)
int k = 0, i = l, j = mid + 1;
while (i <= mid && j <= r)//主排序
if (q[i] <= q[j]) tmp[k++] = q[i++];
else tmp[k++] = q[j++];
//tmp[k++]=q[i]<=q[j]?q[i++]:q[j++]
while (i <= mid) tmp[k++] = q[i++];//将左半边排好顺序的数组剩余的放入
while (j <= r) tmp[k++] = q[j++];//将右半边排好顺序的数组剩余的放入
//复刻
for (i = l, j = 0; i <= r; i++, j++) q[i] = tmp[j];
}
[外链图片转存中…(img-hwgZe8Xp-1711020432946)]
习题1:787. 归并排序 - AcWing题库
#include<iostream>
using namespace std;
const int N=100001;
int tmp[N],a[N];
void merge_sort(int q[],int l,int r)
{
if(l>=r) return ;
int mid=l+r>>1;
merge_sort(q,l,mid);merge_sort(q,mid+1,r);
//进行处理
int k=0,i=l,j=mid+1;
while(i<=mid && j<=r)
{
if(q[i]<=q[j]) tmp[k++]=q[i++];
else tmp[k++]=q[j++];
}
while(i<=mid) tmp[k++]=q[i++];
while(j<=r) tmp[k++]=q[j++];
//赋值操作
for(i=l,j=0;i<=r;i++,j++) q[i]=tmp[j];
}
int main()
{
int n;cin>>n;
for(int i=0;i<n;i++)
{
cin>>a[i];
}
merge_sort(a,0,n-1);
for(int i=0;i<n;i++)
{
cout<<a[i]<<" ";
}
return 0;
}
习题2:912. 排序数组 - 力扣(LeetCode)
class Solution {
public:
void merge_sort(vector<int>& q, int l, int r)
{
if (l >= r) return;
int mid = (l + r) >> 1;
//分组递归
merge_sort(q, l, mid);
merge_sort(q, mid + 1, r);
//进行排序
vector<int> tmp(r-l+1);
int k = 0, i = l, j = mid + 1;
while (i <= mid && j <= r)//主排序
if (q[i] <= q[j]) tmp[k++] = q[i++];
else tmp[k++] = q[j++];
while (i <= mid) tmp[k++] = q[i++];//将左半边排好顺序的数组剩余的放入
while (j <= r) tmp[k++] = q[j++];//将右半边排好顺序的数组剩余的放入
//复刻
for (i = l, j = 0; i <= r; i++, j++) q[i] = tmp[j];
}
vector<int> sortArray(vector<int>& nums)
{
merge_sort(nums, 0,nums.size()-1);
return nums;
}
};
class Solution {
public:
vector<int> tmp;
void merge_sort(vector<int>& nums,int l,int r)
{
if(l>=r) return ;
int mid=(l+r)>>1;
//分组递归
merge_sort(nums,l,mid);
merge_sort(nums,mid+1,r);
//排序合并1+3+2
int k=0,i=l,j=mid+1;
while(i<=mid&&j<=r)
{
if(nums[i]<=nums[j]) tmp[k++]=nums[i++];
else tmp[k++]=nums[j++];
}
while(i<=mid) tmp[k++]=nums[i++];
while(j<=r) tmp[k++]=nums[j++];
//复刻
for(int i=l,j=0;i<=r;i++,j++)
{
nums[i]=tmp[j];
}
}
vector<int> sortArray(vector<int>& nums)
{
tmp.resize(nums.size());
merge_sort(nums,0,nums.size()-1);
return nums;
}
};
习题3:LCR 170. 交易逆序对的总数 - 力扣(LeetCode)
[外链图片转存中…(img-hrBiQ9Td-1711020432947)]
class Solution {
public:
vector<int> tmp;
int merge_sort(vector<int>& record,int l,int r)
{
if(l>=r) return 0;
int mid=(l+r)>>1;
int commit=0;
commit+=merge_sort(record,l,mid);
commit+=merge_sort(record,mid+1,r);
int k=0,i=l,j=mid+1;
while(i<=mid&&j<=r)
{
if(record[i]<=record[j])
{
tmp[k++]=record[i++];
}
else
{
commit+=mid-i+1;
tmp[k++]=record[j++];
}
}
while(i<=mid) tmp[k++]=record[i++];
while(j<=r) tmp[k++]=record[j++];
for(int i=l,j=0;i<=r;i++,j++)
{
record[i]=tmp[j];
}
return commit;
}
int reversePairs(vector<int>& record)
{
//暴力:O(N^2)
//归并排序思想
tmp.resize(record.size()+1);
return merge_sort(record,0,record.size()-1);
}
};
class Solution {
public:
vector<int> tmp;
int merge_sort(vector<int>& record,int l,int r)
{
if(l>=r) return 0;
int mid=(l+r)>>1;
int commit=0;
commit+=merge_sort(record,l,mid);
commit+=merge_sort(record,mid+1,r);
int k=0,i=l,j=mid+1;
while(i<=mid&&j<=r)
{
if(record[i]<=record[j])
{
tmp[k++]=record[j++];
}
else
{
commit+=r-j+1;
tmp[k++]=record[i++];
}
}
while(i<=mid) tmp[k++]=record[i++];
while(j<=r) tmp[k++]=record[j++];
for(int i=l,j=0;i<=r;i++,j++)
{
record[i]=tmp[j];
}
return commit;
}
int reversePairs(vector<int>& record)
{
//暴力:O(N^2)
//归并排序思想
tmp.resize(record.size()+1);
return merge_sort(record,0,record.size()-1);
}
};
习题4:315. 计算右侧小于当前元素的个数 - 力扣(LeetCode)
[外链图片转存中…(img-r3hWikYX-1711020432948)]
错误代码及问题
class Solution {
public:
vector<int> tmp1,tmp2;
void merge_sort(vector<int>& ret,vector<int>& nums,vector<int>& index,int l,int r)
{
if(l>=r) return ;
int mid=(l+r)>>1;
merge_sort(ret,nums,index,l,mid);merge_sort(ret,nums,index,mid+1,r);
int k=0,i=l,j=r;
while(i<=mid&&j<=r)
{
if(nums[i]<=nums[j])
{
tmp1[k++]=nums[j++];
tmp2[k++]=j++;
}
else
{
ret[index[i]]+=r-j+1;
tmp1[k++]=nums[i++];
tmp2[k++]=i++;
}
}
while(i<=mid)
{
tmp1[k++]=nums[i++];
tmp2[k++]=i++;
}
while(j<=r)
{
tmp1[k++]=nums[j++];
tmp2[k++]=j++;
}
for(int i=0,j=l;j<=r;j++,i++)
{
nums[j]=tmp1[i];
index[j]=tmp2[i];
}
}
vector<int> countSmaller(vector<int>& nums)
{
tmp1.resize(nums.size());tmp2.resize(nums.size());
vector<int> index(nums.size());
vector<int> ret(nums.size());
merge_sort(ret,nums,index,0,nums.size()-1);
return ret;
}
};
错误1:没有对index进行初始化(初始化下表)
错误2:对k和i/j加了两次
错误3:index要写明白
正确ac代码
class Solution {
public:
vector<int> tmp1,tmp2;
void merge_sort(vector<int>& ret,vector<int>& nums,vector<int>& index,int l,int r)
{
if(l>=r) return ;
int mid=(l+r)>>1;
merge_sort(ret,nums,index,l,mid);merge_sort(ret,nums,index,mid+1,r);
int k=0,i=l,j=mid+1;
while(i<=mid&&j<=r)
{
if(nums[i]<=nums[j])
{
tmp1[k]=nums[j];
tmp2[k++]=index[j++];
}
else
{
ret[index[i]]+=r-j+1;
tmp1[k]=nums[i];
tmp2[k++]=index[i++];
}
}
while(i<=mid)
{
tmp1[k]=nums[i];
tmp2[k++]=index[i++];
}
while(j<=r)
{
tmp1[k]=nums[j];
tmp2[k++]=index[j++];
}
for(int i=0,j=l;j<=r;j++,i++)
{
nums[j]=tmp1[i];
index[j]=tmp2[i];
}
}
vector<int> countSmaller(vector<int>& nums)
{
tmp1.resize(nums.size());tmp2.resize(nums.size());
vector<int> index(nums.size());
//初始化index数组
for(int i=0;i<nums.size();i++)
{
index[i]=i;
}
vector<int> ret(nums.size());
merge_sort(ret,nums,index,0,nums.size()-1);
return ret;
}
};
习题5:493. 翻转对 - 力扣(LeetCode)
[外链图片转存中…(img-osWoa5aU-1711020432948)]
[外链图片转存中…(img-eMlUhxql-1711020432949)]
[外链图片转存中…(img-Ru1zqwUT-1711020432950)]
class Solution {
public:
int tmp[50010];
int merge_sort(vector<int>& nums,int l,int r)
{
if(l>=r) return 0;
int ret=0;
int mid=(l+r)>>1;
ret+=merge_sort(nums,l,mid);
ret+=merge_sort(nums,mid+1,r);
int k=0,cur1=l,cur2=mid+1;
while(cur1<=mid)
{
while(cur2<=r&&nums[cur2]>=nums[cur1]/2.0) cur2++;
if(cur2>r)
break;
ret+=r-cur2+1;
cur1++;
}
cur1=l,cur2=mid+1;
while(cur1<=mid&&cur2<=r)
tmp[k++]=nums[cur1]<=nums[cur2]?nums[cur2++]:nums[cur1++];
while(cur1<=mid) tmp[k++]=nums[cur1++];
while(cur2<=r) tmp[k++]=nums[cur2++];
for(int j=l,i=0;j<=r;j++,i++)
nums[j]=tmp[i];
return ret;
}
int reversePairs(vector<int>& nums)
{
int size_=nums.size();
return merge_sort(nums,0,size_-1);
}
};
class Solution {
public:
int tmp[50010];
int merge_sort(vector<int>& nums,int l,int r)
{
if(l>=r) return 0;
int ret=0;
int mid=(l+r)>>1;
ret+=merge_sort(nums,l,mid);
ret+=merge_sort(nums,mid+1,r);
int k=0,cur1=l,cur2=mid+1;
while(cur2<=r)
{
while(cur1<=mid&&nums[cur2]>=nums[cur1]/2.0) cur1++;
if(cur1>mid)
break;
ret+=mid-cur1+1;
cur2++;
}
cur1=l,cur2=mid+1;
while(cur1<=mid&&cur2<=r)
tmp[k++]=nums[cur1]>=nums[cur2]?nums[cur2++]:nums[cur1++];
while(cur1<=mid) tmp[k++]=nums[cur1++];
while(cur2<=r) tmp[k++]=nums[cur2++];
for(int j=l,i=0;j<=r;j++,i++)
nums[j]=tmp[i];
return ret;
}
int reversePairs(vector<int>& nums)
{
int size_=nums.size();
return merge_sort(nums,0,size_-1);
}
};
三,二分算法模板
三零,朴素二分模板
算法原理:当一个数组具有二段性的时候
选择中间的位置与目标值作比较
算法方法:
mid的值<target -> left=mid+1
mid的值>target -> right=mid-1
mid的值==taget ->返回结果
算法模板:时间复杂度O(N)=logN;
int search(vector<int>& nums, int target)
{
int left=0,right=nums.size()-1;
while(left<=right)
{
int mid=left+(right-left)/2;
if(nums[mid]<target) left=mid+1;
else if(nums[mid]>target) right=mid-1;
else return mid;
}
return -1;
}
习题:704. 二分查找 - 力扣(LeetCode)
class Solution {
public:
int search(vector<int>& nums, int target)
{
int left=0,right=nums.size()-1;
while(left<=right)
{
int mid=left+(right-left)/2;
if(nums[mid]<target) left=mid+1;
else if(nums[mid]>target) right=mid-1;
else return mid;
}
return -1;
}
};
三一,整数二分算法模板
板子:查找左端点使用模板1,查找右端点使用模板2
[外链图片转存中…(img-nsysdeKP-1711020432951)]
[外链图片转存中…(img-v7MMUrqq-1711020432952)]
bool check(int x);
int SL(int l,int r)
{
while(l<r)
{
int mid=l+r>>1; //比如找3 1 3 3 4 =-、
if(check(mid)) r=mid;
else l=mid+1;
}
return l;
}
就是说mid一直满足要找的条件,更新r=mid,直到不满足此时l=mid+1,出循环,返回l,也就是满足条件的最左边
[外链图片转存中…(img-yvFxCdcj-1711020432953)]
-
当有结果的时候,也就是left=right的时候就是最终结果,没必要再进行判断了。
-
当全部>target的时候,right不断向前,当最后相等的时候没有必要判断了
-
当全部<target的时候,left不断向右运动,当最后相等的时候没有必要判断了
如果等于,那么就会进入死循环!
以数组举个例子:
[外链图片转存中…(img-13JhU1PO-1711020432953)]
bool check(int x);
int SR(int l,int r)
{
while(l<r)
{
int mid=l+r+1>>1;
if(check(mid)) l=mid;
else r=mid-1;
}
return l;//此时这个l是 mid-1][mid 的l 也就是满足不满足
}
就是说mid一直满足要找的条件,更新l=mid,直到不满足此时r=mid-1,出循环,返回r,也就是满足条件的最右边
习题1:789. 数的范围 - AcWing题库
#include<iostream>
#include<vector>
using namespace std;
vector<int> a;
int SL(int l,int r,int x)
{
while(l<r)
{
int mid=(l+r)>>1;
if(a[mid]>=x) r=mid;
else l=mid+1;
}
return l;
}
int SR(int l,int r,int x)
{
while(l<r)
{
int mid=(l+r+1)>>1;
if(a[mid]<=x) l = mid;
else r = mid - 1;
}
return l;
}
int main()
{
int m,n;cin>>m>>n;
while(m--)
{
int x;cin>>x;
a.push_back(x);
}
while(n--)
{
int y;cin>>y;
int l=SL(0,a.size()-1,y);
if(a[l]!=y) cout<<"-1 -1"<<endl;
else{
int r=SR(0,a.size()-1,y);
cout<<l<<" "<<r<<endl;
}
}
return 0;
}
[外链图片转存中…(img-bNtJybx8-1711020432954)]
[外链图片转存中…(img-aDRuo58Z-1711020432954)]
习题2 : 34. 在排序数组中查找元素的第一个和最后一个位置 - 力扣(LeetCode)
class Solution {
public:
int SL(vector<int>& nums,int l,int r,int target)
{
while(l<r)
{
int mid=l+r>>1;
if(nums[mid]>=target) r=mid;
else l=mid+1;
}
if(nums.size()==0) return -1;
else if(nums.size()!=0&&nums[l]!=target) return -1;
else{
return l;
}
}
int SR(vector<int>& nums,int l,int r,int target)
{
while(l<r)
{
int mid=l+r+1>>1;
if(nums[mid]<=target) l=mid;
else r=mid-1;
}
if(nums.size()==0) return -1;
else if(nums.size()!=0&&nums[l]!=target) return -1;
else{
return l;
}
}
vector<int> searchRange(vector<int>& nums, int target)
{
vector<int> ans;
ans.push_back(SL(nums,0,nums.size()-1,target));
ans.push_back(SR(nums,0,nums.size()-1,target));
return ans;
}
};
习题3:69. x 的平方根 - 力扣(LeetCode)
class Solution {
public:
int mySqrt(int x)
{
if(x<1) return 0;
else
{
long long left=1,right=x;
while(left<right)
{
long long mid=left+right+1>>1;
if(mid*mid<=x) left=mid;
else right=mid-1;
}
return left;
}
}
};
习题4:35. 搜索插入位置 - 力扣(LeetCode)
class Solution {
public:
int searchInsert(vector<int>& nums, int target)
{
int left=0,right=nums.size()-1;
while(left<right)
{
int mid=left+right>>1;
if(nums[mid]>=target) right=mid;
else left=mid+1;
}
if(nums[left]<target)
{
return left+1;
}
else return left;
}
};
习题5:852. 山脉数组的峰顶索引 - 力扣(LeetCode)
[外链图片转存中…(img-q7XHteTs-1711020432954)]
class Solution {
public:
int peakIndexInMountainArray(vector<int>& arr)
{
//这个数组具有二分性质
int left=1,right=arr.size()-2;
while(left<right)
{
int mid=(left+right+1)>>1;
if(arr[mid]>arr[mid-1]) left=mid;
else if(arr[mid]<arr[mid-1]) right=mid-1;
}
return left;
}
};
class Solution {
public:
int peakIndexInMountainArray(vector<int>& arr)
{
//这个数组具有二分性质
int left=1,right=arr.size()-2;
while(left<right)
{
int mid=(left+right)>>1;
if(arr[mid]>arr[mid+1]) right=mid;
else if(arr[mid]<arr[mid+1]) left=mid+1;
}
return left;
}
};
习题6:162. 寻找峰值 - 力扣(LeetCode)
[外链图片转存中…(img-bB1ZphEx-1711020432955)]
class Solution {
public:
int findPeakElement(vector<int>& nums)
{
int left=0,right=nums.size()-1;
while(left<right)
{
int mid=left+right>>1;
if(nums[mid]>nums[mid+1]) right=mid;
else if(nums[mid]<nums[mid+1]) left=mid+1;
}
return left;
}
};
习题7:153. 寻找旋转排序数组中的最小值 - 力扣(LeetCode)
class Solution {
public:
int findMin(vector<int>& nums)
{
//这个数组的规律就是先上升后下降,找左边界
int left=0,right=nums.size()-1;
while(left<right)
{
int mid=left+right>>1;
if(nums[mid]>nums[right]) left=mid+1;
else if(nums[mid]<nums[right]) right=mid;
}
return nums[left];
}
};
class Solution {
public:
int findMin(vector<int>& nums)
{
//这个数组的规律就是先上升后下降,找左边界
int left=0,right=nums.size()-1;
while(left<right)
{
int mid=left+right+1>>1;
if(nums[mid]<nums[left]) right=mid-1;
else if(nums[mid]>nums[left]) left=mid;
}
return nums[(left+1)%nums.size()];
}
};
[外链图片转存中…(img-HwqQPAuC-1711020432955)]
这个题的题解非常优秀,我们根据此题题解来写一下总结:
153. 寻找旋转排序数组中的最小值 - 力扣(LeetCode)
- 所有的情况————
[外链图片转存中…(img-kbxzGOG9-1711020432956)]
会发现如果仅仅比较左值,最小的有可能在左边,也有可能在右边,所以要反过来思考,找大的反而容易。
习题8:LCR 173. 点名 - 力扣(LeetCode)
class Solution {
public:
int takeAttendance(vector<int>& records)
{
//解法一,借助哈希容器
unordered_map<int,int> hash;
for(auto num:records)
{
hash[num]++;
}
for(int i=0;i<=records.size();i++)
{
if(hash[i]==0) return i;
}
return -1;
}
};
class Solution {
public:
int takeAttendance(vector<int>& records)
{
//解法3数学公式
int sum=0,sum_arr=0;
for(int i=0;i<=records.size();i++)
{
sum+=i;
if(i<records.size()) sum_arr+=records[i];
}
return sum-sum_arr;
}
};
class Solution {
public:
int takeAttendance(vector<int>& records)
{
int len=records.size();
int x=0;
for(int i=0;i<len;i++)
{
x =x^(records[i]^i);
}
return x^len;
}
};
class Solution {
public:
int takeAttendance(vector<int>& records)
{
int i=0,j=records.size()-1;
while(i<j)
{
int mid=(i+j)>>1;
if(records[mid]==mid) i=mid+1;
else j=mid;
}
//return i
//特判
if(records[i]==i) return i+1;
else return i;
}
};
三二,浮点数二分算法模板
模板:去一个精度比较好的浮点数
bool check(double x) {/* ... */} // 检查x是否满足某种性质
double bsearch_3(double l, double r)
{
const double eps = 1e-6; // eps 表示精度,取决于题目对精度的要求
while (r - l > eps)
{
double mid = (l + r) / 2;
if (check(mid)) r = mid;
else l = mid;
}
return l;
}
习题1:790. 数的三次方根 - AcWing题库
#include <iostream>
using namespace std;
int main()
{
double x;
cin >> x;
double l = -100, r = 100;//要查找的区间(-10000到1000取三次根就得到了-100到100这个区间范围)
while (r - l > 1e-8)
{
double mid =(l+r)/2;
if (mid * mid * mid >= x) r = mid;
else l = mid;
}
printf("%.6lf\n", l);
return 0;
}
四,高精度模板
高精度模板首先要学习数在数组中的存储方式,低位存在数组的下标小的位置
例如:123456 中 6存在a[0] 5存在a[1]这样的存储顺序
四一,高精度加法
vector<int> add(vector<int> &A, vector<int> &B)
{
if (A.size() < B.size()) return add(B, A);//默认第一个大
vector<int> C;//开vecotr c
int t = 0;//t初始化
for (int i = 0; i < A.size(); i ++ )
{
t += A[i];//一次
if (i < B.size()) t += B[i];//两次
C.push_back(t % 10);//推入
t /= 10;//carry
}
if (t) C.push_back(t);//第一位推入
return C;
}
//注意思考记忆a[i]-'0'的方法技巧
习题:
[外链图片转存中…(img-4RWQs2q2-1711020432956)]
#include <iostream>
#include <vector>
using namespace std;
vector<int> add(vector<int>& A, vector<int>& B)
{
if (A.size() < B.size()) return add(B, A);
vector<int> C;
int t = 0;
for (int i = 0; i < A.size(); i++)
{
t += A[i];//第一次
if (i < B.size()) t += B[i];//第二次
C.push_back(t % 10);
t /= 10;
}
if (t) C.push_back(t);//如果第一位有,那么推入第一位
return C;
}
int main()
{
string s1, s2;//s1=123456 s2=234567
cin >> s1 >> s2;
vector<int> is1, is2;
for (int i = s1.size() - 1; i >= 0; i--)
{
is1.push_back(s1[i] - '0');//is1[0]==6低位存储
}
for (int i = s2.size() - 1; i >= 0; i--)
{
is2.push_back(s2[i] - '0');// is2[0] == 2
}
auto C = add(is1, is2);
for (int i = C.size() - 1; i >= 0; i--) cout << C[i];//反向输出
cout << endl;
return 0;
}
四二,高精度减法
vector<int> sub(vector<int> &A, vector<int> &B)
{
vector<int> C;
for (int i = 0, t = 0; i < A.size(); i ++ )
{
t = A[i] - t;//第一次
if (i < B.size()) t -= B[i];//第二次
C.push_back((t + 10) % 10);//push
if (t < 0) t = 1;//改变t
else t = 0;
}
while (C.size() > 1 && C.back() == 0) C.pop_back();//调整处理0
return C;
}
//注意要在main中具体实现判断A>=B,否则C=-(B-A),一定要实现比较函数
习题:
#include <iostream>
#include <vector>
using namespace std;
bool cmp(vector<int>& A, vector<int>& B)
{
if (A.size() != B.size()) return A.size() > B.size();
for (int i = A.size() - 1; i >= 0; i--)
{
if (A[i] != B[i])
{
return A[i] > B[i];
}
}
return true;
}
vector<int> sub(vector<int>& A, vector<int>& B)
{
vector<int> C;
int t = 0;
for (int i = 0; i < A.size(); i++)
{
t = A[i] - t;
if (i < B.size()) t -= B[i];
C.push_back((t + 10) % 10);
if (t < 0) t = 1;
else t = 0;
}
while (C.size() > 1 && C.back() == 0) C.pop_back();
return C;
}
int main()
{
string s1, s2;//s1=123456 s2=234567
cin >> s1 >> s2;
vector<int> is1, is2;
for (int i = s1.size() - 1; i >= 0; i--)
{
is1.push_back(s1[i] - '0');//is1[0]==6低位存储
}
for (int i = s2.size() - 1; i >= 0; i--)
{
is2.push_back(s2[i] - '0');// is2[0] == 2
}
if (cmp(is1, is2))
{
auto C = sub(is1, is2);
for (int i = C.size() - 1; i >= 0; i--) cout << C[i];//反向输出
cout << endl;
}
else
{
auto C = sub(is2, is1);
cout << "-";
for (int i = C.size() - 1; i >= 0; i--) cout << C[i];//反向输出
cout << endl;
}
return 0;
}
四三,高精度乘法:高精度x低精度
vector<int> mul(vector<int> &A, int b)
{
vector<int> C;
int t = 0;
for (int i = 0; i < A.size() || t; i ++ )
{
if (i < A.size()) t += A[i] * b;//1次
C.push_back(t % 10);//push
t /= 10;//t调整
}
while (C.size() > 1 && C.back() == 0) C.pop_back();
return C;
}
习题:
#include <iostream>
#include <vector>
using namespace std;
vector<int> mul(vector<int>& A, int b)
{
vector<int> C;
int t = 0;
for (int i = 0; i < A.size() || t; i++)
{
if (i < A.size()) t += A[i] * b;//一次改
C.push_back(t % 10);//push
t /= 10;//t的调整
}
while (C.size() > 1 && C.back() == 0) C.pop_back();
return C;
}
int main()
{
vector<int> newA;
string A; int b;
cin >> A >> b;
for (int i = A.size() - 1; i >= 0; i--)
{
newA.push_back(A[i] - '0');
}
auto C = mul(newA, b);
for (int i = C.size() - 1; i >= 0; i--)
{
cout << C[i];
}
cout << endl;
return 0;
}
四四,高精度除法
vector<int> div(vector<int> &A, int b, int &r)//这里的r是余数
{
vector<int> C;
r = 0;
for (int i = A.size() - 1; i >= 0; i -- )
{
r = r * 10 + A[i];//一次
C.push_back(r / b);//push
r %= b;//r改变
}
reverse(C.begin(), C.end());//改变方向
while (C.size() > 1 && C.back() == 0) C.pop_back();//调整前导0
return C;
}
[外链图片转存中…(img-1QJlDOsg-1711020432957)]
习题:
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
vector<int> div(vector<int> A, int& b, int& r)
{
vector<int> C;
r = 0;
for (int i = A.size() - 1; i >= 0; i--)
{
r = r * 10 + A[i];
C.push_back(r / b);
r =r % b;
}
reverse(C.begin(), C.end());
while(C.size() > 1 && C.back() == 0) C.pop_back();
return C;
}
int main()
{
vector<int> newA;
string A; int b; cin >> A >> b;
for (int i = A.size() - 1; i >= 0; i--)
{
newA.push_back(A[i] - '0');
}
int r = 0; auto C=div(newA, b, r);
for (int i = C.size() - 1; i >= 0; i--)
{
cout << C[i];
}
cout <<" "<< r;
cout << endl;
return 0;
}
五,双指针算法
leetcode刷题
1.283. 移动零 - 力扣(LeetCode)
利用数组下标来充当指针
两个指针的作用:
-
cur:从左向右扫描数组,遍历数组
-
dest:已处理的区间内,非零元素的最后一个位置
class Solution {
public:
void moveZeroes(vector& nums)
{
int cur=0;int dest=-1;
while(cur<nums.size())
{
if(nums[cur]==0) cur++;
else
{
dest++;//也就是dest会停留在0的位置
swap(nums[dest],nums[cur]);
cur++;
}
}
}
};
拓展:快排的核心思想,数据划分
0 1 0 3 2
dest cur cur
dest
1 0 0 3 2
dest cur
cur
dest
1 3 0 0 2
cur
dest
1 3 2 0 0
cur出循环
2.1089. 复写零 - 力扣(LeetCode)
现根据异地操作优化得到就地操作
异地操作容易,关键是怎么进行
优化
1.异地操作:
[外链图片转存中…(img-uy9LywPY-1711020432957)]
2.初步尝试:从左向右,发现不行
3.思考:从右向左进行
[外链图片转存中…(img-nHkXdrSF-1711020432958)]
4.思考的难点就是怎么找到最后一个数呢!利用双指针算法得到dest的下标:
[外链图片转存中…(img-OtphR56M-1711020432958)]
[外链图片转存中…(img-L9SUd7ev-1711020432959)]
5.边界情况:
[外链图片转存中…(img-qUzFrpUP-1711020432959)]
针对这种边界情况,可以加入特判如果cur指向的位置是零,则让dest-1的位置为0,cur–,dest-=2;
步骤:
-
进行双指针找cur和dest
-
从后往前进行复写
class Solution {
public:
void duplicateZeros(vector& arr)
{
int cur=0,dest=-1;
while(cur<arr.size())
{
if(arr[cur]0) dest+=2;
else if(arr[cur]!=0) dest++;
if(dest>=arr.size()-1) break;
cur++;
}
//找到了cur和dest的位置
//特判
if(destarr.size())
{
arr[dest-1]=0;
cur–;
dest-=2;
}
while(cur>=0)
{
if(arr[cur]==0)
{
arr[dest–]=0;
arr[dest–]=0;
cur–;
}
else
{
arr[dest–]=arr[cur–];
}
}
}
};
3.202. 快乐数 - 力扣(LeetCode)
类似:判断链表是否有环
这里只用判断环里面的值是否为一就可以。
所以这里就可以用快慢指针的方法
方法:
-
定义快慢指针。
-
慢指针每次向后移动一步,快指针每次向后移动两步,这两个指针一定会相遇。
-
判断相遇点的值为多少。
这里非常巧妙的是用数字来充当指针
class Solution {
public:
int process(int n)
{
int sum = 0;
while(n > 0)
{
int process1=n % 10;
sum += process1*process1;
n=n / 10;
}
return sum;
}
bool isHappy(int n)
{
int slow=n,fast=n;
do
{
slow=process(slow);
fast=process(process(fast));
}while(slow!=fast);
return slow==1;
}
};
数学知识:鸽巢原理
n个巢穴,而有n+1个鸽子,则至少有一个巢穴鸽子数>1
同理推出:
例如超出int的数据
10个9 9999999999
经过process处理之后编程(9^2)*10结果为810
随便给定一个数,经过811次其中肯定有出现重复的数
4.11. 盛最多水的容器 - 力扣(LeetCode)
算法流程:
[1,8,6,2,5,4,8,3,7]
-
取两个指针分别维护下标为0的位置,和下标为.size()-1的位置
-
小的那个×区间数 ,然后让小的下标往中间移动
这是因为:区间数肯定会减小,而数值只会不变或者减小,因为取得是更小的那一个
图解:
[外链图片转存中…(img-YXGe0btO-1711020432960)]
class Solution {
public:
int maxArea(vector<int>& height)
{
//暴力解法:选出两根线,把所有的情况都枚举出来,把所有的值算出来,选出最大值,会发现超时
//双指针算法:
int i=0,j=height.size()-1,res=0;
while(i<j)
{
if(height[i]<height[j])
{
if((height[i]*(j-i))>=res) res=height[i]*(j-i);
i++;
}
else
{
if((height[j]*(j-i))>=res) res=height[j]*(j-i);
j--;
}
}
return res;
}
};
5.611. 有效三角形的个数 - 力扣(LeetCode)
-
从后往前枚举最大的数
-
维护两个指针:下标为0的数,下表为枚举数的前一个数
时间复杂度: O(N^2) 空间复杂度:O(logN)
[外链图片转存中…(img-u00TGiWc-1711020432961)]
class Solution {
public:
int triangleNumber(vector<int>& nums)
{
//排序+双指针
//让a<b<c,则只用判断a+b>c
int n=nums.size();
sort(nums.begin(),nums.end());
int cnt=0;
for(int k=n-1;k>=0;k--)
{
int i=0,j=k-1;
while(i<j)
{
if(nums[i]+nums[j]>nums[k])
{
cnt+=j-i;
j--;
}
else
{
i++;
}
}
}
return cnt;
}
};
6.LCR 179. 查找总价格为目标值的两个商品 - 力扣(LeetCode)
class Solution {
public:
vector<int> twoSum(vector<int>& price, int target)
{
//暴力枚举超时:
vector<int> a;
for(int i=0;i<price.size();i++)
{
for(int j=price.size()-1;j>=0;j--)
{
if(price[i]+price[j]==target)
{
a.push_back(price[i]);
a.push_back(price[j]);
return a;
}
}
}
return a;
}
};
class Solution {
public:
vector<int> twoSum(vector<int>& price, int target)
{
vector<int> a;
int i=0,j=price.size()-1;
while(i<j)
{
if(price[i]+price[j]>target) j--;
else if(price[i]+price[j]<target) i++;
else
{
a.push_back(price[i]);
a.push_back(price[j]);
return a;
}
}
return a;
}
};
[外链图片转存中…(img-dpUyZtoC-1711020432962)]
7.15. 三数之和 - 力扣(LeetCode)
解法:排序+双指针(同6的思路)+三重去重操作
解法思路推荐:15. 三数之和 - 力扣(LeetCode)
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums)
{
vector<vector<int>> ans;
sort(nums.begin(),nums.end());//排序
//去重操作:不用去重---int len=unique(nums.begin(),nums.end())-nums.begin();
int len=nums.size();//数组大小
for(int k=0;k<len-2;k++)
{
//直接返回结果
if(nums[k]>0) return ans;
//去重操作1
if(k>0&&nums[k]==nums[k-1]) continue;
int target=-nums[k];
int i=k+1;int j=len-1;
while(i<j)
{
if(nums[i]+nums[j]<target) i++;//同两数和
else if(nums[i]+nums[j]>target) j--;//同两数和
else if(nums[i]+nums[j]==target)
{
ans.push_back(vector<int>{nums[k],nums[i],nums[j]});
//去重操作23
while (j > i && nums[j] == nums[j - 1]) j--;
while (j > i && nums[i] == nums[i + 1]) i++;
// 找到答案时,双指针同时收缩
j--;
i++;
}
}
}
return ans;
}
};
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> result;
sort(nums.begin(), nums.end());
// 找出a + b + c = 0
// a = nums[i], b = nums[j], c = -(a + b)
for (int i = 0; i < nums.size(); i++) {
// 排序之后如果第一个元素已经大于零,那么不可能凑成三元组
if (nums[i] > 0) {
break;
}
if (i > 0 && nums[i] == nums[i - 1]) { //三元组元素a去重
continue;
}
unordered_set<int> set;
for (int j = i + 1; j < nums.size(); j++) {
if (j > i + 2
&& nums[j] == nums[j-1]
&& nums[j-1] == nums[j-2]) { // 三元组元素b去重
continue;
}
int c = 0 - (nums[i] + nums[j]);
if (set.find(c) != set.end()) {
result.push_back({nums[i], nums[j], c});
set.erase(c);// 三元组元素c去重
} else {
set.insert(nums[j]);
}
}
}
return result;
}
};
8.18. 四数之和 - 力扣(LeetCode)
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target)
{
//三数之和的思路拓展
vector<vector<int>> ans;
sort(nums.begin(),nums.end());
int len=nums.size();
for(int k=0;k<len-3;k++)
{
if(k&&nums[k]==nums[k-1]) continue;
for(int l=k+1;l<len-2;l++)
{
if(l>k+1&&nums[l]==nums[l-1]) continue;
int i=l+1;int j=len-1;
while(i<j)
{
if((long long)nums[i]+nums[j]+nums[k]+nums[l]<(long long)target) i++;
else if((long long)nums[i]+nums[j]+nums[k]+nums[l]>(long long)target) j--;
else if((long long)nums[i]+nums[j]+nums[k]+nums[l]==(long long)target)
{
ans.push_back(vector<int>{nums[k],nums[l],nums[i],nums[j]});
while(i<j&&nums[i+1]==nums[i]) i++;
while(i<j&&nums[j-1]==nums[j]) j--;
i++;
j--;
}
}
}
}
return ans;
}
};
六,滑动窗口
算法原理:
-
找两个指针进行通向移动
-
进窗口会发生什么
-
出窗口的判断(难点),以及出窗口的操作
更新结果到底在判断体内还是在哪呢?需要自己决定。
leetcode刷题:
1.209. 长度最小的子数组 - 力扣(LeetCode)
leetcode的题解非常直观:理解就是leetcode的官方题解三:
1.看暴力解法得出有单调性结论的时候
2.判断成立更新结果!然后再出窗口
3.正确性:利用单调性,省去了很多的情况
4.时间复杂度:n+n O(N)级别
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums)
{
int len=nums.size();
if(len==0) return 0;
int sum=0;int ans=INT_MAX;
int start=0,end=0;
while(end<len)
{
sum+=nums[end];
while(sum>=target)
{
sum-=nums[start];
ans =min(ans,end-start+1);
start++;
}
end++;
}
return ans==INT_MAX?0:ans;
}
};
2.3. 无重复字符的最长子串 - 力扣(LeetCode)
class Solution {
public:
int lengthOfLongestSubstring(string s)
{
int hash[128]={0};//哈希表判断重复
int i=0,j=0,len=s.size();//左右指针维护窗口
int res=0;
while(j<len)
{
hash[s[j]]++;//不断进窗口
while(hash[s[j]]>=2)//出窗口
{
hash[s[i]]--;
i++;
}
res=max(res,j-i+1);//返回值
j++;
}
return res;
}
};
3.1004. 最大连续1的个数 III - 力扣(LeetCode)
class Solution {
public:
int longestOnes(vector<int>& nums, int k)
{
//正难则反,这个正向操作很有难度,反向操作却很顺利。
//你无法反转,暴力情况过多
//所以找一个连续区间,判断其中0的个数要<=k
int i=0,j=0,len=nums.size(),cnt=0,res=0;
while(j<len)
{
if(nums[j]==0) cnt++;
while(cnt>k)
{
if(nums[i]==0)
{
cnt--;
}
i++;
}
res=max(res,j-i+1);
j++;
}
return res;
}
};
4.1658. 将 x 减到 0 的最小操作数 - 力扣(LeetCode)
也是经典正难则反,找不到两端区间的,就找中间连续区间的。
class Solution {
public:
int minOperations(vector<int>& nums, int x)
{
int sum=0;
for(int i=0;i<nums.size();i++)
{
sum+=nums[i];
}
int target=sum-x; int res=-1;
if(target<0) return -1;//注意这个区间把控问题
int i=0,j=0,len=nums.size(),tmp=0;
while(j<len)
{
tmp+=nums[j];
while(tmp>target)
{
tmp-=nums[i];
i++;
}
if(tmp==target)
{
res=max(res,j-i+1);
}
j++;
}
cout<<res;
if(res==-1)
return -1;
return nums.size()-res;
}
};
5.904. 水果成篮 - 力扣(LeetCode)
class Solution {
public:
int totalFruit(vector<int>& fruits)
{
int left=0,right=0,cnt=0,len=fruits.size();
int hash[100001]={0};
int res=0;
while(right<len)
{
if(hash[fruits[right]]==0)
{
cnt++;
}
hash[fruits[right]]++;
while(cnt>2)
{
hash[fruits[left]]--;
if(hash[fruits[left]]==0)
{
cnt--;
}
left++;
}
res=max(res,right-left+1);
right++;
}
return res;
}
};
6.438. 找到字符串中所有字母异位词 - 力扣(LeetCode)
非优化版:比较两个哈希表
class Solution {
public:
bool check(int hash1[ ],int hash2[ ])
{
bool flag=1;
for(int i=0;i<26;i++)
{
if(hash1[i]!=hash2[i]) flag=0;
}
return flag;
}
vector<int> findAnagrams(string s, string p)
{
//它奶奶的终于过了,debug了1h
vector<int> res;
int hash1[26]={0};
for(auto ch:p) hash1[ch-'a']++;
int hash2[26]={0};
int len=p.size();
int left=0,right=0;
while(right<s.size())
{
hash2[s[right]-'a']++;
if(right-left+1>len)//判断是否超过了p的长度
{
hash2[s[left]-'a']--;//去除这一个
left++;
}
if(check(hash1,hash2)) res.push_back(left);
right++;
}
return res;
}
};
使用了cnt来进行优化
class Solution {
public:
vector<int> findAnagrams(string s, string p)
{
//它奶奶的终于过了,debug了1h
vector<int> res;
int hash1[26]={0};
for(auto ch:p) hash1[ch-'a']++;
int hash2[26]={0};
int len=p.size();
int left=0,right=0,cnt=0;
while(right<s.size())
{
hash2[s[right]-'a']++;
if(hash2[s[right]-'a']<=hash1[s[right]-'a']) cnt++;//符合条件的字符就会让计数器+1,多的不会加,异类字符不会加
if(right-left+1>len)//化繁杂为简单->判断是否超过了p的长度
{
if(hash2[s[left]-'a']<=hash1[s[left]-'a']) cnt--;
hash2[s[left]-'a']--;//去除这一个
left++;
}
if(cnt==len) res.push_back(left);
right++;
}
return res;
}
};
7.30. 串联所有单词的子串 - 力扣(LeetCode)
class Solution {
public:
vector<int> findSubstring(string s, vector<string>& words)
{
//从438题一个个字符->一个个字符串
//滑动窗口+哈希表(<string,int>)
vector<int> ans;
unordered_map<string,int> hash1;
for(auto&s :words) hash1[s]++;
int len=words[0].size();
//word里面的长度都相同,right每次增加size的长度,并且把size长度的字符串传入到哈希表中
//小tip:barfoothefoobarman 从b开始执行滑动窗口 a开始执行滑动窗口 r开始执行滑动窗口
for(int i=0;i<len;i++)
{
int left=i,right=i,cnt=0;unordered_map<string,int> hash2;
while(right+len<=s.size())
{
//进窗口,维护count
string in=s.substr(right,len);
hash2[in]++;
if(hash2[in]<=hash1[in]) cnt++;
//判断
if(right-left+1>len*words.size())
{
//出窗口+维护cnt
string out=s.substr(left,len);
if(hash2[out]<=hash1[out])//判断有效单词
{
cnt--;
}
hash2[out]--;
left+=len;
}
//更新结果
if(cnt==words.size())
{
ans.push_back(left);
}
right+=len;
}
}
return ans;
}
};
//为什么慢呢?是因为哈希表当检索不存在时,会创建一个,所以时间有消耗
//可以hash1.count(in)
//看看有没有,进行加速
8.76. 最小覆盖子串 - 力扣(LeetCode)
class Solution {
public:
unordered_map<char,long long> hash1,hash2;
bool check(unordered_map<char, long long>& hash1,unordered_map<char, long long>& hash2)
{
for (const auto& entry : hash1)
{
char key = entry.first;
long long value = entry.second;
if (hash2.find(key) == hash2.end() || hash2.at(key) < value)
{
return false;
}
}
//当每个元素都大于等于的时候,返回true
return true;
}
string minWindow(string s, string t)
{
//完成对hash1的传递
for(auto ch:t)
{
hash1[ch]++;
}
long long left=0,right=0,cnt=INT_MAX;
string ans;
while(right<s.size())
{
char in=s[right];hash2[in]++;//进窗口
while(check(hash1,hash2))//判断:当hash2中的元素大于等于hash1中的元素对应值的时候
{
if(right-left+1<cnt)
{
ans=s.substr(left,right-left+1);
cnt=right-left+1;
}
char out=s[left++];hash2[out]--;
}
right++;
}
return ans;
}
};
class Solution {
public:
unordered_map <char, int> ori, cnt;
bool check()
{
for (const auto &p: ori)
{
if (cnt[p.first] < p.second)
{
return false;
}
}
return true;
}
string minWindow(string s, string t) {
for (const auto &c: t)
{
++ori[c];
}
int l = 0, r = 0;
int len = INT_MAX, ansL = -1, ansR = -1;
while (r < int(s.size()))
{
if (ori.find(s[r]) != ori.end()) //能在ori中找到
{
++cnt[s[r]];
}
while (check() && l <= r) //检查是否刚好满足
{
if (r - l + 1 < len)
{
len = r - l + 1;
ansL = l;
}
if (ori.find(s[l]) != ori.end()) //如果left代表的值是有效字符
{
--cnt[s[l]];//--
}
++l;//移动l
}
r++;
}
return ansL == -1 ? string() : s.substr(ansL, len);//这里string就是""
}
};
class Solution {
public:
string minWindow(string s, string t)
{
//hash1的处理
int hash1[128]={0};int kinds=0;
for(auto ch:t)
{
if(hash1[ch]++==0) kinds++;
//这里即使不满足条件也会让hash1[ch++],这很关键
}
//
int i=0,j=0,cnt=0;int hash2[128]={0};
int minlen=INT_MAX,begin=-1;
while(j<s.size())
{
//进窗口
char in=s[j];
hash2[in]++;
if(hash2[in]==hash1[in]) cnt++;//满足一个字符种类的条件,就让这个种类的计数器+=1
while(cnt==kinds)//判断条件
{
if(j-i+1<minlen)//更新结果
{
minlen=j-i+1;
begin=i;
}
//出窗口操作
char out=s[i];
if(hash2[out]==hash1[out]) cnt--;//注意这个细节
hash2[out]--;
i++;
}
j++;
}
if(begin==-1) return "";
else return s.substr(begin,minlen);
}
};
七,前缀和
一维模板/公式:
- 预处理前缀和数组
dp[i]或者S[i]表示[1,i]区间内所有元素的和
dp[i]=dp[i-1]+arr[i]
- 使用前缀和数组
dp[区间右端]-dp[区间左端-1]
[外链图片转存中…(img-rJmRnI3i-1711020432963)]
二维模板/公式:
- 预处理前缀和数组
dp[x,y]或者S[x,y]表示[x,y]区间内所有元素的和
dp[x,y]=dp[x-1,y]+dp[x,y-1]-dp[x-1,y-1]+arr[x,y]
- 使用前缀和数组
dp[x2,y2]-dp[x1-1,y2]-dp[x2,y1-1]+dp[x1-1,y1-1]
[外链图片转存中…(img-sPwMW7y4-1711020432963)]
后缀和模板:看习题3和习题4
习题:
1.【模板】前缀和_牛客题霸_牛客网 (nowcoder.com)
#include <iostream>
using namespace std;
long long arr[100010],dp[100010];
int main()
{
long long a, b;cin>>a>>b;
for(int i=1;i<=a;i++)
{
cin>>arr[i];
dp[i]=dp[i-1]+arr[i];
}
while(b--)
{
long long c,d;cin>>c>>d;
cout<<dp[d]-dp[c-1]<<endl;
}
}
2.【模板】二维前缀和_牛客题霸_牛客网 (nowcoder.com)
#include <iostream>
using namespace std;
#include <iostream>
using namespace std;
long long arr[1010][1010],dp[1010][1010];
int main()
{
int r,c,q;cin>>r>>c>>q;
for(int i=1;i<=r;i++)
{
for(int j=1;j<=c;j++)
{
cin>>arr[i][j];
dp[i][j]=dp[i-1][j]+dp[i][j-1]-dp[i-1][j-1]+arr[i][j];
}
}
while(q--)
{
long long x1,y1,x2,y2;cin>>x1>>y1>>x2>>y2;
cout<<dp[x2][y2]-dp[x1-1][y2]-dp[x2][y1-1]+dp[x1-1][y1-1]<<endl;
}
}
// 64 位输出请用 printf("%lld")
3.724. 寻找数组的中心下标 - 力扣(LeetCode)
class Solution {
public:
int pivotIndex(vector<int>& nums)
{
//其实就是计算 dp[len]-dp[i]==dp[i-1] 是否相等
//预处理
int len=nums.size();
vector<int> dp(len+1);
for(int i=1;i<=len;i++)
{
dp[i]=dp[i-1]+nums[i-1];
}
//执行
for(int i=1;i<=len;i++)
{
if(dp[len]-dp[i]==dp[i-1]) return i-1;
}
return -1;
}
};
class Solution {
public:
int pivotIndex(vector<int>& nums)
{
//预处理:前缀和fdp[i]表示[0,i-1] 区间 所有元素的和,后缀和gpd[i] 表示[i+1,len-1] 区间所有元素的和
int len=nums.size();
vector<int> fdp(len+1),gdp(len+1);
for(int i=1;i<=len;i++)
{
fdp[i]=fdp[i-1]+nums[i-1];
}
for(int i=len-2;i>=0;i--)
{
gdp[i]=gdp[i+1]+nums[i+1];
}
//执行
for(int i=0;i<len;i++)
{
if(gdp[i]==fdp[i]) return i;
}
return -1;
}
};
4.238. 除自身以外数组的乘积 - 力扣(LeetCode)
class Solution {
public:
vector<int> productExceptSelf(vector<int>& nums)
{
//预处理思想
//前缀积+前缀和 fdp[i]表示[0,i-1]前缀积 gdp[i]表示[i+1,n-1]后缀积
//这个就是dp方程不好找,fdp[i]=fdp[i-1]*nums[i-1],gdp[i]=gdp[i+1]*nums[i+1];
int len=nums.size();
vector<int> fdp(len+1),gdp(len+1);
fdp[0]=1;gdp[len-1]=1;
for(int i=1;i<len;i++) //fdp[0] ---> fdp[len-1];//表示那个数组的前缀和
{
fdp[i]=fdp[i-1]*nums[i-1];
}
for(int i=len-2;i>=0;i--) //gdp[0] ---> gdp[len-1];//表示那个数组的后缀和
{
gdp[i]=gdp[i+1]*nums[i+1];
}
//那么答案就是前缀积*后缀积
//细节:fdp[1]=1 gdp[len-1]=0;
vector<int> ans;
for(int i=0;i<len;i++)
{
ans.push_back(gdp[i]*fdp[i]);
}
return ans;
}
};
5.560. 和为 K 的子数组 - 力扣(LeetCode)
[外链图片转存中…(img-0XqpL8tZ-1711020432964)]
hash.count()
初步分析:
- 通过暴力枚举遍历结果是O(N^2)
进一步分析:
-
利用双指针来优化,但是数组中有负数,所以不管是前缀和还是原数组都不具有单调性,无法使用双指针来进行优化。
-
利用前缀和会发现从前往后遍历前缀和数组,然后找dp[i]-k,但是在前面找的过程就要遍历,时间复杂度还是O(N^2)
最后分析:
-
所以问题在于怎么找dp[i]-k的数量
-
构建哈希表每次求了前缀和都直接在哈希表对应值的位置++就可以了
[外链图片转存中…(img-IKcbTjzZ-1711020432965)]
细节分析:
- 前缀和加入哈希表在什么时候呢?
需求:在遍历到i位置的时候,要知道0 - i-1位置的前缀和
回应:所以在进行遍历的时候让哈希表++就可以了
- 当整个前缀和恰好等于k的时候,怎么办?
在预处理的时候让hash[0]=1,恰好=0时 if(hash.count(0)) ret+=1;
-
优化:我们可以通过滚动数组的方式来进行优化空间:用一个变量sum来得到前缀和
class Solution {
public:
int subarraySum(vector& nums, int k)
{
//通过暴力枚举可以推导遍历前缀和,(从后往前)减(从前往后)但是时间复杂度也是O(N^2)
//例如:[-1,0,1,2,-2,3,-3] 前缀和:[0,-1,0,0,2,0,3,0]目标要找0
//不可以通过双指针优化,因为有负数不具有单调性
//在0 - i-1 的位置,有多少个前缀和等于sum[i]-k,但是时间复杂度是o(N^2)
//那我们可以通过哈希表来帮助<int,int>第一个存储的前缀和次数,这样到遍历的时候通过哈希表就可以
//注意1:前缀和加入哈希表的时机?在计算i位置之前,哈希表里面只保存[0,i-1]位置的前缀和
//注意2:我们不用真的创建一个前缀和数组,可以通过滚动数组的方式进行优化:用一个变量sum来标记前一个位置的前缀和
//注意3:如果整个前缀和等于k呢?找0+加自身 或者 直接hash[0]=1;
unordered_map<int,int> hash;
hash[0]=1;int sum=0;int ret=0;
for(auto x:nums)
{
sum +=x;
if(hash.count(sum-k)) ret+=hash[sum-k];
hash[sum]++;
}
return ret;
}
};
6.974. 和可被 K 整除的子数组 - 力扣(LeetCode)
补充知识:1.同余定理 2.c++中 负数%正数 的结果以及修正
1.(a-b)÷p=k……0 可以推出 a%p==b%p
例如:(26-12)÷7=2 可以推出26%7==12%7
对取余本质的理解:
[外链图片转存中…(img-CZt1Hdox-1711020432965)]
证明:
[外链图片转存中…(img-owz2BDZl-1711020432966)]
2.负数%正数 =负数 —修正—>a%p=ret ret+p
怎么进行统一:(a%p+p)%p
算法原理:基本同第五题,就是找“sum-x”需要特殊的方法
[外链图片转存中…(img-ENxW5CyK-1711020432967)]
而为了对负数也进行处理
x%k=(sum%k +k)%k
这里哈希第一个位置存放的前缀和的余数
ac代码
class Solution {
public:
int subarraysDivByK(vector<int>& nums, int k)
{
unordered_map<int,int> hash;
hash[0%k]=1;int ret=0,sum=0;
for(int i=0;i<nums.size();i++)
{
sum+=nums[i];
int r=(sum%k+k)%k;
if(hash.count(r)) ret+=hash[r];
hash[r]++;
}
return ret;
}
};
7.525. 连续数组 - 力扣(LeetCode)
将所有的0都变成-1
[外链图片转存中…(img-psiL4cKe-1711020432968)]
细节处理:
[外链图片转存中…(img-W2cAoB8O-1711020432969)]
关于最后的长度!
[外链图片转存中…(img-piQKftHI-1711020432969)]
class Solution {
public:
int findMaxLength(vector<int>& nums)
{
unordered_map<int,int> hash;
hash[0]=-1;//默认前缀和为0
int res=0;int sum=0;
for(int i=0;i<nums.size();i++)
{
sum += (nums[i]==0?-1:nums[i]);
if(hash.count(sum)) res=max(res,i-hash[sum]);
else hash[sum]=i;
}
return res;
}
};
8.1314. 矩阵区域和 - 力扣(LeetCode)
[外链图片转存中…(img-fVyfM1C4-1711020432970)]
前置知识:vector<vector<int>> shuzu(m,vector<int>(n))
//初始化行数为m,列数为n
class Solution {
public:
vector<vector<int>> matrixBlockSum(vector<vector<int>>& mat, int k)
{
//dp[i][j]=dp[i-1][j]+dp[i][j-1]-dp[i-1][j-1]+mat[i][j];
//dp[x2,y2]-dp[x1-1][y2]+dp[x2][y1-1]-dp[x1-1][y1-1];
//沿着各个方向扩展k个长度
//x1=(i-k) y1=(j-k) x2=(i+k) y2=(j+k)
//1.注意越界问题该怎么处理:令x1,y1=max(0,i/j-k)
// x2,y2=min(dp的列/行-1,i/j+k)
//2.注意这里的下标的映射关系,返回的这个ans
//p[i][j]=dp[i-1][j]+dp[i][j-1]-dp[i-1][j-1]+mat[i-1][j-1];
//这里的ans可以在max的时候映射就好
//得dp数组
int m=mat.size(),n=mat[0].size();
vector<vector<int>> dp(m+1,vector<int>(n+1));
for(int i=1;i<=m;i++)
{
for(int j=1;j<=n;j++)
{
dp[i][j]=dp[i-1][j]+dp[i][j-1]-dp[i-1][j-1]+mat[i-1][j-1];
}
}
//拿到结果
vector<vector<int>> ans(m,vector<int>(n));
for(int i=0;i<m;i++)
for(int j=0;j<n;j++)
{
int x1=max(0,i-k)+1,y1=max(0,j-k)+1;
int x2=min(m-1,i+k)+1,y2=min(n-1,j+k)+1;
ans[i][j]=dp[x2][y2]-dp[x1-1][y2]-dp[x2][y1-1]+dp[x1-1][y1-1];
}
return ans;
}
};
八,位运算
1.六大基础位运算
>>
<<
~按位取反
& 按位与 and
|按位或 or
^异否 相同为0,相异为1
2.给一个数n,确定它的二进制表示中的第x位是0还是1
n: 0 1 1 0 1 0 1 0 0 1 1
位数: 10 9 8 7 6 5 4 3 2 1 0
n>>x &1
n>>x & 1
3.将一个数n的二进制表示的第x位修改成1
n = n|(1<<x)
4.将1个数n的二进制表示的第x位,修改成0
n=n&(~(1<<x))
5.位图思想
本质:哈希表
之前是一个int数组,现在我们可以用int的二进制位
之前的 1 2 3 4都需要用到
6.提取一个数(n)二进制表示最右侧的1
lowbit方法:++n & -n++
1011101000 &
0100011000
0000001000
7.干掉一个数(n)二进制表示中最右侧的1
++n & (n-1)++
1011101000 &
1011100111
1011100000
8.运算符优先级无论是啥能加()确定
9.异或(^)运算的规律
a^0=a
a^a=0
abc=a(bc) 本质上是无进位相加
[外链图片转存中…(img-xeYH2S84-1711020432971)]
习题:
1.191. 位1的个数 - 力扣(LeetCode)
class Solution {
public:
int hammingWeight(uint32_t n)
{
int cnt=0;
for(int i=31;i>=0;i--)
{
if(n>>i & 1) cnt++;
}
return cnt;
}
};
2.338. 比特位计数 - 力扣(LeetCode)
class Solution {
public:
vector<int> countBits(int n)
{
vector<int> ans;
for(int i=0;i<=n;i++)
{
int cnt=0;
for(int j=30;j>=0;j--)
{
if(i>>j&1) cnt++;
}
ans.push_back(cnt);
}
return ans;
}
};
3.461. 汉明距离 - 力扣(LeetCode)
class Solution {
public:
int hammingDistance(int x, int y)
{
int cnt=0;
for(int i=30;i>=0;i--)
{
if((x>>i&1) ^ (y>>i&1))//相同为0,相异为1
{
cnt++;
}
}
return cnt;
}
};
class Solution {
public:
int hammingDistance(int x, int y) {
return __builtin_popcount(x ^ y);
}
};
//__builtin_popcount(x ^ y)内置计数器
4.136. 只出现一次的数字 - 力扣(LeetCode)
class Solution {
public:
int singleNumber(vector<int>& nums)
{
int ans=0;
for(int i=0;i<nums.size();i++)
{
ans ^= nums[i];
}
return ans;
}
};
5.260. 只出现一次的数字 III - 力扣(LeetCode)
class Solution {
public:
vector<int> singleNumber(vector<int>& nums)
{
//哈希表
unordered_map<int,int> hash;
for(auto& num:nums)
{
hash[num]++;
}
vector<int> ans;
for(const auto& [num,tar]:hash)
{
if(tar==1)
{
ans.push_back(num);
}
}
return ans;
}
};
[外链图片转存中…(img-7uuZzNRB-1711020432971)]
class Solution {
public:
vector<int> singleNumber(vector<int>& nums)
{
int ans=0;
for(auto& num:nums)
{
ans ^= num;
}
//将两个独特的数字分成不同的组!!!!!!
//相同的数字分成相同的组
//怎么分组?
//随便取一个:取得那一位是0的分成一组,那一位是1的分成一组
//将lowbit位是1的都异或到一起
int a=0,b=0;
//Char 25: runtime error: negation of -2147483648 cannot be represented in type 'int'; cast to an unsigned type to negate this value to itself (solution.cpp)
//int lowbit= ans & (-ans);
//解决方法1:
int lowbit=ans==INT_MIN ? ans:ans&(-ans);
for(int& num:nums)
{
if(num&lowbit)
{
a=a^num;
}
}
return {a,ans^a};
}
};
class Solution {
public:
vector<int> singleNumber(vector<int>& nums)
{
unsigned int ans=0;
for(auto& num:nums)
{
ans ^= num;
}
//将两个独特的数字分成不同的组!!!!!!
//相同的数字分成相同的组
//怎么分组?
//随便取一个:取得那一位是0的分成一组,那一位是1的分成一组
//将lowbit位是1的都异或到一起
int a=0,b=0;
//Char 25: runtime error: negation of -2147483648 cannot be represented in type 'int'; cast to an unsigned type to negate this value to itself (solution.cpp)
int lowbit= ans & (-ans);
for(int& num:nums)
{
if(num&lowbit)
{
a=a^num;
}
}
b=ans^a;
return {a,b};
}
};
6.面试题 01.01. 判定字符是否唯一 - 力扣(LeetCode)
[外链图片转存中…(img-XiTxz1EC-1711020432972)]
优化点:鸽巢原理if(len>26) return false;
class Solution {
public:
bool isUnique(string astr)
{
//解法1:使用哈希表
//解法2:创建哈希数组,模拟哈希表
//解法3:位图思想
if(astr.size()>26) return false;//鸽巢原理优化
int bitMap=0;
for(auto ch:astr)
{
int i=ch-'a';
//先判断字符是否已经出现过了
if(bitMap>>i&1==1) return false;
//将当前字符加入到哈希表中
bitMap |=(1<<i);
}
return true;
}
};
class Solution {
public:
bool isUnique(string astr)
{
if(astr.size()>26) return false;
int bitMap=0;
for(auto& ch:astr)
{
int i=ch-'a';
if(bitMap>>i&1==1) return false;//这里模拟的就是如果是1,字母是b,往右移动一位,位图中从右向左开始递增
bitMap |=(1<<i);
}
return true;
}
};
7.268. 丢失的数字 - 力扣(LeetCode)
class Solution {
public:
int missingNumber(vector<int>& nums)
{
int ans=0;
for(int i=0;i<=nums.size();i++)
{
ans^=i;
}
for(auto& num:nums)
{
ans ^=num;
}
return ans;
}
};
8.371. 两整数之和 - 力扣(LeetCode)
class Solution {
public:
int getSum(int a, int b)
{
int ans=0;
//利用异或运算进行无进位相加
for(int i=0;i<31;i++)
{
if(((a>>i&1)+(b>>i&1))==2)
{
ans=ans|(1>>(i+1));
}
ans=ans|(((a>>i&1)>>i)^((b>>i&1)>>i));
}
return ans;
}
};
//待修改错误解答
class Solution {
public:
int getSum(int a, int b) {
int ans = 0;
int carry = 0;
for (int i = 0; i < 32; ++i)
{
int bitA = (a >> i) & 1;
int bitB = (b >> i) & 1;
// 无进位相加:使用异或操作
int sum = bitA ^ bitB ^ carry;
// 计算进位:如果有两个或以上的位为 1,产生进位
carry = (bitA & bitB) | (bitA & carry) | (bitB & carry);
// 将当前位加入到结果中
ans |= (sum << i);
}
return ans;
}
};
[外链图片转存中…(img-KcBp5PcT-1711020432972)]
class Solution {
public:
int getSum(int a, int b)
{
while(b!=0)
{
unsigned int carry=(unsigned int)(a&b)<<1;
a=a^b;
b=carry;
}
return a;
}
};
9.137. 只出现一次的数字 II - 力扣(LeetCode)
所有bit位,无非就这四种情况:
[外链图片转存中…(img-1sSmhxAY-1711020432973)]
class Solution {
public:
int singleNumber(vector<int>& nums)
{
int ret=0;
for(int i=0;i<32;i++)
{
int sum=0;
for(int num:nums)//遍历nums
{
if(((num>>i)&1)==1) sum++;//单独针对num
}
if(sum%3==1)//处理
{
ret |=(1<<i);//回归位置
}
}
return ret;
}
};
10.面试题 17.19. 消失的两个数字 - 力扣(LeetCode)
解法:位运算
-
将所有的数都异或在一起,tmp
-
找到tmp中,bit位上为1的那一位(相异的为1)
我这里采用了lowbit的方法
-
根据lowbit或者diff的不同,将所有的数划分为两类来异或!
class Solution {
public:
vector missingTwo(vector& nums)
{
//丢失的数字+只出现一次的数字3的原理
//丢失的数字
int res=0;
int len=nums.size();
for(int i=1;i<=len+2;i++)
{
res=res^i;
}
for(auto& num:nums)
{
res=res ^ num;
}
//lowbit;
int lowbit=res==INT_MIN ? res:res&(-res);
int a=0,b=0;
for(auto& num:nums)
{
if(num & lowbit)
{
a=a^num;
}
}
for(int i=1;i<=len+2;i++)
{
if(i & lowbit)
{
a=a^i;
}
}
b=res^a;
return{a,b};}
};
class Solution {
public:
vector missingTwo(vector& nums)
{
1.将所有的数异或在一起
2.找出a,b中bit位不同的那一位
3.根据diff位来划分并异或
}
};
九,模拟
特点:思路简单!
-
模拟算法流程(一定要在演草纸上过一遍流程)
-
流程转变成算法
习题:
1.1576. 替换所有的问号 - 力扣(LeetCode)
class Solution {
public:
string modifyString(string s)
{
//保证不能有连续重复的字符
int len=s.size();
for(int i=0;i<len;i++)
{
if(s[i]=='?')
{
for(char ch='a';ch<='c';ch++)
{
if((i>0&&s[i-1]==ch)||(i<len-1)&&s[i+1]==ch) continue;
s[i]=ch;
break;
}
}
}
return s;
}
};
2.495. 提莫攻击 - 力扣(LeetCode)
要找规律,不要莽撞
timeSeries[i+1]-timeSeries[i]与duration作比较
class Solution {
public:
int findPoisonedDuration(vector<int>& timeSeries, int duration)
{
int ans=0;
for(int i=0;i<timeSeries.size()-1;i++)
{
if(timeSeries[i+1]-timeSeries[i]>=duration) ans+=duration;
if(timeSeries[i+1]-timeSeries[i]<duration) ans+=timeSeries[i+1]-timeSeries[i];
}
ans+=duration;
return ans;
}
};
3.6. N 字形变换 - 力扣(LeetCode)
方法1:直接模拟
class Solution {
public:
string convert(string s, int numRows) {
int n = s.length(), r = numRows;
if (r == 1 || r >= n)
{
return s;
}
int t = r * 2 - 2;//执行完一次的周期
int c = (n + t - 1) / t * (r - 1);//通过周期来确定行数这里+t-1是为了确保这个剩余的字符不会越界
vector<string> mat(r, string(c, 0));//建立矩阵
for (int i = 0, x = 0, y = 0; i < n; ++i)
{
mat[x][y] = s[i];
if (i % t < r - 1)//向下移动的情况
{
++x; // 向下移动
} else
{
--x;
++y; // 向右上移动
}
}
string ans;
for (string &row : mat) {
for (char ch : row) {
if (ch)
{
ans += ch;
}
}
}
return ans;
}
};
class Solution {
public:
string convert(string s, int numRows) {
int n=s.size();int r=numRows;
if (r == 1 || r >= n) {
return s;
}
vector<string> mat(r);
for(int i=0,x=0,t=2*r-2;i<n;i++)
{
mat[x]+=s[i];
i%t<r-1?++x:--x;
}
string ans;
for(auto& row:mat)
{
ans+=row;
}
return ans;
}
};
方法2:找规律
公差d是2n-2
第0行: 0 -> 0+d ->0+2d ->0+3d
第k行: k和d-k k+d和2d-k k+2d和3d-k
第n-1行: n-1 -> n-1+d ->n-1+2d ->n-1+3d
class Solution {
public:
string convert(string s, int numRows)
{
if(numRows==1) return s;
string ans;
int d=2*numRows-2;
for(int i=0;i<s.size();i+=d)//如果不处理numrows=1的情况,那么就会陷入无限循环
{
ans.push_back(s[i]);
}
for(int k=1;k<numRows-1;k++)
{
for(int i=k,j=d-k;i<s.size()||j<s.size();i+=d,j+=d)
{
if(i<s.size()) ans.push_back(s[i]);
if(j<s.size()) ans.push_back(s[j]);
}
}
for(int i=numRows-1;i<s.size();i+=d)
{
ans.push_back(s[i]);
}
return ans;
}
};
4.38. 外观数列 - 力扣(LeetCode)
class Solution {
public:
string countAndSay(int n)
{
//双指针的做法
if(n==1) return "1";
string ret="1";
for(int i=0;i<n-1;i++)
{
int left=0,right=0;string ans;
while(right<ret.size())
{
while(right<ret.size()&&ret[right]==ret[left])
{
right++;
}
ans+=to_string(right-left)+ret[left];
left=right;
}
ret=ans;
}
return ret;
}
};
5.1419. 数青蛙 - 力扣(LeetCode)
使用哈希表进行模拟
[外链图片转存中…(img-WRaaH12p-1711020432974)]
[外链图片转存中…(img-gNL5UMwr-1711020432974)]