算法


前言

边刷题边学

哈希表

1. 两数之和 - 力扣(LeetCode)

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        int i,j;
        for(i=0;i<nums.size()-1;i++)
        {
            for(j=i+1;j<nums.size();j++)
            {
                if(nums[i]+nums[j]==target)
                {
                   return {i,j};
                }
            }
        }
        return {i,j};
    };
};

通过两层for循环直接比对,但是时间复杂度大于使用哈希表

class Soution{
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        map<int,int>a;//建立hash表
        vector<int> b(2,-1);//用来接收数据
        for(int i = 0;i<nums.size();i++){
            a.insert(pair<int,int>(nums[i],i));//i是value,*it是key
        }//将数据存入哈希表
        for(int i=0;i<nums.size();i++)
        {
            if(a.count(target-nums[i])>0&&(a[target-nums[i]]!=i))
            //a.count()查找map中是否存在key值,存在返回1
            //a[target-nums[i]]!=i这句的效果在于如果map表中出现了重读的key值,则这个key值不能是本身
            //判断是否找到目标元素且目标元素不能是本身
            {
                b[0]=i;
                b[1]=a[target-nums[i]];
                break;
            }
        }
        return b;
    }
};

实现思路:

这里将数据存在哈希表中,哈希表每个key值都有一个相对于的value值

哈希表的就是用空间换时间的算法

每一个key值都有一个对应的value

leetcode第十三题

class Solution {
public:
    int romanToInt(string s) {
        int sum = 0;
        int a = 0;
        map<char,int> HashMap;
        HashMap.insert(make_pair('I',1));
        HashMap.insert(make_pair('V',5));
        HashMap.insert(make_pair('X',10));
        HashMap.insert(make_pair('L',50));
        HashMap.insert(make_pair('C',100));
        HashMap.insert(make_pair('D',500));
        HashMap.insert(make_pair('M',1000));
        for(int i = 0;i<s.size();i++){
            if(s[i] == 'I' && s[i+1] == 'V'){
                a=4;
                sum +=a;
                i++;
                continue;
            }else if(s[i] == 'I' && s[i+1] == 'X'){
                a=9;
                sum +=a;
                i++;
                continue;
            }else if(s[i] == 'X' && s[i+1] == 'L'){
                a=40;
                sum +=a;
                i++;
                continue;
            }else if(s[i] == 'X' && s[i+1] == 'C'){
                a=90;
                sum +=a;
                i++;
                continue;
            }else if(s[i] == 'C' && s[i+1] == 'D'){
                a=400;
                sum +=a;
                i++;
                continue;
            }else if(s[i] == 'C' && s[i+1] == 'M'){
                a=900;
                sum +=a;
                i++;
                continue;
            }
            sum = sum + HashMap[s[i]];
        }
        return sum;
    }
};

以上是我使用的笨方法,通过使用哈希表存取数据,然后再用条件判断来计算,这样写的话代码十分的臃肿(典型负面)

class Solution{
public:
    int romanToInt(string s) {
        int sum = 0;
        map<char,int> HashMap={
            {'I',1},
            {'V',5},
            {'X',10},
            {'L',50},
            {'C',100},
            {'D', 500},
            {'M', 1000}
        };
        for(int i = 0;i<s.size();i++){
            if(HashMap[s[i]]<HashMap[s[i+1]])
                sum -= HashMap[s[i]];
            else 
                sum += HashMap[s[i]];
        }
        return sum;
    }
};

优秀题解

这里直接通过哈希表来取值来判断,因为在一般情况下小的数字总是在大的右边,所以就是加,通过读取哈希表,只要HashMap[s[i]]<HashMap[s[i+1]],就可以说明当前是特殊情况,小的在大的左边,应该减去小的的值,累加起来就可以得到想要的答案

2512. 奖励最顶尖的 K 名学生 - 力扣(LeetCode) 哈希加排序

