停更阶段学习算法题的一些总结

目录

  • 一星题
    • 快速排序
    • 归并排序
    • 高精度加法
    • 高精度乘法
    • 前缀和
    • 子矩阵的和(二维数组的前缀和)
    • 移除元素
    • 二分查找
    • 回文数
  • 二星题目
    • 单链表(数组模拟实现)
    • 双链表(数组实现)
    • 模拟栈
    • 模拟队列
    • 单调栈
    • 滑动窗口(单调队列)
    • KMP算法
    • Trie字符串统计
    • 最大异或对
    • 并查集
    • 连通块中点的数量(并查集的应用)
    • 堆排序
    • 模拟堆
    • BFS
      • 二叉树的范围和
      • 二叉树的层序遍历II
      • 岛屿数量(在下面的DFS中有出现)
      • 二叉树的层序遍历
    • DFS
      • 二叉树的范围和(在上面的BFS中出现过,不再重复)
      • 二叉树的层序遍历(在BFS中出现过,不再重复)
      • 二叉树的中序遍历
      • 二叉树的后序遍历
      • 括号生成
      • 子集
      • 全排列
      • 岛屿数量
    • 贪心
      • 分发饼干
    • 回溯
      • 子集(在上面的DFS中出现过,不再重复)
      • 括号生成(在上面的DFS中出现过,不再重复)
      • 组合总和III
      • 组合
    • 哈希
      • 快乐数(set)
      • 两个数组的交集(set)
      • 有效的字母异位词(数组)
      • 多数元素(数组)
    • 动态规划
      • 最大子序和
      • 三级目录

一星题

快速排序

题目链接:快速排序
停更阶段学习算法题的一些总结_第1张图片

其实这道题在停更之前的八大排序中已经涉及过了,下面来看看y总的代码。

#include 

using namespace std;

const int N = 100010;

int q[N];

void quick_sort(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);
}

int main()
{
    int n;
    scanf("%d", &n);

    for (int i = 0; i < n; i ++ ) scanf("%d", &q[i]);

    quick_sort(q, 0, n - 1);

    for (int i = 0; i < n; i ++ ) printf("%d ", q[i]);

    return 0;
}

y总的代码其实是很简便和优雅的,但由于我比较习惯之前的实现方式,所以遇到快速排序时一直都是用的我自己的那套写法。

void QuickSort1(int* a, int left, int right)
{
	if (left >= right)//当只有一个数据或是序列不存在时,不需要进行操作
		return;

	int begin = left;//L
	int end = right;//R
	int key = left;//key的下标
	while (left < right)
	{
		//right先走,找小
		while (left < right&&a[right] >= a[key])
		{
			right--;
		}
		//left后走,找大
		while (left < right&&a[left] <= a[key])
		{
			left++;
		}
		if (left < right)
		{
			swap(&a[left], &a[right]);
		}
	}
	int meet = left;//L和R的相遇点
	swap(&a[key], &a[meet]);//交换key和相遇点的值

	QuickSort1(a, begin, meet - 1);//key的左序列进行此操作
	QuickSort1(a, meet + 1, end);//key的右序列进行此操作
}

这只是快速排序的一种实现方法:Hoare法。其他的方法可以看这篇文章:八大排序


归并排序

题目链接:归并排序
停更阶段学习算法题的一些总结_第2张图片

这里也不多结束了,之前的那篇八大排序中也涉及到了此排序。
y总的代码:

#include 

using namespace std;

const int N = 1e5 + 10;

int a[N], tmp[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;
    scanf("%d", &n);
    for (int i = 0; i < n; i ++ ) scanf("%d", &a[i]);

    merge_sort(a, 0, n - 1);

    for (int i = 0; i < n; i ++ ) printf("%d ", a[i]);

    return 0;
}

自己平常写的:

void  MergeSort(int* a, int left, int right,int* temp)
{
	if (left >= right)
		return;
	int mid = left + (right - left) / 2;
	int begin1 = left, end1 = mid;
	int begin2 = mid+1, end2 = right;
	int i = left,j=0;  //i一定要写成left不能写成0,如果是递归左序列还可以适用,但是递归到右序列时就不行了
	MergeSort(a, left, mid, temp);  //这里要注意区间的划分,不然可能出现死递归
	MergeSort(a, mid + 1, right, temp);
	while (begin1 <= end1&&begin2 <= end2)
	{
		if (a[begin1] < a[begin2])
		{
			temp[i++] = a[begin1++];
		}
		else
		{
			temp[i++] = a[begin2++];
		}
	}
	while (begin1 <= end1)
	{
		temp[i++] = a[begin1++];
	}
	while (begin2 <= end2)
	{
		temp[i++] = a[begin2++];
	}
	for (j = left; j < right + 1; j++)
	{
		a[j] = temp[j];
	}
}


高精度加法

其实这就是一道模拟题,并不涉及什么算法,注意到细节也就相对容易了。
题目链接:高精度加法
停更阶段学习算法题的一些总结_第3张图片
代码实现:

#include 
#include 

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 a, b;
    vector<int> A, B;
    cin >> a >> b;
    for (int i = a.size() - 1; i >= 0; i -- ) A.push_back(a[i] - '0');
    for (int i = b.size() - 1; i >= 0; i -- ) B.push_back(b[i] - '0');

    auto C = add(A, B);

    for (int i = C.size() - 1; i >= 0; i -- ) cout << C[i];
    cout << endl;

    return 0;
}

注意:这里写代码时要时刻想清楚哪个时候vector里面存放的是正向排序,什么时候是逆序的。

由于减法其实就是加上一个负数,其实如果你愿意,也可以把减速的运算过程模拟出来,因为之前已经介绍了加法,所以减法这里就不过多介绍了。


高精度乘法

题目链接:高精度乘法
停更阶段学习算法题的一些总结_第4张图片
代码实现:

#include 
#include 

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);
        t /= 10;
    }

    while (C.size() > 1 && C.back() == 0) C.pop_back();  //用于解决前导0的问题

    return C;
}


int main()
{
    string a;
    int b;

    cin >> a >> b;

    vector<int> A;
    for (int i = a.size() - 1; i >= 0; i -- ) A.push_back(a[i] - '0');

    auto C = mul(A, b);

    for (int i = C.size() - 1; i >= 0; i -- ) printf("%d", C[i]);

    return 0;
}

注意:乘法前导0的问题。


前缀和

题目链接:前缀和
停更阶段学习算法题的一些总结_第5张图片
这道题如果用暴力解法的话,时间可能会超时。但是用了前缀和的话,从O(n)的复杂度可以变为O(1)的复杂度。下面我们先来了解一下前缀和是什么,其实这和高中学的数列的知识有些关系,前缀和其实是一个S[N]的数组,其中N为元素的个数,S[1]表示a1,S[2]表示a1+a2,S[3]表示a1+a2+a3,也就是说i是几,S[i]就是前几项的和,这里我们第一个元素在数组中下标并不是从0开始的,而是从1开始的。
代码实现:

#include 

using namespace std;

const int N = 100010;

int n, m;
int a[N], s[N];

int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i ++ ) scanf("%d", &a[i]);

    for (int i = 1; i <= n; i ++ ) s[i] = s[i - 1] + a[i]; // 前缀和的初始化

    while (m -- )
    {
        int l, r;
        scanf("%d%d", &l, &r);
        printf("%d\n", s[r] - s[l - 1]); // 区间和的计算
    }

    return 0;
}

如果你一开始并不能怎么看懂这段代码,其实你举一个示例,然后对着代码看,其实很容易理解。下面我们来提升一下难度,这里是一维数组的前缀和,下面看看二维数组的前缀和。


子矩阵的和(二维数组的前缀和)

题目链接:子矩阵的和
停更阶段学习算法题的一些总结_第6张图片
代码实现:

#include 

using namespace std;

const int N = 1010;

int n, m, q;
int s[N][N];

int main()
{
    scanf("%d%d%d", &n, &m, &q);

    for (int i = 1; i <= n; i ++ )
        for (int j = 1; j <= m; j ++ )
            scanf("%d", &s[i][j]);

    for (int i = 1; i <= n; i ++ )
        for (int j = 1; j <= m; j ++ )
            s[i][j] += s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1];  //这里自己举个比较简单的二维数组可以很简单的分析出来

    while (q -- )
    {
        int x1, y1, x2, y2;
        scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
        printf("%d\n", s[x2][y2] - s[x1 - 1][y2] - s[x2][y1 - 1] + s[x1 - 1][y1 - 1]); //对应到下面图中用荧光笔涂鸦的部分
    }

    return 0;
}

