一,基础算法(leetcode和acwing)

一,基础算法(leetcode和acwing)

一,快速排序

模板:image

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);
}

image

习题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);
    }
};

三路分治

image

image

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. 出去条件+中间值

  2. 分组递归

  3. 进行排序,1+3+2

  4. 复刻

    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)]

  1. 当有结果的时候,也就是left=right的时候就是最终结果,没必要再进行判断了。

  2. 当全部>target的时候,right不断向前,当最后相等的时候没有必要判断了

  3. 当全部<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)

  1. 所有的情况————

[外链图片转存中…(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)

利用数组下标来充当指针

两个指针的作用:

  1. cur:从左向右扫描数组,遍历数组

  2. 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;

步骤:
  1. 进行双指针找cur和dest

  2. 从后往前进行复写

    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(dest
    arr.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)

类似:判断链表是否有环

这里只用判断环里面的值是否为一就可以。

所以这里就可以用快慢指针的方法

方法:

  1. 定义快慢指针。

  2. 慢指针每次向后移动一步,快指针每次向后移动两步,这两个指针一定会相遇。

  3. 判断相遇点的值为多少。

这里非常巧妙的是用数字来充当指针

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]

  1. 取两个指针分别维护下标为0的位置,和下标为.size()-1的位置

  2. 小的那个×区间数 ,然后让小的下标往中间移动

这是因为:区间数肯定会减小,而数值只会不变或者减小,因为取得是更小的那一个

图解:

[外链图片转存中…(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)

  1. 从后往前枚举最大的数

  2. 维护两个指针:下标为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;
    }
};

六,滑动窗口

算法原理:

  1. 找两个指针进行通向移动

  2. 进窗口会发生什么

  3. 出窗口的判断(难点),以及出窗口的操作

更新结果到底在判断体内还是在哪呢?需要自己决定。

leetcode刷题:

1.209. 长度最小的子数组 - 力扣(LeetCode)

leetcode的题解非常直观:理解就是leetcode的官方题解三:

209. 长度最小的子数组 - 力扣(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);
    }
};

七,前缀和

一维模板/公式:

  1. 预处理前缀和数组

dp[i]或者S[i]表示[1,i]区间内所有元素的和

dp[i]=dp[i-1]+arr[i]

  1. 使用前缀和数组

dp[区间右端]-dp[区间左端-1]

[外链图片转存中…(img-rJmRnI3i-1711020432963)]

二维模板/公式:

  1. 预处理前缀和数组

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]

  1. 使用前缀和数组

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()

初步分析:

  1. 通过暴力枚举遍历结果是O(N^2)

进一步分析:

  1. 利用双指针来优化,但是数组中有负数,所以不管是前缀和还是原数组都不具有单调性,无法使用双指针来进行优化。

  2. 利用前缀和会发现从前往后遍历前缀和数组,然后找dp[i]-k,但是在前面找的过程就要遍历,时间复杂度还是O(N^2)

最后分析:

  1. 所以问题在于怎么找dp[i]-k的数量

  2. 构建哈希表每次求了前缀和都直接在哈希表对应值的位置++就可以了

[外链图片转存中…(img-IKcbTjzZ-1711020432965)]

细节分析:

  1. 前缀和加入哈希表在什么时候呢?

需求:在遍历到i位置的时候,要知道0 - i-1位置的前缀和

回应:所以在进行遍历的时候让哈希表++就可以了

  1. 当整个前缀和恰好等于k的时候,怎么办?

在预处理的时候让hash[0]=1,恰好=0时 if(hash.count(0)) ret+=1;

  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)

解法:位运算

  1. 将所有的数都异或在一起,tmp

  2. 找到tmp中,bit位上为1的那一位(相异的为1)

我这里采用了lowbit的方法

  1. 根据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. 模拟算法流程(一定要在演草纸上过一遍流程)

  2. 流程转变成算法

习题:

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)]

  • 27
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值