class Solution {
public:
    static bool cmp(pair<int,int> a, pair<int,int> b) { //在类中的自定义比较函数,必须是static类型的
        if (a.first != b.first) {
            return a.first > b.first;
        } else {
            return a.second < b.second;
        }
    }
 vector<int> topStudents(vector<string>& positive_feedback, vector<string>& negative_feedback, vector<string>& report, vector<int>& student_id, int k) {
        unordered_set<string>m1(positive_feedback.begin(),positive_feedback.end());
        unordered_set<string>m2(negative_feedback.begin(),negative_feedback.end());
        vector<pair<int,int>>ret;   
        vector<int>ans;
        for(int i = 0;i<report.size();i++){
            int sec = student_id[i];
            string word;
            istringstream ss(report[i]);
            int fir = 0;
            while (ss >> word) {
                if(m1.find(word)!=m1.end()) {
                    fir += 3;
                }else if(m2.find(word)!=m2.end()){
                    fir -= 1;
                }
            }
            ret.push_back({fir,sec});
        }
        sort(ret.begin(),ret.end(),cmp);
        for(int i = 0;i<k;i++) {
            ans.emplace_back(ret[i].second);
        }
        return ans;
    }
   
};

主要是istringstream的一个操作,它可以从一个句子中提取需要的那个word

双指针

输入一行英文判断是否是回文

//回文判断
#include<iostream>
#include<string>

using namespace std;

void test(){
    string str;
    string str1;
    string str2;
    cin>>str;
    int l=0;
    int r = str.size()-1;
    while (l<r){
        str1.insert(l,1,str[l]);
        //cout <<"str1 "<<str1[l]<<" "<<endl;
        str2.insert(l,1,str[r]);
        //cout <<"str2 "<<str2[l]<<" "<<endl;
        ++l;
        --r;
    }
    if(str1.compare(str2) == 0){
        cout<<"Y";
    }else{
        cout<<"N";
    }
}
int main(){
    test();
    return 0;
}

这里定义了两个指针,一个指向字符串的起始位置,一个指在了末位置,两个指针同时向中间移动,并将当前指向的字符存进一个字符串,跟二分查找还是蛮像的,不过好像二分查找也是通过双指针实现的,双指针算是个技巧吧。

11. 盛最多水的容器 - 力扣(LeetCode)

class Solution {
public:
    int maxArea(vector<int>& height) {
        int l = 0;
        int r = height.size()-1;
        long long ans = 0;
        while(l<=r){
            if(height[l]<height[r]) {
                ans = max(ans,(long long)height[l]*(r-l));
                l++;
            } else {
                ans = max(ans,(long long)height[r]*(r-l));
                r--;
            }
        }
        return ans;
    }
};
func maxArea(height []int) int {
    l := 0
    r := len(height)-1
    ans := 0
    for l<r {
        if height[l] < height[r] {
            ans = max(ans,height[l]*(r-l))
            l++
        } else {
            ans = max(ans,height[r]*(r-l))
            r--
        }
    }
    return ans
}

func max(a int,b int) int {
    if a>b {
        return a
    } else {
        return b
    }
}

BFS

广度优先算法

例如经典迷宫问题,在一个迷宫中输入起始位置和末位置,查找最短路径

算法思路:

先建立一个visit[] []的二维数组用于判断当前的路是否通路,全部初始化为0,走过的路赋值为1,代表不可以再走了,不是通路了

再定义一个队列用来保存当前坐标的信息,新判断的点入队,判断完成的点出队

定义坐标运动数组

int dx[4]={0,1,0,-1};//右下左上
int dy[4]={1,0,-1,0};//定义运动的坐标变化

在迷宫中的某个点要向该点的上下左右四个方位进行试探,试探通路的点就移动到该点上去

核心代码

while (!r.empty())//如果当前队列不为空的话,进入循环
    {
        int tmpx,tmpy;
        tmpx = r.front().x;
        tmpy = r.front().y;//如果当前队头的坐标点等于终点坐标的位置
        if(tmpx == endx && tmpy == endy){
            flag = 1;
            cout<<"最少的步数为"<<r.front().step;
            break;
        }
        
        //开始试探四个方向
        for(int k=0;k<4;k++){
            int x,y;
            x = r.front().x + dx[k];
            y = r.front().y + dy[k];//此时的x,y就是试探后的坐标值

            //如果试探当前的坐标点是通路且没有走过的
            if(a[x][y] == 1 && v[x][y] == 0){
                point tmpe;//将符合条件的坐标点入队
                tmpe.x = x;
                tmpe.y = y;
                tmpe.step = r.front().step+1;
                //将数据保存在临时结构体并入队
                r.push(tmpe);
                v[x][y] = 1;//成功入队后,该坐标点已经走过了就不是通路了,设置为1
            }
        }
        //试探完4个方向,将队头出队,继续判断对头的下一个元素
        r.pop();
    }