这段代码想比前面的一维数组前缀和,难度又提升了,不过大致的思路还是一样的。这里博主就不具体介绍了,因为这只是一篇用于个人总结的博客。下面附上一张我分析时的图片。
停更阶段学习算法题的一些总结_第7张图片


移除元素

题目链接:移除元素
停更阶段学习算法题的一些总结_第8张图片
代码实现:

class Solution {
public:
    int removeElement(vector<int>& nums, int val)
    {
       if(nums.size()==0)
       {
           return 0;
       }
       vector<int>::iterator begin=nums.begin();
       vector<int>::iterator end=nums.end()-1;
       while(begin<end)
       {
           while(begin<end&&(*begin)!=val)
           {
               begin++;
           }
           while(begin<end&&(*end)==val)
           {
               end--;
           }
           swap((*begin),(*end));
       }
       int sum=0;
       for(vector<int>::iterator i=nums.begin();i!=nums.end();i++)
       {
           if((*i)!=val)
           {
               ++sum;
           }
       }
       return sum;
    }
};

更优的做法(双指针法):

// 时间复杂度:O(n)
// 空间复杂度:O(1)
class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int slowIndex = 0;
        for (int fastIndex = 0; fastIndex < nums.size(); fastIndex++) {
            if (val != nums[fastIndex]) {
                nums[slowIndex++] = nums[fastIndex];
            }
        }
        return slowIndex;
    }
};

二分查找

题目链接:二分查找
停更阶段学习算法题的一些总结_第9张图片
代码实现:

class Solution {
public:
    int search(vector<int>& nums, int target)
    {
      int right=nums.size()-1;
      int left=0;
      while(left<=right)
      {
          int mid=left+(right-left)/2;
          if(nums[mid]>target)
          {
              right=mid-1;
          }
          else if(nums[mid]<target)
          {
              left=mid+1;
          }
          else
          {
              return mid;
          }
      }
      return -1;
    }
};

第二种写法:

// 版本二
class Solution {
public:
    int search(vector<int>& nums, int target) {
        int left = 0;
        int right = nums.size(); // 定义target在左闭右开的区间里,即:[left, right)
        while (left < right) { // 因为left == right的时候,在[left, right)是无效的空间,所以使用 <
            int middle = left + ((right - left) >> 1);
            if (nums[middle] > target) {
                right = middle; // target 在左区间,在[left, middle)中
            } else if (nums[middle] < target) {
                left = middle + 1; // target 在右区间,在[middle + 1, right)中
            } else { // nums[middle] == target
                return middle; // 数组中找到目标值,直接返回下标
            }
        }
        // 未找到目标值
        return -1;
    }
};

回文数

题目链接:回文数
停更阶段学习算法题的一些总结_第10张图片

bool isPalindrome(int x){
    if(x<0|(x%10==0&&x!=0))
    {
        return false;
    }
    if(x==0)
    {
        return true;
    }
    int temp=0;
    int nums=0;
    while(x>temp)
    {
        temp=temp*10+x%10;
        x/=10;
    }
    if(temp/10==x||temp==x)
    {
        return true;
    }
    else
    {
        return false;
    }
}

二星题目

二星题目里面的算法题基本都涉及到一些数据结构了或者是一些比较难理解的题目。

单链表(数组模拟实现)

题目链接:单链表
停更阶段学习算法题的一些总结_第11张图片
代码实现:
y总的代码实现:

#include 

using namespace std;

const int N = 100010;


// head 表示头结点的下标
// e[i] 表示节点i的值
// ne[i] 表示节点i的next指针是多少
// idx 存储当前已经用到了哪个点
int head, e[N], ne[N], idx;

// 初始化
void init()
{
    head = -1;
    idx = 0;
}

// 将x插到头结点
void add_to_head(int x)
{
    e[idx] = x, ne[idx] = head, head = idx ++ ;
}

// 将x插到下标是k的点后面
void add(int k, int x)
{
    e[idx] = x, ne[idx] = ne[k], ne[k] = idx ++ ;
}

// 将下标是k的点后面的点删掉
void remove(int k)
{
    ne[k] = ne[ne[k]];
}

int main()
{
    int m;
    cin >> m;

    init();

    while (m -- )
    {
        int k, x;
        char op;

        cin >> op;
        if (op == 'H')
        {
            cin >> x;
            add_to_head(x);
        }
        else if (op == 'D')
        {
            cin >> k;
            if (!k) head = ne[head];
            else remove(k - 1);
        }
        else
        {
            cin >> k >> x;
            add(k - 1, x);
        }
    }

    for (int i = head; i != -1; i = ne[i]) cout << e[i] << ' ';
    cout << endl;

    return 0;
}

自己的代码实现:

#include
using namespace std;

const int N=100010;

int e[N],ne[N],idx,head;//idx表示当前的节点,ne【N】表示N节点的下一个节点是什么

void init()
{
    head=-1; //-1表示空节点
    idx=0;
}
void  add_to_head(int x)
{
    e[idx]=x;
    ne[idx]=head;
    head=idx++;
}

void add_to_after(int k,int x)
{
    e[idx]=x;
    ne[idx]=ne[k];
    ne[k]=idx++;
}

void move_after(int k)
{
    ne[k]=ne[ne[k]];
}

int main()
{
    int m;
    cin>>m;
    init();

    while(m--)
    {
        char op;
        int x;
        int k;
        cin>>op;
        if(op=='H')
        {
            cin>>x;
            add_to_head(x);
        }
        else if(op=='I')
        {
            cin>>k>>x;
            add_to_after(k-1,x);
        }
        else
        {
            cin>>k;
            if(!k) head=ne[head];
            else move_after(k-1);
        }
    }
    for(int i=head;i!=-1;i=ne[i])
    {
        cout<<e[i]<<" ";
    }
    cout<<endl;
    return 0;
}

如果你还是不怎么理解单链表,可以去看看博主之前关于单链表的文章。单链表的实现


双链表(数组实现)

题目链接:双链表
停更阶段学习算法题的一些总结_第12张图片
代码实现:
y总的:

#include 

using namespace std;

const int N = 100010;

int m;
int e[N], l[N], r[N], idx;

// 在节点a的右边插入一个数x
void insert(int a, int x)
{
    e[idx] = x;
    l[idx] = a, r[idx] = r[a];
    l[r[a]] = idx, r[a] = idx ++ ;
}

// 删除节点a
void remove(int a)
{
    l[r[a]] = l[a];
    r[l[a]] = r[a];
}

int main()
{
    cin >> m;

    // 0是左端点,1是右端点
    r[0] = 1, l[1] = 0;
    idx = 2;

    while (m -- )
    {
        string op;
        cin >> op;
        int k, x;
        if (op == "L")
        {
            cin >> x;
            insert(0, x);
        }
        else if (op == "R")
        {
            cin >> x;
            insert(l[1], x);
        }
        else if (op == "D")
        {
            cin >> k;
            remove(k + 1);
        }
        else if (op == "IL")
        {
            cin >> k >> x;
            insert(l[k + 1], x);
        }
        else
        {
            cin >> k >> x;
            insert(k + 1, x);
        }
    }

    for (int i = r[0]; i != 1; i = r[i]) cout << e[i] << ' ';
    cout << endl;

    return 0;
}

自己做时:

#include

using namespace std;

const int N=100010;

int e[N],l[N],r[N],idx;

//初始化  0表示左端点,1表示右端点
void init()
{
    r[0]=1;
    l[1]=0;
    idx=2;
}

//在节点k的右边插入一个节点
void add_k_right(int k,int x)
{
    e[idx]=x;
    l[idx]=k;
    r[idx]=r[k];
    l[r[k]]=idx;   //这两个顺序不能反,因为你要引用r[k]
    r[k]=idx++;
}

//删除k节点
void movek(int k)
{
     l[r[k]]=l[k];
     r[l[k]]=r[k];
}