完整代码

//迷宫问题
#include<iostream>
#include<queue>
using namespace std;

int a[100][100],v[100][100]={0};//a数组用来存数据,v数组用来判断当前路是否是通路

struct point{
    int x;
    int y;
    int step;
};//定义一个结构体其中包括坐标点的信息和当前已走的步数


queue<point> r;//定义一个队列,用来保存当前的坐标步数信息

int dx[4]={0,1,0,-1};//右下左上
int dy[4]={1,0,-1,0};//定义运动的坐标变化

void test(){
    int flag = 0;//用来判断迷宫是否可以成功走出
    int n,m,startx,starty,endx,endy;
    cout<<"请分别输入迷宫的行列"<<endl;
    cin>>n>>m;
    cout<<"请输入迷宫"<<endl;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            cin>>a[i][j];
        }
    }
    cout<<"请输入分别输入起始坐标的x,y与终点坐标的x,y"<<endl;
    cin>>startx>>starty>>endx>>endy;
    
    point start;//定义刚开始位置的结构体

    start.x = startx;
    start.y = starty;
    start.step = 0;
    v[startx][starty] = 1;//把当前位置的设置为通路

    r.push(start);//将第一个点入队

    while (!r.empty())
    {
        int tmpx,tmpy;
        tmpx = r.front().x;
        tmpy = r.front().y;//如果当前队头的坐标点等于终点坐标的位置
        if(tmpx == endx && tmpy == endy){
            flag = 1;
            cout<<"最少的步数为"<<r.front().step;
            break;
        }
        
        //开始试探四个方向
        for(int k=0;k<4;k++){
            int x,y;
            x = r.front().x + dx[k];
            y = r.front().y + dy[k];

            //如果试探当前的坐标点是通路且没有走过的
            if(a[x][y] == 1 && v[x][y] == 0){
                point tmpe;
                tmpe.x = x;
                tmpe.y = y;
                tmpe.step = r.front().step+1;
                //将数据保存在临时结构体并入队
                r.push(tmpe);
                v[x][y] = 1;
            }
        }
        //一个循环结束后,将队头出队
        r.pop();
    }
    if(flag!=1){
        cout<<"查找最短路径失败";
    }
}
int main(){
    test();
    return 0;
}

回溯算法

模板

void backtracking(条件){
	if(){
		//结束判断
	}
	for(){//遍历子问题
		处理节点;
        backtracking(路径,选择列表); // 递归
        回溯,撤销处理结果
		
	}
}

排列问题

全排列

46. 全排列 - 力扣(LeetCode)

class Solution {
public:
    vector<vector<int>>ans;	//返回
    vector<int>path;		//记录结果
    void backtracking(vector<int> nums,vector<bool> cnt){
        if(path.size() == nums.size()) { //边界条件
            ans.push_back(path);
            return;
        }
        for(int i = 0;i<nums.size();i++) {//遍历子问题
            if(cnt[i] == true) continue;    //如果被遍历过
            cnt[i] = true;					
            path.push_back(nums[i]);
            backtracking(nums,cnt);
            path.pop_back();          //回溯
            cnt[i] = false;
        }
    }
    vector<vector<int>> permute(vector<int>& nums) {
        vector<bool>cnt(nums.size(),false);
        backtracking(nums,cnt);
        return ans;
    }
};
func permute(nums []int) [][]int {
    var ans [][]int
    var path []int
    cnt := make([]bool, len(nums))

    var backtracking func(nums []int, cnt []bool)
    backtracking = func(nums []int, cnt []bool) {
        if len(path) == len(nums) {
            temp := make([]int, len(path))
            copy(temp, path)
            ans = append(ans, temp)
            return
        }
        for i := 0; i < len(nums); i++ {
            if cnt[i] {
                continue
            }
            cnt[i] = true
            path = append(path, nums[i])
            backtracking(nums, cnt)
            path = path[:len(path)-1]
            cnt[i] = false
        }
    }

    backtracking(nums, cnt)
    return ans
}