int main()
{
    int m;
    cin>>m;
    init();  //记得要初始化
   while(m--)
   {
       string op;
       cin>>op;
       int x;
       int k;
       if(op=="L")
       {
           cin>>x;
           add_k_right(0,x);
       }
       else if(op=="R")
       {
           cin>>x;
           add_k_right(l[1],x);
       }
       else if(op=="D")
       {
           cin>>k;
           //注意这儿idx是从2开始的,即第一个插入的元素idx为2,因为0,1被左右端点占据了
           movek(k+1);
       }
       else if(op=="IL")
       {
           cin>>k;    //注意这儿的顺序,k写在前面,因为在函数传参时,先传的是有关k+1的这个,所以k的cin要写在前面
           cin>>x;
           add_k_right(l[k+1],x);
       }
       else
       {
           cin>>k>>x;  //注意这儿的顺序,k写在前面,因为在函数传参时,先传的是有关k+1的这个,所以k的cin要写在前面
           add_k_right(k+1,x);
       }

   }
   for(int i=r[0];i!=1;i=r[i])  //注意打印时传入的参数
   {
       cout<<e[i]<<' ';
   }
   cout<<endl;
   return 0;
}

对双向带头循环不怎么了解的小伙伴可以去看看博主之前的这篇文章。双向带头循环链表的实现


模拟栈

题目链接:模拟栈
停更阶段学习算法题的一些总结_第13张图片
代码实现:
y总的:

#include 

using namespace std;

const int N = 100010;

int m;
int stk[N], tt;

int main()
{
    cin >> m;
    while (m -- )
    {
        string op;
        int x;

        cin >> op;
        if (op == "push")
        {
            cin >> x;
            stk[ ++ tt] = x;
        }
        else if (op == "pop") tt -- ;
        else if (op == "empty") cout << (tt ? "NO" : "YES") << endl;
        else cout << stk[tt] << endl;
    }

    return 0;
}

自己写时:

#include

using namespace std;

const int N=100010;

int m;
int stk[N],tt ;  //stk相当于栈,tt是栈顶元素的下标

int main()
{

    cin>>m;
    int x;
    string op;
    while(m--)
    {
        cin>>op;   //cin>>op不能写在外面,因为你循环里面每次都要输入op来判断是哪个操作
        if(op=="pop")
        {
            tt--;
        }
        else if(op=="push")
        {
            cin>>x;
            stk[++tt]=x;
        }
        else if(op=="empty")
        {
            if(tt==0)
            {
                cout<<"YES"<<endl;
            }
            else 
            {
                cout<<"NO"<<endl;
            }
        }
        else 
        {
            cout<<stk[tt]<<endl;
        }
    }
    return 0;
}

如果对栈不怎么熟悉的小伙伴可以去看看博主之前的这篇文章。栈的多种实现


模拟队列

题目链接:模拟队列
停更阶段学习算法题的一些总结_第14张图片
代码实现:
y总的:

#include 

using namespace std;

const int N = 100010;

int m;
int q[N], hh, tt = -1;

int main()
{
    cin >> m;

    while (m -- )
    {
        string op;
        int x;

        cin >> op;
        if (op == "push")
        {
            cin >> x;
            q[ ++ tt] = x;
        }
        else if (op == "pop") hh ++ ;
        else if (op == "empty") cout << (hh <= tt ? "NO" : "YES") << endl;
        else cout << q[hh] << endl;
    }

    return 0;
}

自己写时:

#include

using namespace std;

const int N=100010;

int m;
int q[N],hh,tt=-1;  //hh为队列的头,tt为队列的尾

int main()
{
    cin>>m;
    while(m--)
    {
        string op;
        cin>>op;
        int x;
        if(op=="push")
        {
            cin>>x;
            q[++tt]=x;
        }
        else if(op=="pop")
        {
            hh++;
        }
        else if(op=="empty")
        {
            if(hh>tt)   //这儿不能写成hh>=tt,因为如果已经插入了一个元素,此时tt=0,hh==tt,就不能说明队列时空的了
            {
                printf("YES\n");
            }
            else
            {
                printf("NO\n");
            }
        }
        else
        {
            printf("%d\n",q[hh]);
        }
    }
}

其实队列和栈的实现很像,都是用一个数组,然后用一个类似于头指针和尾指针的控制量来解决问题。
如果对队列不怎么理解的小伙伴可以去看看博主之前的这篇文章:队列的实现


单调栈

题目链接:单调栈
停更阶段学习算法题的一些总结_第15张图片
代码实现:
y总的:

#include 

using namespace std;

const int N = 100010;

int stk[N], tt;

int main()
{
    int n;
    cin >> n;
    while (n -- )
    {
        int x;
        scanf("%d", &x);
        while (tt && stk[tt] >= x) tt -- ; 
        if (!tt) printf("-1 ");
        else printf("%d ", stk[tt]);
        stk[ ++ tt] = x;
    }

    return 0;
}

自己写时:

#include 

using namespace std;

const int N = 100010;

int stk[N], tt;

int main()
{
    int n;
    cin>>n;
    for(int i=0;i<n;i++)
    {    
        int x;
        cin>>x;
         while(tt&&stk[tt]>=x) //这一步的while循环用来确保栈是单调递增的,因为它只有找到比x小时才会停止
         {
             tt--;
         }
         if(!tt)  //如果tt=0,也就是走到头都没有找到符合条件的数时则说明没有,此时输出-1
         {
             printf("-1 ");
         }
         else
         {
             printf("%d ",stk[tt]);
         }
         stk[++tt]=x;  //这边一定要是++tt不能写成tt++,因为你的0号位是不存放元素的,是用来判断栈是否为空的
    }
}

滑动窗口(单调队列)

题目链接:滑动窗口(单调队列)
停更阶段学习算法题的一些总结_第16张图片
代码实现:
y总的:

#include 

using namespace std;

const int N = 1000010;

int a[N], q[N];
//这里队里q存放的是a数组里元素的下标

int main()
{
    int n, k;
    scanf("%d%d", &n, &k);
    for (int i = 0; i < n; i ++ ) scanf("%d", &a[i]);

    int hh = 0, tt = -1;
    for (int i = 0; i < n; i ++ )
    {
        if (hh <= tt && i - k + 1 > q[hh]) hh ++ ;

        while (hh <= tt && a[q[tt]] >= a[i]) tt -- ; //为了保证队列的单调性
        q[ ++ tt] = i;

        if (i >= k - 1) printf("%d ", a[q[hh]]);
    }

    puts("");

    hh = 0, tt = -1;
    for (int i = 0; i < n; i ++ )
    {
        if (hh <= tt && i - k + 1 > q[hh]) hh ++ ;

        while (hh <= tt && a[q[tt]] <= a[i]) tt -- ;
        q[ ++ tt] = i;

        if (i >= k - 1) printf("%d ", a[q[hh]]);
    }

    puts("");

    return 0;
}

自己写时:

#include

using namespace std;

const int N=1000010;
int a[N],q[N],hh,tt=-1;
//注意这里q数组存放的是a数组里元素的下标
int main()
{
    int n,k;
    scanf("%d%d",&n,&k);
    for(int i=0;i<n;i++)
    {
        scanf("%d",&a[i]);
    }
    for(int i=0;i<n;i++)
    {
        if(hh<=tt&&i-k+1>q[hh])  //i-k+1表示当前元素的下标,如果当前元素的下标比队列里队头存放的下标大,则说明队列要向后移
        {
            hh++;
        }
        while(hh<=tt&&a[q[tt]]>=a[i]) //为了保证队列的单调性
        {
            tt--;
        }
        q[++tt]=i;
        if(i-k+1>=0)  //当此时的下标符合条件时,则说出,由于之前实现时保证了队列单调递增,所以队头一定存放的是最小的元素
        {
            printf("%d ",a[q[hh]]);
        }
    }
    cout<<endl;

    hh=0,tt=-1;     //当找最大值得时候别忘记重置一下,因为你之前的tt和hh已经被改变过了
    for(int i=0;i<n;i++)
    {
        if(hh<=tt&&i-k+1>q[hh])
        {
            hh++;
        }
        while(hh<=tt&&a[q[tt]]<=a[i])
        {
            tt--;
        }
        q[++tt]=i;
        if(i-k+1>=0)
        {
            printf("%d ",a[q[hh]]);
        }
     }
    cout<<endl;
    return 0;
}

KMP算法

题目链接:KMP字符串
停更阶段学习算法题的一些总结_第17张图片