组合问题

77. 组合 - 力扣(LeetCode)

class Solution {
public:
    vector<vector<int>>ans;
    vector<int>path;
    vector<vector<int>> combine(int n, int k) {
        backtracking(n,k,1);
        return ans;
    }
    void backtracking(int n,int k,int index){
        if(path.size() == k){
            ans.push_back(path);
            return;
        }
        for(int t = index;t<=n;t++){
            path.push_back(t);
            backtracking(n,k,t+1);
            path.pop_back();
        }
    }
};
func combine(n int, k int) [][]int {
    var ans [][]int
    var path []int
    var backtracking func(n int ,k int,index int)
    backtracking = func(n int ,k int,index int) {
        if len(path) == k {
            temp := make([]int ,len(path))
            copy(temp,path)
            ans = append(ans,temp)
        }
        for t := index;t<=n;t++ {
            path = append(path,t)
            backtracking(n,k,t+1)
            path = path[:len(path)-1]
        }
    }
    backtracking(n,k,1)
    return ans
}

17. 电话号码的字母组合 - 力扣(LeetCode)

class Solution {
public:
    string m[10] = {"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"}; 
    vector<string>ans;
    string path;
    void backtracking(string digits,int index){
        if(path.size() == digits.size()){
            ans.push_back(path);
            return;
        }
        int t = digits[index] - '0';
        string temp = m[t];
        for(int i = 0;i<temp.size();i++){
            path.push_back(temp[i]);
            backtracking(digits,index+1);
            path.pop_back();
        }
    }
    vector<string> letterCombinations(string digits) {
        if(digits.size() == 0){
            return {};
        }
        backtracking(digits,0);
        return ans;
    }   
    
};

动态规划

当一个问题有多个重叠的小问题,每一个状态都是由上一个状态推导而来的。

动规五部曲

  • 确定dp数组(dp table)以及下标的含义
  • 确定递推公式
  • dp数组如何初始化
  • 确定遍历顺序
  • 举例推导dp数组

01背包问题,小明有一个容量为m的书包,现有物品A,B,C三件有不同的重量和价值,求在小明书包容量下可以装的最大价值装法

//背包01问题
#include<iostream> 
#include<vector>
using namespace std;
int max(int a,int b){
    return a>b?a:b;
}
void test(){
    int num,bagweight;
    cout<<"请输入商品的数量和背包大小"<<endl;
    cin>>num>>bagweight;
    vector<int>wight(num);
    vector<int>value(num);
    cout<<"请输入商品信息(先输入体积再价值)"<<endl;
    for(int i =0;i<num;i++){
        cin>>wight[i];
        cin>>value[i];
    }

    //定义一个二维数组用于记录数据
    vector<vector<int>> dp(wight.size(),vector<int>(bagweight+1,0));
    //该dp的含义是在0~i中选取i个物品装进j容量的背包中
    //初始化第一行和第一列
    for(int j=wight[0];j<=bagweight;j++){
        dp[0][j] = value[0];
    }

    //weight数组的大小就是物品个数
    for(int i=1;i<wight.size();i++){//先遍历物品,这里因为第一行已经全部初始话所以,从i=1开始遍历
        for(int j =0;j<=bagweight;j++){//遍历背包容量
            if(j<wight[i]){//如果背包的容量小于物品的重量,就不拿i物品
                dp[i][j] = dp[i-1][j];
            }else{//拿物品
                dp[i][j] = max(dp[i-1][j],dp[i-1][j-wight[i]]+value[i]);
            }
        }
    }
    cout << dp[wight.size() - 1][bagweight] << endl;//最后一格即使最优的解
}

int main(){
    test();
    return 0;
}

leetcode70题

class Solution {
public:
    int climbStairs(int n) {
        if(n<=1){
            return n;
        }
        vector<int>a(n+1);//这里要+1因为物理空间大小和逻辑空间大小差1
        a[1] = 1;
        a[2] = 2;
        for(int i = 3;i<n;i++){
            a[i] = a[i-1] +a[i-2];
        }
        return a[n];
    }
};

Dijkstra

迪杰斯特拉算法

用于找出单源的最小路径问题

#include<iostream>
#define V 20 //顶点的最大个数
#define INFINITY 65535
typedef struct{
    int vexs[V];    // 储存图中的顶点数据
    int arcs[V][V]; //二维数组记录顶点之间的关系
    int vexnum,arcnum;  // 记录图的顶点数和弧数
}MGraph;

//根据顶点本身的数据,判断出顶点在二维数组中的位置
int LocateVex(MGraph* G,int v){
    int i=0;
    for(;i<G->vexnum;i++){
        if(v == G->vexs[i]){ 
            break;//遍历数组找到位置
        }
    }
    if(i>G->vexnum){
        std::cout<<"not find"<<std::endl;
        return -1;
    }
    return i;
}


//构造无向有权图

void Init_GM(MGraph* G){
    std::cout<<"请输入顶点数和边数"<<std::endl;
    std::cin>>G->vexnum>>G->arcnum;
    std::cout<<"请输入各个顶点"<<std::endl;

    for(int i=0;i<G->vexnum;i++){
        std::cin>>G->vexs[i];
    }

    for(int i=0;i<G->vexnum;i++){
        for(int j=0;j<G->vexnum;j++){
            G->arcs[i][j] = INFINITY; // 初始化一开始的距离
        }
    }

    std::cout<<"输入每个边的数据"<<std::endl;
    for(int i=0;i<G->arcnum;i++){
        int v1,v2,w;//边关联的两个顶点和权值
        std::cin>>v1>>v2>>w;
        int n=LocateVex(G,v1);
        int m=LocateVex(G,v2);
        if(n==-1 || m==-1) return;
        G->arcs[n][m] = w;
        G->arcs[m][n] = w;
    }
}

//迪杰斯特拉算法
void Dijkstra_minTree(MGraph G,int v0,int p[V],int d[V]){ // v0代表其实坐标点的数组下标
    int find_min[V]; // 判断各个顶点是否已经是最短路径
    for(int i=0;i<G.vexnum;i++){
        find_min[i] = 0;
        d[i] = G.arcs[v0][i];
        p[i] = 0;
    }

    d[v0] = 0;
    find_min[v0] = 1;
    int k = 0;
    for(int i=0;i<G.vexnum;i++){
        int min = INFINITY;
        for(int w=0;w<G.vexnum;w++){
            if(!find_min[w]){
                if(d[w]<min){
                    k = w;
                    min = d[w];
                }
            }
        }
        find_min[k] = 1;
        for(int w=0;w<G.vexnum;w++){
            if(!find_min[w] && (min+G.arcs[k][w] < d[w])){
                d[w] = min + G.arcs[k][w];
                p[w] = k;
            }
        }
    }
}

int main(){
    MGraph G;
    Init_GM(&G);
    int p[V];
    int d[V];
    Dijkstra_minTree(G,0,p,d);
}

两版

#include <iostream>
#include <vector>
#include <queue>
#include <limits.h>

// 使用标准命名空间
using namespace std;

// 定义边
struct Edge {
int to;      // 目标节点
int weight;  // 边权重
// 构造函数,初始化目标节点和边权重
Edge(int t, int w):to(t), weight(w){}
};

// 定义图
typedef vector<vector<Edge>> Graph;

// 定义距离数组
typedef vector<int> Distance;