关于KMP算法,博主之前专门写了一篇文章来介绍。KMP算法
如果看文章还不怎么懂的小伙伴可以去看看B站的这个教学视频,将的真的很清楚。KMP算法视频讲解


Trie字符串统计

题目链接:Trie字符串统计
停更阶段学习算法题的一些总结_第18张图片
代码实现:
y总的:

#include 

using namespace std;

const int N = 100010;

int son[N][26], cnt[N], idx;
char str[N];

void insert(char *str)
{
    int p = 0;
    for (int i = 0; str[i]; i ++ )
    {
        int u = str[i] - 'a';
        if (!son[p][u]) son[p][u] = ++ idx;
        p = son[p][u];
    }
    cnt[p] ++ ;
}

int query(char *str)
{
    int p = 0;
    for (int i = 0; str[i]; i ++ )
    {
        int u = str[i] - 'a';
        if (!son[p][u]) return 0;
        p = son[p][u];
    }
    return cnt[p];
}

int main()
{
    int n;
    scanf("%d", &n);
    while (n -- )
    {
        char op[2];
        scanf("%s%s", op, str);
        if (*op == 'I') insert(str);
        else printf("%d\n", query(str));
    }

    return 0;
}

自己写时:

#include

using namespace std;

const int N=100010;

int s[N][26],cnt[N],idx;   //idx表示下标为idx的那个元素
char str[N];

//其实Insert这个函数就相当于构成这个Trie树
void Insert(char* str)
{
    int p=0;
//不一定非要以i
    for(int i=0;str[i];i++)
    {
//将a~z这些字母映射为0~26,注意是'a'而不是“a”,不然减的就不是阿斯克码了
        int u=str[i]-'a';
//s这个数组表示以p为根节点的第u号儿子是否存在,如果不存在的话,则++idx,存在的话则,则迭代,此时
//让之前的儿子节点变为父亲节点。
        if(!s[p][u])
        {
           s[p][u]=++idx;
        }
        p=s[p][u];

    }
//cnt用于记录字符串出现的次数的,当for循环结束时,则以p根节点结束的那个分支的字符串的cnt++
    cnt[p]++;
}

int query(char* str)
{
    int p=0;
    for(int i=0;str[i];i++)
    {
        int u=str[i]-'a';
        if(!s[p][u])  
        {
            return 0;
        }
        p=s[p][u];

    }
    return cnt[p];
}

int main()
{
    int n;
    scanf("%d",&n);
    while(n--)
    {
         char op[2];
         scanf("%s%s",op,str);
        if(op[0]=='I')
        {
            Insert(str);
        }
        else
        {
           printf("%d\n",query(str));
        }
    }
    return 0;
}

下面这张是我分析这段代码时画的图,如果读者不怎么看的懂这段代码,也可以举几个例子来结合代码理解。
停更阶段学习算法题的一些总结_第19张图片


最大异或对

题目链接:最大异或对
停更阶段学习算法题的一些总结_第20张图片
代码实现:
y总的:

#include 
#include 

using namespace std;

const int N = 100010, M = 3100010;

int n;
int a[N], son[M][2], idx;

void insert(int x)
{
    int p = 0;
    for (int i = 30; i >= 0; i -- )
    {
        int &s = son[p][x >> i & 1];
        if (!s) s = ++ idx;
        p = s;
    }
}

int search(int x)
{
    int p = 0, res = 0;
    for (int i = 30; i >= 0; i -- )
    {
        int s = x >> i & 1;
        if (son[p][!s])
        {
            res += 1 << i;
            p = son[p][!s];
        }
        else p = son[p][s];
    }
    return res;
}

int main()
{
    scanf("%d", &n);
    for (int i = 0; i < n; i ++ )
    {
        scanf("%d", &a[i]);
        insert(a[i]);
    }

    int res = 0;
    for (int i = 0; i < n; i ++ ) res = max(res, search(a[i]));

    printf("%d\n", res);

    return 0;
}

自己写时:

#include
#include
using namespace std;

const int N=100010,M=31*N;

int n;
int a[N];
int son[M][2],idx;

//创建一棵Trie树
void  insert(int x)
{
    int p=0;
    for(int i=30;i>=0;i--)
    {
        int u=x>>i&1;
        if(!son[p][u])   //如果这个儿子不存在的话,就创建一个idx++
        {
            son[p][u]=++idx;
        }
        p=son[p][u];   //如果已经有的了话,就迭代就行了,让儿子做父亲。
    }
}

int query(int x)
{
    int p=0,res=0;  //res用于记录最终的这个数是多少用的
    for(int i=30;i>=0;i--)
    {
        int u=x>>i&1;
        if(son[p][!u])  //如果另外一个方向存在,那么就往另外一个方向走
        {
            p=son[p][!u];
            res=res*2+!u;  //从二进制角度看,相当于前移一位,空出一位来,然后让!u也就是不同的那个二级制位加
        }
        else
        {
            p=son[p][u];
            res=res*2+u;
        }
    }
    return res;
}
int main()
{
    scanf("%d",&n);
    for(int i=0;i<n;i++)
    {
        scanf("%d",&a[i]);
    }
    int res=0;
    for(int i=0;i<n;i++)
    {
        insert(a[i]);
        int t=query(a[i]);
        res=max(res,a[i]^t);
    }
    printf("%d\n",res);
    return 0;
}

写这题时很难直接想到这个思路,所以一开始我们可以用暴力的解法去尝试一下,但时间上可能会过不去,这个时候我们想办法去优化它就行了,发现第一层for循环肯定是无法在优化了,只能想办法去优化第二层for循环了。


并查集

题目链接:并查集
停更阶段学习算法题的一些总结_第21张图片
代码实现:
y总的:

#include 

using namespace std;

const int N = 100010;

int p[N];

int find(int x)
{
    if (p[x] != x) p[x] = find(p[x]);
    return p[x];
}

int main()
{
    int n, m;
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i ++ ) p[i] = i;

    while (m -- )
    {
        char op[2];
        int a, b;
        scanf("%s%d%d", op, &a, &b);
        if (*op == 'M') p[find(a)] = find(b);
        else
        {
            if (find(a) == find(b)) puts("Yes");
            else puts("No");
        }
    }

    return 0;
}

自己写时:

#include

using namespace std;

const int N=100010;

int p[N];   //p是用来存放每个节点的父亲节点的

int find(int x)  //返回x的祖宗节点和路径压缩
{
    if(p[x]!=x)
    {
        p[x]=find(p[x]);
    }
    return p[x];
}

int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        p[i]=i;
    }
    while(m--)
    {
        char op[2];
        int a,b;
        scanf("%s%d%d",op,&a,&b);
        if(op[0]=='M')
        {
//找到a的祖宗节点,让a的祖宗节点的父亲为b的祖宗节点,这样就是a树接到b树里了
            p[find(a)]=find(b);   
        }
        else
        {
            if(find(a)==find(b))
            {
                puts("Yes");
            }
            else
            {
                puts("No");
            }
        }
    }
    return 0;
}

连通块中点的数量(并查集的应用)

题目链接:连通块中点的数量
停更阶段学习算法题的一些总结_第22张图片
代码实现:
y总的:

#include 

using namespace std;

const int N = 100010;

int n, m;
int p[N], cnt[N];

int find(int x)
{
    if (p[x] != x) p[x] = find(p[x]);
    return p[x];
}

int main()
{
    cin >> n >> m;

    for (int i = 1; i <= n; i ++ )
    {
        p[i] = i;
        cnt[i] = 1;
    }

    while (m -- )
    {
        string op;
        int a, b;
        cin >> op;

        if (op == "C")
        {
            cin >> a >> b;
            a = find(a), b = find(b);
            if (a != b)
            {
                p[a] = b;
                cnt[b] += cnt[a];
            }
        }
        else if (op == "Q1")
        {
            cin >> a >> b;
            if (find(a) == find(b)) puts("Yes");
            else puts("No");
        }
        else
        {
            cin >> a;
            cout << cnt[find(a)] << endl;
        }
    }

    return 0;
}

自己写时:

#include

using namespace std;

const int N=100010;

int n,m;
int p[N],cnt[N];   //p是用来存放每个节点的父亲节点的;

int find(int x)  //返回x的祖宗节点和路径压缩
{
    if(p[x]!=x)
    {
        p[x]=find(p[x]);
    }
    return p[x];
}