// 迪杰斯特拉算法,求从起点出发到其他各个节点的最短距离
Distance dijkstra(const Graph& graph, int source) {
const int n = graph.size();     // 图中节点数目
Distance distances(n, INT_MAX); // 初始化距离数组,全为INT_MAX
distances[source] = 0;          // 初始节点到自身距离为0

// 使用小根堆维护每个节点距离起点的距离
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> heap;
heap.push(0, source);        // 将起点加入堆中,距离为0

while (!heap.empty()) {
    // 取出堆顶元素(当前距离起点最近的节点)
    pair<int, int> p = heap.top();
    heap.pop();

    int node = p.second, distance = p.first;

    // 如果该节点已被访问过,则跳过之
    if (distances[node] < distance) {
        continue;
    }

    // 更新邻居节点距离,并加入堆中
    for (const Edge& edge : graph[node]) {
        int neighbor = edge.to, weight = edge.weight;
        int new_distance = distance + weight;

        if (new_distance < distances[neighbor]) {
            distances[neighbor] = new_distance;
            heap.push(new_distance, neighbor);
        }
    }
}

return distances;   // 返回起点到各节点的最短距离数组
}

// 主函数
int main() {
// 定义示例图
Graph graph(5);     // 图中有5个节点
graph[0].push_back(Edge(1, 10));    // 0号节点到1号节点距离为10
graph[0].push_back(Edge(4, 5));     // 0号节点到4号节点距离为5
graph[1].push_back(Edge(2, 1));     // 1号节点到2号节点距离为1
graph[1].push_back(Edge(4, 2));     // 1号节点到4号节点距离为2
graph[2].push_back(Edge(3, 4));     // 2号节点到3号节点距离为4
graph[3].push_back(Edge(0, 7));     // 3号节点到0号节点距离为7
graph[3].push_back(Edge(2, 6));     // 3号节点到2号节点距离为6
graph[4].push_back(Edge(1, 3));     // 4号节点到1号节点距离为3
graph[4].push_back(Edge(2, 9));     // 4号节点到2号节点距离为9
graph[4].push_back(Edge(3, 2));     // 4号节点到3号节点距离为2

Distance distances = dijkstra(graph, 0);   // 以0号节点为起点,求最短距离

// 打印起点到各点的最短距离
for (int i = 0; i < distances.size(); i++) {
    cout << "Distance from 0 to " << i << " is " << distances[i] << endl;
}

return 0;   // 返回程序执行成功状态
}

单调栈

739. 每日温度 - 力扣(LeetCode) 单调递减

class Solution {
public:
    //单调栈模板题目
    vector<int> dailyTemperatures(vector<int>& temperatures) {
        int n = temperatures.size();
        vector<int>ret(n);
        stack<int>ss;        	//储存下标
        for(int i = 0;i<n;i++) {
            while(!ss.empty() &&  temperatures[i] > temperatures[ss.top()]){
                int j = ss.top();       
                ss.pop();
                ret[j] = i-j;
            }
            ss.push(i);
        }
        return ret;
    }
};
func dailyTemperatures(temperatures []int) []int {
    n := len(temperatures)
    ss := []int{}
    ret := make([]int,n)

    for i:=0;i<n;i++ {
        for len(ss) > 0 && temperatures[i] > temperatures[ss[len(ss)-1]] {
            j := ss[len(ss)-1]
            ss = ss[:len(ss)-1]
            ret[j] = i - j
        }
        ss = append(ss,i)
    }
    return ret
}

前后缀和

42. 接雨水 - 力扣(LeetCode) 这题解法很多

class Solution {
public:
    int trap(vector<int>& height) {
        //前,后缀和解法
        int n = height.size();
        vector<int>per(n,0);
        per[0] = height[0];
        vector<int>suf(n,0);
        suf[n-1] = height[n-1];
        int ans = 0;
        for(int i = 1;i<n;i++) {
            per[i] = max(per[i-1],height[i]);
        }
        for(int i = n-2;i>=0;i--) {
            suf[i] = max(suf[i+1],height[i]);
        }
        for(int i=0;i<n;i++)
        ans += min(per[i],suf[i]) - height[i];
        return ans;
    }
};
func trap(height []int) int {
    n := len(height)
    per := make([]int,n)
    suf := make([]int,n)
    per[0] = height[0]
    suf[n-1] = height[n-1]
    var ans int = 0

    for i:=1;i<n;i++ {
        per[i] = max(per[i-1],height[i])
    }

    for i:=n-2;i>=0;i-- {
        suf[i] = max(suf[i+1],height[i])
    }

    for i:=0;i<n;i++ {
        ans += min(per[i],suf[i]) - height[i]
    }
    return ans
}