int main()
{
    int n,m;
     scanf("%d%d",&n,&m);
    for (int i = 1; i <= n; i ++ )
    {
        p[i] = i;
        cnt[i] = 1;
    }

    while (m -- )
    {
        string op;
        int a, b;
        cin >> op;

        if (op == "C")
        {
            scanf("%d%d",&a,&b);
            //a=find(a),b=find(b);
            if (find(a)!= find(b))
            {
                cnt[find(b)] += cnt[find(a)];  //如果这么写的
                p[find(a)] =find(b);
                //cnt[find(b)] += cnt[find(a)];
            }
             /*if (a!= b)
            {
                p[a] =b;
                cnt[b] += cnt[a];
            }*/
        }
        else if (op == "Q1")
        {
            scanf("%d%d",&a,&b);
            if (find(a) == find(b)) puts("Yes");
            else puts("No");
        }
        else
        {
            scanf("%d",&a);
            printf("%d\n",cnt[find(a)]);
        }
    }

    return 0;
}

堆排序

关于堆排序,之前博主详细写了一篇介绍的文章。有兴趣的小伙伴可以去看看。堆排序


模拟堆

题目链接:模拟堆
停更阶段学习算法题的一些总结_第23张图片
代码实现:
y总的:

#include 
#include 
#include 

using namespace std;

const int N = 100010;

int h[N], ph[N], hp[N], cnt;

void heap_swap(int a, int b)
{
    swap(ph[hp[a]],ph[hp[b]]);
    swap(hp[a], hp[b]);
    swap(h[a], h[b]);
}

void down(int u)
{
    int t = u;
    if (u * 2 <= cnt && h[u * 2] < h[t]) t = u * 2;
    if (u * 2 + 1 <= cnt && h[u * 2 + 1] < h[t]) t = u * 2 + 1;
    if (u != t)
    {
        heap_swap(u, t);
        down(t);
    }
}

void up(int u)
{
    while (u / 2 && h[u] < h[u / 2])
    {
        heap_swap(u, u / 2);
        u >>= 1;
    }
}

int main()
{
    int n, m = 0;
    scanf("%d", &n);
    while (n -- )
    {
        char op[5];
        int k, x;
        scanf("%s", op);
        if (!strcmp(op, "I"))
        {
            scanf("%d", &x);
            cnt ++ ;
            m ++ ;
            ph[m] = cnt, hp[cnt] = m;
            h[cnt] = x;
            up(cnt);
        }
        else if (!strcmp(op, "PM")) printf("%d\n", h[1]);
        else if (!strcmp(op, "DM"))
        {
            heap_swap(1, cnt);
            cnt -- ;
            down(1);
        }
        else if (!strcmp(op, "D"))
        {
            scanf("%d", &k);
            k = ph[k];
            heap_swap(k, cnt);
            cnt -- ;
            up(k);
            down(k);
        }
        else
        {
            scanf("%d%d", &k, &x);
            k = ph[k];
            h[k] = x;
            up(k);
            down(k);
        }
    }

    return 0;
}

自己写时:

#include
#include
#include
using namespace std;

const int N=100010;

int h[N],ph[N],hp[N],cnt,m;   //ph,hp,m是用来构造映射关系的。

void heap_swap(int a,int b)
{
    swap(ph[hp[a]],ph[hp[b]]);
    swap(hp[a],hp[b]);
    swap(h[a],h[b]);
}

void down(int root)
{
    int children = root;
    if (root * 2 <= cnt && h[root * 2] < h[children]) children = root * 2;
    if (root * 2 + 1 <= cnt && h[root * 2 + 1] < h[children]) children = root * 2 + 1;
    if(root!=children)
    {
        heap_swap(root,children);
        down(children);
    }
}


void up(int root)
{
    while(root/2!=0&&h[root/2]>h[root])
    {
        heap_swap(root/2,root);
        root>>=1;
    }
}

int main()
{
    int n;
    scanf("%d",&n);
    while(n--)
    {
        int k,x;
        char op[10];
        scanf("%s",op);
        if(!strcmp(op,"I"))
        {
            scanf("%d",&x);
            cnt++;
            m++;
            ph[m]=cnt,hp[cnt]=m;
            h[cnt]=x;
            up(cnt);
        }
        else if(!strcmp(op,"PM"))
        {
            printf("%d\n",h[1]);
        }
        else if(!strcmp(op,"DM"))
        {
            heap_swap(1,cnt);
            cnt--;
            down(1);
        }
        else if(!strcmp(op,"D"))
        {
            scanf("%d",&k);
            k=ph[k];
            heap_swap(k,cnt);
            cnt--;
            down(k);
            up(k);
        }
        else
        {
            scanf("%d%d",&k,&x);
            k=ph[k];
            h[k]=x;
            down(k);
            up(k);
        }
    }
    return 0;
}

说实话模拟堆的意思并不是很大,所以看不懂也没有太大的问题。


BFS

注意:往往BFS能做的题,DFS也能做出来,反之亦然,所以下面的题目可能在DFS或者BFS有重复的情况

二叉树的范围和

题目链接:二叉树的范围和
停更阶段学习算法题的一些总结_第24张图片
代码实现:

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
 
 /*
 //用前序遍历的方法,遍历过程中如果满足条件就加到sum上(这个其实也能看成是dfs)
class Solution {
public:
    int rangeSumBST(TreeNode* root, int low, int high)
    {
         int sum=0;
         BinaryPrev(root,low,high,sum);
         return sum;
    }
    void BinaryPrev(TreeNode* n1, int low, int high,int& sum)
    {   
        if(n1==NULL)
        {
            return;
        }
        if (n1!=NULL&&n1->val>=low&&n1->val<=high)
        {
            sum+=n1->val;
        }
        if(n1!=NULL)
        {
            BinaryPrev(n1->left,low,high,sum);
            BinaryPrev(n1->right,low,high,sum);
        }
    }
};*/


/*
//递归法(其实就是dfs):整个树满足要求的节点和==左子树满足要求的节点和+右子树满足要求的节点和+根节点是否满足
class Solution {
public:
    int rangeSumBST(TreeNode* root, int low, int high)
    {
         int sum=func(root,low,high);
         return sum;
    }

    int func(TreeNode* root, int low, int high)
    {
        int sum=0;
        /*if(root==NULL)
        {
           return 0;
        }
        else
        {
//计算左子树所有满足要求的节点和
            int leftsum=func(root->left,low,high); 
//计算右子树所有满足要求的节点和  
            int rightsum=func(root->right,low,high);
//最后看根节点是否满足要求,满足则加
            if(root->val>=low&&root->val<=high)
            {
                sum=leftsum+rightsum+root->val;
            }
            else
            {
                sum=leftsum+rightsum;
            }
        }*/
/*
        //换种写法
        if(root==NULL)
        {
           return 0;
        }
        int leftsum=func(root->left,low,high);
        int rightsum=func(root->right,low,high);
        sum=leftsum+rightsum;
        if(root->val>=low&&root->val<=high)
        {
            sum=leftsum+rightsum+root->val;
        }
        return sum;
    }
};
*/


//bfs算法解决   广度优先遍历,一层一层的看,满足则加。一般bfs算法都要结合队列解决问题
class Solution {
public:
    int rangeSumBST(TreeNode* root, int low, int high)
    { 
       queue<TreeNode*> q;
       int sum=func(root,low,high,q);
       return sum;
    }

    int func(TreeNode* root, int low, int high,queue<TreeNode*>& q)
    {
        int sum=0;
         if(root!=NULL)
         {
             q.push(root);
         }
         
         /*while(q.size()>0)
         {
//这个里面的while循环是用来处理当前这一层的节点的
             while(q.size()>0)  
             {
                TreeNode* cur=q.front();
                if(cur->val>=low&&cur->val<=high)
                {
                    sum+=cur->val;
                }
                if(cur->left!=NULL)
                {
                    q.push(cur->left);
                }
                if(cur->right!=NULL)
                {
                    q.push(cur->right); 
                }
//你这人pop时其实size就已经减过了,所以没有必要再size--了
                q.pop(); 
                //--q.size();
             }
         }*/

//更加好的写法
         while(!q.empty())
         {
             int count=q.size();
             for(int i=0;i<count;i++)
             {
                 TreeNode* cur=q.front();
                 if(cur->val>=low&&cur->val<=high)
                {
                    sum+=cur->val;
                }
                if(cur->left!=NULL)
                {
                    q.push(cur->left);
                }
                if(cur->right!=NULL)
                {
                    q.push(cur->right); 
                }
                q.pop();
             }
         }
         return sum;
    }
};