func min(a int,b int) int {
    if(a<b) {
        return a
    }else {
        return b
    }
}

func max(a int,b int) int {
    if(a>b) {
        return a
    }else {
        return b
    }
}

前后缀积 思想是一样的

238. 除自身以外数组的乘积 - 力扣(LeetCode)

class Solution {
public:
    vector<int> productExceptSelf(vector<int>& nums) {
        int n = nums.size();
        vector<int> ans(n);
        vector<long long> pre(n);
        vector<long long> beh(n);
        pre[0] = 1; // 将pre[0]初始化为1
        beh[n-1] = 1; // 将beh[n-1]初始化为1
        // 计算pre数组
        for(int i = 1; i < n; i++){ 
            pre[i] = nums[i-1] * pre[i-1]; 
        }
        // 计算beh数组
        for(int i = n-2; i >= 0; i--){ 
            beh[i] = nums[i+1] * beh[i+1]; 
        }
        // 计算结果数组ans
        for(int i = 0; i < n; i++){
            ans[i] = pre[i] * beh[i];
        }
        return ans;
    }
};
func productExceptSelf(nums []int) []int {
    n := len(nums)
    ans := make([]int,n)
    pre := make([]int,n)
    beh := make([]int,n)
    pre[0] = 1;
    beh[n-1] = 1;
    //前缀
    for i:=1;i<n;i++ {
        pre[i] = pre[i-1]*nums[i-1]
    }
    //后缀
    for i:=n-2;i>=0;i-- {
        beh[i] = beh[i+1]*nums[i+1]
    }
    for i:=0;i<n;i++ {
        ans[i] = pre[i] * beh[i]
    }
    return ans
}

滑动窗口

3. 无重复字符的最长子串 - 力扣(LeetCode)

class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        int ans = 0;    
        int left = 0;  
        unordered_map<char,int>m;   //使用hash表记录字符的出现次数
        for(int right = 0;right<s.size();right++) {
            m[s[right]]++;          //记录
            while (m[s[right]] > 1) {   //如果出现次数大于1
                m[s[left]]--;
                left++;
            }
            ans = max(ans,right-left+1);
        }
        return ans;
    }
};
func lengthOfLongestSubstring(s string) int {
    ans := 0
    left := 0
    m := make(map[byte]int)
    for right:=0;right<len(s);right++ {
        m[s[right]]++
        for m[s[right]] > 1 {
            m[s[left]]--
            left++
        }
        ans = max(ans,right-left+1)
    }
    return ans
}
func max(a int,b int)int {
    if(a>b){
        return a
    }else {
        return b
    }
}

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

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int ans = INT_MAX;
        int l = 0;
        long long sum = 0;
        for(int r = 0;r<nums.size();r++) {
            sum+=nums[r];
            while(sum>=target){
                sum-=nums[l];
                ans = min(ans,r-l+1);
                l++;
            }
        }
        return (ans!=INT_MAX)?ans:0;
    }
};

438. 找到字符串中所有字母异位词 - 力扣(LeetCode)

class Solution {
public:
    vector<int> findAnagrams(string s, string p) {
        //hash表存储p中出现字符
        int m[26] = {0};
        int curm[26] = {0};
        //vector<int>m(26,0);
        //vector<int>curm(26,0);
        if(s.size()<p.size()) return {};
        for(auto i:p){
            m[i-'a']++;
        }
        int n = p.size();
        for(int i = 0;i<n-1;i++){
            curm[s[i]-'a']++;
        }
        vector<int>ans;
        int l = 0; 
        for(int r = n-1;r<s.size();r++){
            curm[s[r]-'a']++;
            if(!memcmp(m,curm,sizeof(m))){ //普通数组不能直接用m == curm,用普通数组主要是能优化4ms(岂不美哉)
                ans.push_back(l);
            }
            curm[s[l]-'a']--;
            l++;
        }
        return ans;
    }
};

模板

int left = 0;
int ans = 0;
for (int right = 0;right<len;right++) {
	//操作
	while(//题目条件){
		//操作
		l++
	}
}

文章作者: zhang
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 zhang !
  目录