二叉树的层序遍历II

题目链接:二叉树的层序遍历II
停更阶段学习算法题的一些总结_第25张图片
代码实现:

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
 //bfs算法

class Solution {
public:
    int Max(int a, int b)
    {
        return a > b ? a : b;
    }

    int maxDepth(struct TreeNode* root)
    {
        if (root == NULL)
        {
            return 0;
        }
        return 1 + Max( maxDepth(root->left), maxDepth(root->right));
    }

    vector<vector<int>> levelOrderBottom(TreeNode* root) 
    {
            vector<vector<int>> res;
            int depth=maxDepth(root);
            res.resize(depth);
            queue<TreeNode*> q;
            if(root==NULL)
            {
                 return res;
            }
            q.push(root);
            while(!q.empty())
            {
                int size=q.size();
                vector<int> cur;
                for(int i=0;i<size;i++)
                {
                    TreeNode* temp=q.front();
                    q.pop();
                    cur.push_back(temp->val);
                    if(temp->left!=NULL)
                    {
                        q.push(temp->left);
                    }
                    if(temp->right!=NULL)
                    {
                        q.push(temp->right);
                    }
                }
                res[depth-1]=cur;
                depth--;
            }
            return res;
    }
};

岛屿数量(在下面的DFS中有出现)


二叉树的层序遍历

题目链接:二叉树的层序遍历
停更阶段学习算法题的一些总结_第26张图片
代码实现:

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */

 //bfs算法

class Solution {
public:

    vector<vector<int>> levelOrder(TreeNode* root) 
    {
            vector<vector<int>> res;
            queue<TreeNode*> q;
            if(root==NULL)
            {
                 return res;
            }
            
            q.push(root);
           /* 
           while(!q.empty())
            {
                vector cur;
                int count=q.size();
                while(count>0)
                {
                    TreeNode* node=q.front();
                    //q.pop();
                    cur.push_back(node->val); 
                    if(node->left!=NULL)
                    {
                        q.push(root->left);
                    }
                    if(node->right!=NULL)
                    {
                        q.push(root->right);
                    }
                    count--;
                }
                res.push_back(cur);
            }*/
           
//这样写不容易写出bug来,而且代码更加的优美好理解
            while(!q.empty())
            {
                int size=q.size();
                vector<int> cur;
                for(int i=0;i<size;i++)
                {
                    TreeNode* temp=q.front();
                    q.pop();
                    cur.push_back(temp->val);
                    if(temp->left!=NULL)
                    {
                        q.push(temp->left);
                    }
                    if(temp->right!=NULL)
                    {
                        q.push(temp->right);
                    }
                }
                res.push_back(cur);
            }
            return res;
    }
};


//dfs的方法,其实就是用先序遍历来做这一题
/*class Solution {
public:
    vector> levelOrder(TreeNode* root) 
    {
            vector> res;
            if(root==NULL)
            {
              return res;
            }
            else
            {
                dfs(res,root,0);
            }
            return res;
    }

    void dfs(vector>& res,TreeNode* root,int level)
    {
        if(root==NULL)
        {
            return;
        }
//如果res中不够用,就插入一个数组
        if(level>=res.size())
        {
//注意这儿往里面插入括号的写法
            res.push_back(vector ());
        }
        res[level].push_back(root->val);
        if(root->left!=NULL)
        {
            dfs(res,root->left,level+1);
        }
        if(root->right!=NULL)
        {
            dfs(res,root->right,level+1);
        }
    }
};*/

DFS

二叉树的范围和(在上面的BFS中出现过,不再重复)


二叉树的层序遍历(在BFS中出现过,不再重复)


二叉树的中序遍历

题目链接:二叉树的中序遍历
停更阶段学习算法题的一些总结_第27张图片
代码实现:

 typedef struct TreeNode BT;
int BinarySize(BT* root)
{
    return root==NULL ? 0:BinarySize(root->left)+BinarySize(root->right)+1;
}

void MiddleOrder(BT* root,int* arr,int* pi)
{
    if(root)
    {
        MiddleOrder(root->left,arr,pi);
        arr[(*pi)++]=root->val;
        MiddleOrder(root->right,arr,pi);
    }
}
int* inorderTraversal(struct TreeNode* root, int* returnSize){
   int size=BinarySize(root);
   int i=0;
   int *arr=(int*)malloc(sizeof(int)*size);
   MiddleOrder(root,arr,&i);
   *returnSize=size;
   return arr;
}

二叉树的后序遍历

题目链接:二叉树的后序遍历
停更阶段学习算法题的一些总结_第28张图片

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     struct TreeNode *left;
 *     struct TreeNode *right;
 * };
 */


/**
 * Note: The returned array must be malloced, assume caller calls free().
 */

 int BinarySize(struct TreeNode* root)
 {
     if(root==NULL)
     {
         return 0;
     }
     else
     {
         return BinarySize(root->left)+BinarySize(root->right)+1;
     }
 }

 void AfterOrder(struct TreeNode* root,int *arr,int* pi)
{
	if (root == NULL)
	{
		return;
	}
	AfterOrder(root->left,arr,pi);
	AfterOrder(root->right,arr,pi);
	arr[(*pi)++]=root->val;
}

int* postorderTraversal(struct TreeNode* root, int* returnSize){
    int size=BinarySize(root);
    int *arr=(int *)malloc(sizeof(int)*size);
    int i=0;
    AfterOrder(root,arr,&i);
    *returnSize=size;
    return arr;
}

括号生成

题目链接:括号生成
停更阶段学习算法题的一些总结_第29张图片
代码实现:

class Solution {
public:
    vector<string> generateParenthesis(int n)
    {
        vector<string> res;
         backtracking(n,res,0,0,"");
         return res;
    }
    void backtracking(int n,vector<string>& res,int leftnums,int rightnums,string str)
    {
        if(rightnums>leftnums)
       {
           return ;
       }
       if(leftnums<n)
       {
         //str.add('(');
         backtracking(n,res,leftnums+1,rightnums,str+"(");
         //str.pop_back();
       }
       if(rightnums<leftnums)
       {
           backtracking(n,res,leftnums,rightnums+1,str+")");
       }
       if(leftnums==n&&rightnums==n)
       {
          res.push_back(str);
          return;
       }
    }
};    

/*
// DFS深度优先搜索算法来解决  
class Solution {
public:
    vector res; //记录答案 
    vector generateParenthesis(int n) {
        dfs(n , 0 , 0, "");
        return res;
    }
    void dfs(int n ,int lc, int rc ,string str)
    {
        if( lc == n && rc == n)
        {
            res.push_back(str);    //递归边界
            return ;
        } 
        else
        {
            if(lc < n) dfs(n, lc + 1, rc, str + "(");            //拼接左括号
            if(rc < n && lc > rc) dfs(n, lc, rc + 1, str + ")"); //拼接右括号
        }
    }
};
*/

子集

题目链接:子集
停更阶段学习算法题的一些总结_第30张图片
代码实现:

//回溯算法
/*class Solution {
public:
    vector> subsets(vector& nums) 
    {
       vector> res;
       vector sub;
//注意这儿集合的写法,必须要写成{}
       res.push_back({});
//for循环每次子集的长度
       for(int length=1;length<=nums.size();length++)
       {
//传res是因为back这个函数是void返回类型的,所以我们要将其传入,在函数里面改变它
//length是用来记录这个子集的长度的
//0这个位置的参数表示我们下一次要访问的元素
//sub这个数组存储这个子集
           backtracking(nums,res,length,0,sub);
       }
       return res;
    }

    void backtracking(vector& nums,vector>& res,int length,int idx,vector& sub)
    {
//当sub这个数组的长度达到要求的长度时就将这个子集插入到结果集中
        if(sub.size()==length)
       {
           res.push_back(sub);
           return ;
       }


       for(int i=idx;i


//dfs算法
class Solution {
public:
    vector<vector<int>> subsets(vector<int>& nums) 
    {
       vector<vector<int>> res;
       vector<int> sub;
       dfs(nums,res,0,sub);
       return res;
    }

    void dfs(vector<int>& nums,vector<vector<int>>& res,int idx,vector<int>& sub)
    {  
//注意这个要每次写在dfs的最开头,这个和回溯算法看起来很像,但还是有区别的,因为你去画树形图
//时,他每一个节点其实就是一个集合。所以这句话要写在一开头,具体可以看图解。
        res.push_back(sub);
        if(idx==nums.size())
       {
           //res.push_back(sub);
           return ;
       }


       for(int i=idx;i<nums.size();i++)
      {
          sub.push_back(nums[i]);
          dfs(nums,res,i+1,sub);  
          sub.pop_back();
      }
    }
};

全排列

题目链接:全排列
停更阶段学习算法题的一些总结_第31张图片
代码实现:

class Solution {
public:
    vector<vector<int>> permute(vector<int>& nums) 
    {
            vector<vector<int>> ans;
            vector<int> array;
            vector<bool> st=vector<bool>(nums.size());
            dfs(ans,array,nums,st);
            return ans;
    }
    void dfs(vector<vector<int>>& ans,vector<int>& array,vector<int> nums,vector<bool>& st)
    {
        if(array.size()==nums.size())
        {
            ans.push_back(array);
            return;
        }
        for(int i=0;i<nums.size();i++)
        {
             if(!st[i])
            {
                array.push_back(nums[i]);
                st[i]=true;
                dfs(ans,array,nums,st);
                array.pop_back();
                st[i]=false;
            }
        }
    }
};

y那儿写的:

#include
using namespace std;
const int maxn=10;
int n;
bool st[maxn];
int path[maxn];

void dfs(int u)
{
    if(u==n)
    {
        for(int i=0;i<n;i++)
        {
            printf("%d ",path[i]);
        }
        printf("\n");
        return;
    }
    for(int i=1;i<=n;i++)
    {
        if(!st[i])
        {
            path[u]=i;
            st[i]=true;
            dfs(u+1);
            st[i]=false;
        }
    }
}
int main()
{
    scanf("%d",&n);
    dfs(0);
    return 0;
}

回溯的写法:

class Solution {
public:
    vector<vector<int>> result;
    vector<int> path;
    void backtracking (vector<int>& nums, vector<bool>& used) {
        // 此时说明找到了一组
        if (path.size() == nums.size()) {
            result.push_back(path);
            return;
        }
        for (int i = 0; i < nums.size(); i++) {
            if (used[i] == true) continue; // path里已经收录的元素,直接跳过
            used[i] = true;
            path.push_back(nums[i]);
            backtracking(nums, used);
            path.pop_back();
            used[i] = false;
        }
    }
    vector<vector<int>> permute(vector<int>& nums) {
        result.clear();
        path.clear();
        vector<bool> used(nums.size(), false);
        backtracking(nums, used);
        return result;
    }
};

岛屿数量

题目链接:岛屿数量
停更阶段学习算法题的一些总结_第32张图片
代码实现:

/*
//dfs同化方法
class Solution {
public:
    int numIslands(vector>& grid) 
    {
        int res=func(grid);
        return res;
    }

    int func(vector>& grid)
    {
        if(grid.size()==0)
        {
            return 0;
        }
        int sum=0;
//注意c++中二维数组行和列的计算
        int row=grid.size();
        int col=grid[0].size();
        int i=0,j=0;
        for(i=0;i>& grid,int x,int y,int row,int col)
    {
//首先写出递归的结束条件
        if(x<0||y<0||x>=row||y>=col||grid[x][y]=='0')
        {
            return ;
        }
//首先把当前位置弄成0
        grid[x][y]='0';
//接下来dfs他的前后左右
        dfs(grid,x+1,y,row,col);
        dfs(grid,x-1,y,row,col);
        dfs(grid,x,y+1,row,col);
        dfs(grid,x,y-1,row,col);
    }
};
*/


//bfs算法
/*
class Solution {
public:
    int numIslands(vector>& grid) 
    {
        int res=func(grid);
        return res;
    }

    int func(vector>& grid)
    {
        if(grid.size()==0)
        {
            return 0;
        }
        queue> q;
        int sum=0;
//注意c++中二维数组行和列的计算
        int ROW=grid.size();
        int COL=grid[0].size();
        int i=0,j=0;
        for(i=0;i0)
                    {
                        auto cur=q.front();
                        q.pop();
                        int x=cur.first,y=cur.second;
    //检查此元素上面一个元素
                        if(x-1>=0&&grid[x-1][y]=='1')
                        {
                            grid[x-1][y]='0';
                            q.push({x-1,y});
                        }
    //检查下面一个元素
                        if(x+1=0&&grid[x][y-1]=='1')
                        {
                            grid[x][y-1]='0';
                            q.push({x,y-1});
                        }
    //检查右边一个元素
                        if(y+1

//并查集的写法
class UnionFind
{
public:
        vector<int> root;
//用来记录合并的次数的
        int count=0;
//构造函数
       UnionFind(vector<vector<char>>& grid)
       {
           int row=grid.size();
           int col=grid[0].size();
//总共的个数
           count=row*col;
           for(int i=0;i<row*col;i++)
           {
               root.push_back(i);
           }
       }
       int find(int x)
       {
           if(x!=root[x])
           {
               root[x]=find(root[x]);
           }
           return root[x];
       }
       void unite(int x,int y)
       {
           int rootx=find(x);
           int rooty=find(y);
           if(rootx!=rooty)
           {
               root[rootx]=rooty;
//此时合并了一个,总的count就减一
               count--;
           }
       }

       int getCount()
       {
           return count;
       }
};

class Solution {
public:
    int numIslands(vector<vector<char>>& grid) 
    {
//4个方向的方向向量
        int dx[4] = {-1,1,0,0}; 
        int dy[4] = {0,0,-1,1};
        if(grid.size()==0)
        {
            return 0;
        }
        int row=grid.size();
        int col=grid[0].size();
//记录有多少个水域
        int water=0;
//将二维数组grid传入并查集这个类中,构成并查集
        UnionFind uf(grid);
        for(int i=0;i<row;i++)
        {
            for(int j=0;j<col;j++)
            {
                if(grid[i][j]=='0')
                {
                    water++;
                }
                else
                {
//检查4个方向,能同化则同化
                    for(int k=0;k<4;k++)
                    {
                        int x=i+dx[k];
                        int y=j+dy[k];
                        if(x>=0&&y>=0&&x<row&&y<col&&grid[x][y]=='1')
                        {
//这里面有一个二维数组下标转化成一维数组下标的问题
                            uf.unite(x*col+y,i*col+j);
                        }
                    }
                }
            }
        }
//最终答案=总的个数-0(水域的个数)-联通的次数
        return uf.getCount()-water;
    }
};

贪心

分发饼干

题目链接:分发饼干
停更阶段学习算法题的一些总结_第33张图片
代码实现:

class Solution {
public:
    /*int findContentChildren(vector& g, vector& s) 
    {
        sort(g.begin(),g.end());
        sort(s.begin(),s.end());
        int cnt=0;
        vector flags;
        flags.resize(s.size(),false);
        for(int i=0;i=g[i]&&flags[j]==false)
                {
                    ++cnt;
                    flags[j]=true;
                    break;
                }
            }
        }
        return cnt;
    }*/

//贪心算法
    int findContentChildren(vector<int>& g, vector<int>& s) 
    {
        sort(g.begin(),g.end());
        sort(s.begin(),s.end());
        int cnt=0;
        int tempj=s.size()-1;
        /*for(int i=g.size()-1;i>=0;i--)
        {
            for(int j=tempj;j>=0;j--)
            {
                if(s[j]>=g[i])
                {
                    ++cnt;
                    tempj=j-1;
                    break;
                }
            }
        }*/
        //一个for循环的写法
        int indexs=s.size()-1;
        for(int i=g.size()-1;i>=0;i--)
        {
            if(indexs>=0&&s[indexs]>=g[i])
            {
                ++cnt;
                --indexs;
            }
        }
        return cnt;
    }
};

回溯

子集(在上面的DFS中出现过,不再重复)


括号生成(在上面的DFS中出现过,不再重复)


组合总和III

题目链接:组合总和III
停更阶段学习算法题的一些总结_第34张图片
代码实现:

class Solution {
public:
    vector<vector<int>> res;
    vector<int> path;
    vector<vector<int>> combinationSum3(int k, int n) 
    {
       int sum=0;
       backtracking(sum,n,k,1);
       return res;
    }

    void backtracking(int sum,int n,int k,int c)
    {
        if(path.size()==k)
        {
            if(sum==n)
            {
               res.push_back(path);
            }
            return ;
        }
        
        for(int i=c;i<=9;i++)
        {
            sum+=i;
            path.push_back(i);
            backtracking(sum,n,k,i+1);
            sum-=i;
            path.pop_back();
        }
    }
};

组合

题目链接:组合
停更阶段学习算法题的一些总结_第35张图片
代码实现:

/*class Solution {
public:
    vector> combine(int n, int k)
    {
       vector> res;
       vector cur;
       
       dfs(res,cur,n,k);
       return res;
    }

    void dfs(vector>& res,vectorcur,int n,int k)
    {
        if(cur.size()==k)
        {
            res.push_back(cur);
            return;
        }

//这样倒着写在这题中是有好处的,可以少传一个参数进来,具体可以看此题图解。
        for(int i=n;i>=1;i--)
        {
            cur.push_back(i);
//注意这边一定是i-1,因为你要的是这个元素的之前一个元素。
            dfs(res,cur,i-1,k);
//同样回头时不要忘了pop
            cur.pop_back();
        }
    }
};*/

/*
//for循环中正过来的写法
class Solution {
public:
    vector> combine(int n, int k)
    {
       vector> res;
       vector cur;
//这时要多传入一个变量,不然解决不了问题,具体看此题图解
       int curn=1;
       dfs(res,cur,n,k,curn);
       return res;
    }

    void dfs(vector>& res,vectorcur,int n,int k,int curn)
    {
        if(cur.size()==k)
        {
            res.push_back(cur);
            return;
        }

        for(int i=curn;i<=n;i++)
        {
            cur.push_back(i);
//注意这边一定是i+1,因为你要的是这个元素的之后一个元素。
            dfs(res,cur,n,k,i+1);
//同样回头时不要忘了pop
            cur.pop_back();
        }
    }
};*/

//回溯写法
class Solution {
public:
    vector<vector<int>> res;
    vector<int> path;
    vector<vector<int>> combine(int n, int k)
    {
       backtracking(n,k,1);
       return res;
    }

    void backtracking(int n,int k,int startindex)
    {
        if(path.size()==k)
        {
            res.push_back(path);
            return;
        }
        for(int i=startindex;i<=n;i++)
        {
            path.push_back(i);
            backtracking(n,k,i+1);
            path.pop_back();
        }
    }
};

哈希

快乐数(set)

题目链接:快乐数
停更阶段学习算法题的一些总结_第36张图片
代码实现:

class Solution {
public:
    // 取数值各个位上的单数之和
    int getSum(int n) {
        int sum = 0;
        while (n) {
            sum += (n % 10) * (n % 10);
            n /= 10;
        }
        return sum;
    }
    bool isHappy(int n) {
        unordered_set<int> set;
        while(1) {
            int sum = getSum(n);
            if (sum == 1) {
                return true;
            }
            // 如果这个sum曾经出现过,说明已经陷入了无限循环了,立刻return false
            if (set.find(sum) != set.end()) {
                return false;
            } else {
                set.insert(sum);
            }
            n = sum;
        }
    }
};

两个数组的交集(set)

题目链接:两个数组的交集
停更阶段学习算法题的一些总结_第37张图片
代码实现:

class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
        unordered_set<int> result_set; // 存放结果
        unordered_set<int> nums_set(nums1.begin(), nums1.end());
        for (int num : nums2) {
            // 发现nums2的元素 在nums_set里又出现过
            if (nums_set.find(num) != nums_set.end()) {
                result_set.insert(num);
            }
        }
        return vector<int>(result_set.begin(), result_set.end());
    }
};

有效的字母异位词(数组)

题目链接:有效的字母异位词
停更阶段学习算法题的一些总结_第38张图片
代码实现:

class Solution {
public:
    bool isAnagram(string s, string t) {
        int record[26] = {0};
        for (int i = 0; i < s.size(); i++) {
            // 并不需要记住字符a的ASCII,只要求出一个相对数值就可以了
            record[s[i] - 'a']++;
        }
        for (int i = 0; i < t.size(); i++) {
            record[t[i] - 'a']--;
        }
        for (int i = 0; i < 26; i++) {
            if (record[i] != 0) {
                // record数组如果有的元素不为零0,说明字符串s和t 一定是谁多了字符或者谁少了字符。
                return false;
            }
        }
        // record数组所有元素都为零0,说明字符串s和t是字母异位词
        return true;
    }
};

多数元素(数组)

题目链接:多数元素
停更阶段学习算法题的一些总结_第39张图片
代码实现:

/*  排序法
class Solution {
public:
    int majorityElement(vector& nums) 
    {
        sort(nums.begin(), nums.end());
        return nums[nums.size() / 2];
    }
};
*/

/*分治法
class Solution {
public:
    int majorityElement(vector& nums) 
    {
         return getmajor(nums,0,nums.size()-1);
    }

     int getmajor(vector& nums,int left,int right)
    {
        if (left == right)
        {
            return nums[left];
        }
        int mid = (left+right) / 2;
        int left_majority = getmajor(nums, left, mid);
        int right_majority = getmajor(nums, mid + 1, right);
        if(left_majority==right_majority)
        {
            return left_majority;
        }

        int leftcount=0;
        int rightcount=0;
        for(int i=left;i<=right;i++)
        {
            if(nums[i]==left_majority)
            {
                leftcount++;
            }
            if(nums[i]==right_majority)
            {
                rightcount++;
            }
        }
        return leftcount>=rightcount?left_majority:right_majority;
    }
};
*/

/*哈希法
class Solution {
public:
    int majorityElement(vector& nums) 
    {
         unordered_map hash;
         for(int i=0;inums.size()/2)
             {
                 return it.first;
             }
         }
         return 0;
    }
};
*/

动态规划

最大子序和

题目链接:最大子序和
停更阶段学习算法题的一些总结_第40张图片
代码实现:

/*暴力法,时间上面是过不去
class Solution {
public:
    int maxSubArray(vector& nums)
    {    
          int n=nums.size();
          int max=INT_MIN;
          for(int i=0;imax)
                  {
                      max=sum;
                  }
              }
          }
          return max;
    }
};
*/

/*分治法
class Solution {
public:
    int maxSubArray(vector& nums)
    {    
       return getmax(nums,0,nums.size()-1);
    }
     
 int threemax(int a, int b, int c)
{
	if (a>b)
	{
		if (c>a)
		{
			return c;
		}
		else 
		{
			return a;
		}
	}
	else
	{
		if (b > c)
		{
			return b;
		}
		else
		{
			return c;
		}
	}
}
    int getmax(vector& nums,int left,int right)
    {
        if(left==right)
        {
            return nums[left];
        }
        int mid=left+(right-left)/2;
        int leftmax=getmax(nums,left,mid);
        int rightmax=getmax(nums,mid+1,right);
        int crossmax=midmax(nums,left,right);
        return threemax(leftmax,rightmax,crossmax);
    }

    int midmax(vector& nums,int left,int right)
    {
        if(left==right)
        {
            return nums[left];
        }
        int mid=left+(right-left)/2;
        int maxleft=nums[mid];
        int sumleft=nums[mid];
        int maxright=nums[mid+1];
        int sumright=nums[mid+1];
        for(int i=mid-1;i>=left;i--)
        {
           sumleft+=nums[i];
           if(sumleft>maxleft)
           {
               maxleft=sumleft;
           }
        }
        for(int i=mid+2;i<=right;i++)
        {
           sumright+=nums[i];
           if(sumright>maxright)
           {
               maxright=sumright;
           }
        }
        return maxleft+maxright;
    }
};*/

/*动态规划
class Solution {
public:
    int max(int a,int b)
    {
        return a>b?a:b;
    }

    int maxSubArray(vector& nums)
    {    
        int n=nums.size();
        vector dp;
        dp.resize(n,0);
        dp[0]=nums[0];
        int maxdp=nums[0];
        for(int i=1;imaxdp)
            {
                maxdp=dp[i];
            }
        }
        return maxdp;
    }
};*/

三级目录

你可能感兴趣的:(笔记)