算法基础模板

前言

记录c++算法基础的模板
就像cin.tie(0);cout.tie(0);可以提速一样,提高代码编写速度。


一、基础算法

1、快速排序模板

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)/2];
    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);
}

2、归并排序模板

void merge_sort(vector<int> &q, int l, int r){
    if(l >= r) return;
    int mid = (r + l)/2;
    merge_sort(q, l, mid);
    merge_sort(q, mid+1, r);
    
    int k =0, i = l, j = mid+1;
    vector<int> tmp(r - l + 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(int i = l, j = 0; i <= r; i++, j++)
        q[i] = tmp[j];
}

3、高精度加法

vector<int> add(vector<int> &A, vector<int> &B){
    vector<int> C;
    int val = 0, carry = 0;
    int i = 0, j = 0;
    while(carry || i < A.size() || j < B.size()){
        val = carry;
        if(i < A.size()){val += A[i++];}
        if(j < B.size()){val += B[j++];}
        carry = val / 10;
        val = val % 10;
        C.push_back(val);
    }
    return C;
}

4、高精度减法

void sub(vector<int> &A, vector<int> &B, vector<int> &C){
    int carry = 0, val = 0;
    int i = 0, j = 0;
    while(carry || i < A.size() || j < B.size()){
        val = carry;
        if(i < A.size()) {val += A[i++];}
        if(j < B.size()) {val -= B[j++];}
        carry = val < 0 ? -1 : 0;
        val = (val + 10) % 10;
        C.push_back(val);
    }
    while(C.size()>1&&C.back()==0)
    {
        C.pop_back();
    }
}

5、高精度乘法

vector<int> mul(vector<int> &A, int &b){
    vector<int> C;
    int i = 0;
    int val = 0, carry = 0;
    while(carry || i < A.size()){
        if(i < A.size())    {val = A[i++] * b + carry;}
        else    {val = carry;}
        carry = val / 10;
        val = val % 10;
        C.push_back(val);
    }
    return C;
}

6、前缀和

前缀和值得是sum[i]为a数组0-i个值的和。
用于解决数组[l,k]的和,在K组查询中
从O(n)到O(1)复杂度。

vector<int> sum(n + 1);
    sum[0] = 0;
    for(int i = 1; i <= n ; i++){
        sum[i] = sum[i - 1] + q[i - 1];
    }

7、差分

差分和前缀和为逆运算。
用于解决数组[l,k]中全部加C,在K组查询中
从O(n)到O(1)复杂度。

for(int i = 1; i <= n ; i++){
        temp[i] = array[i] - array[i - 1];
    }

8、双指针

int res = 0;
    for(int i = 0, j = 0; i < n; i++){
        s[a[i]] ++;//为计数数组,存放该位的个数。
        while(s[a[i]] > 1){
            s[a[j]] --;
            j ++;
        }
        res = max(res, i - j + 1);

哈希表

unordered_set<int> haxi;
    int i = 0, j = 0, res = 0;
    while(i < n){
        if(haxi.count(a[i]) == 0){
            haxi.insert(a[i]);
            i++;
        }
        else{
            haxi.erase(a[j]);
            j++;
        }
        res = max(res, i - j);
    }

9、位运算

cout<<(n >> k & 1);//取二进制第K位
cout<< lowbit(x);//例1010返回10 	实现x & (~x + 1)及( x & -x)
//取出x中的1的个数
int x, res;
    while(cin>>x){
        res = 0;
        while(x != 0)  x -= lowbit(x), res++;
        cout<<res<<" ";
    }

10、离散化

思想:将点放在数组里,利用二分查找下标

typedef pair<int, int> PII;
const int N = 300010;
int a[N], s[N];

vector<int> all;
vector<PII> add, query;

int main(){
    int n, m;
    cin>>n>>m;
    for(int i = 0; i < n; i++)
    {
        int x, y;
        cin>>x>>y;
        add.push_back({x,y});
        all.push_back(x);
    }
    for(int i = 0; i < m; i++){
        int x, y;
        cin>>x>>y;
        query.push_back({x,y});
        all.push_back(x);
        all.push_back(y);
    }
    
    sort(all.begin(),all.end());
    all.erase(unique(all.begin(),all.end()), all.end());
    for(auto i : add){
        int t = lower_bound(all.begin(), all.end(), i.first) - all.begin() + 1;
        a[t] += i.second;
    }
    for(int i = 1; i <= all.size();i++){
        s[i] = s[i-1] + a[i];
    }
    
    for(auto i : query){
        int l = lower_bound(all.begin(), all.end(), i.first) - all.begin() + 1; 
        int r = lower_bound(all.begin(), all.end(), i.second) - all.begin() + 1;
        cout<< s[r] - s[l -1]<<endl;
    }
    
    return 0;
}

11、区间合并

void merge(vector<PII> &segs){
    vector<PII> res;
    sort(segs.begin(), segs.end());
    int l = -2e9, r = -2e9;
    for(auto seg : segs){
        if(r < seg.first){
            if(l != -2e9)
                res.push_back({l,r});
            l = seg.first;
            r = seg.second;
        }
        else
            r = max(seg.second, r);
    }
    if(l != -2e9)
        res.push_back({l,r});
    segs = res;
}

二、数据结构

1、单链表(数组模拟)

//静态链表	开始的为空-1;
const int N = 100010;
int val[N], next_node[N], idx, head;

void init(){
    head = -1;
    idx = 0;
}
void insert_head(int x){
    val[idx] = x;
    next_node[idx] = head;
    head = idx ++;
}
void insert(int k, int x){
    val[idx] = x;
    next_node[idx] = next_node[k];
    next_node[k] = idx++;
}
void remove(int k){
    next_node[k] = next_node[next_node[k]];
}
/**动态链表
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */

2、双链表(数组模拟)

//开始0作为最左边,1作为最右边,idx从第2开始
const int N = 100010;
int idx, r[N], l[N], val[N];
void init(){
    r[0] = 1;
    l[1] = 0;
    idx = 2;
}
void remove(int k){
    r[l[k]] = r[k];
    l[r[k]] = l[k];
}
void insert_k_right(int k, int x){
    val[idx] = x;
    r[idx] = r[k];
    l[idx] = k;
    l[r[k]] = idx;
    r[k] = idx;
    idx ++;
}
void insert_k_left(int k, int x){
    insert_k_right(l[k],x);
}

3、栈(数组模拟)

const int N = 100010;
int stk[N], tt;

void push(int x){
    stk[++tt] = x;
}
void pop(){
    tt --;
}
void empty(){
    if(tt > 0)
        cout<<"NO"<<endl;
    else
        cout<<"YES"<<endl;
}
void query(){
    cout<<stk[tt]<<endl;
}

4、队列(数组模拟)

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

void push(int x){
    q[++tt] = x;
}
void pop(){
    hh ++;
}
void empty(){
    if(hh <= tt > 0)
        cout<<"NO"<<endl;
    else
        cout<<"YES"<<endl;
}
void query(){
    cout<<q[hh]<<endl;
}

5、单调栈(数组模拟)

单调栈解决求前一个小于x的值

#include<iostream>
using namespace std;

const int N = 100010;
int stk[N], tt;

int main(){
    int n;
    cin>>n;
    while(n--){
        int x;
        cin>>x;
        while(tt && stk[tt] >= x)   tt--;//这里当i < j && stk[i] > stk[j]时,i无用
        if(!tt)  cout<<"-1 ";
        else    cout<<stk[tt]<<" ";
        stk[++tt] = x;
    }
    return 0;
}

6、单调队列(数组模拟)

解决在滑动窗口中输出最大值和最小值

const int N= 1000010;
int a[N];
int q[N];//存放下标
int tt = -1, hh;

int main(){
    int n, k;
    cin>>n>>k;
    for(int i = 0; i < n; i++)  cin>>a[i];
    
    for(int i = 0; i < n; i++){
        if(hh <= tt && i - q[hh] + 1 > k)   hh++;//多出来以后将hh++
        while(hh <= tt && a[q[tt]] >= a[i])     tt--;//当i值比队列尾小时,队列尾没用
        q[++tt] = i;
        
        if(i >= k - 1)  printf("%d ",a[q[hh]]);
    }
    cout<<endl;
    
    tt = -1, hh = 0;
    for(int i = 0; i < n; i++){
        if(hh <= tt && i - q[hh] + 1 > k)   hh++;
        while(hh <= tt && a[q[tt]] <= a[i])     tt--;
        q[++tt] = i;
        
        if(i >= k - 1)  printf("%d ",a[q[hh]]);
    }
    
    return 0;
}

7、KMP字符串匹配

//find函数查找全部位置
int position;
while(position != s.npos){
    position = s.find(p,position);
    if(position != s.npos)  {
        cout<<position<<" ";
        position ++;
    }
}
#include<iostream>
using namespace std;

const int N = 1e5 + 10, M = 1e6 + 10;
int ne[N];
char p[N], s[M];

int main(){
    int n,m;
    cin>> n;
    for(int i = 1; i <= n; i++) cin>>p[i];
    cin>>m;
    for(int i = 1; i <= m; i++) cin>>s[i];
    
    //求ne[j]数组
    for(int i = 2, j = 0; i <= n; i++){
        while(j && p[i] != p[j + 1]) j = ne[j];
        if(p[i] == p[j + 1]) j++;
        ne[i] = j;
    }
    for(int i = 1, j = 0; i<= m; i++){
        while(j && s[i]!= p[j+1]) j = ne[j];
        if(s[i] == p[j+1]) j++;
        if(j == n){
            cout<<i - n<<" ";
            j = ne[j];
        }
    }
    
    return 0;
}

8、Trie字符串统计

Trie主要是快速插入和查询string

const int N = 1e5 + 10;
int son[N][26], cnt[N], idx;
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];
}

9、并查集

int find_p(int x){//找祖宗结点 + 路径压缩
    if(p[x] != x)    p[x] = find_p(p[x]);
    return p[x];
}
if(op == 'M'){
     p[find_p(y)] = find_p(x);
}
else if(op == 'Q'){
    if(find_p(x) == find_p(y))  cout<<"Yes"<<endl;
    else    cout<<"No"<<endl;
}

10、字符串哈希

#include<iostream>
#include<algorithm>
using namespace std;
//字符串哈希,不一定完全对,看人品,99.99%
//思想:将string转化成P进制的数,利用前缀和求
typedef unsigned long long ULL;
const int N = 1e5 + 10, P = 131;//P一般为131或13331(经验),冲突率0.001%
ULL h[N], p[N];//p存放位数的次方, h作为前缀和

ULL get(int l, int r){
    return h[r] - h[l - 1] * p[r - l + 1];
}


int main(){
    int n, m;
    cin>>n>>m;
    string s;
    cin>>s;
    s = " " + s;
    
    p[0] = 1;
    for(int i = 1; i <= n; i++){
        p[i] = p[i - 1] * P;
        h[i] = h[i - 1] * P + s[i]; 
    }
    while(m--){
        int l1, r1, l2, r2;
        cin>>l1>>r1>>l2>>r2;
        if(get(l1, r1) == get(l2, r2))  cout<<"Yes"<<endl;
        else    cout<<"No"<<endl;
    }

    return 0;
}

三、搜索与图论

1、DFS(数字全排列)

#include <iostream>
using namespace std;

const int N = 10;
int n;
int state[N], path[N];

void dfs(int u){
    if(u == n){
        for(int i = 0; i < n; i++)  cout<<path[i]<<" ";
        cout<<endl;
        return;
    }
    for(int i = 1; i <= n; i++){
        if(!state[i]){
            state[i] = true;
            path[u] = i;
            dfs(u + 1);
            
            state[i] = false;
            path[u] = 0;
        } 
    }
}

int main(){
    cin>>n;
    dfs(0);
    return 0;
}

2、BFS(迷宫问题)

#include <iostream>
#include <queue>
#include <cstring>
using namespace std;

typedef pair<int, int> PII;

const int N = 110;
int n, m;
int path[N][N];
int g[N][N];
int bfs(){
    memset(path, -1, sizeof path);
    queue<PII> q;
    path[0][0] = 0;
    q.push({0, 0});
    
    int dx[4] = {1, -1, 0, 0}, dy[4] = {0, 0, 1, -1};
    
    while(q.size()){
        auto t = q.front();
        q.pop();
        for(int i = 0; i < 4; i++){
            int x = t.first + dx[i], y = t.second + dy[i];
            if(x >= 0 && x < n && y >=0 && y <m && path[x][y] == -1 && g[x][y] == 0)
            {
                q.push({x, y});
                path[x][y] = path[t.first][t.second] + 1;
            }
        }
        
    }
    
    return path[n - 1][m -1];
}


int main(){
    cin>>n>>m;
    for(int i = 0; i < n; i++)
        for(int j = 0; j < m; j++)
            cin>>g[i][j];
    cout << bfs() <<endl;
}

3、树与图的深度优先遍历(树的重心)

#include <iostream>
#include <vector>
using namespace std;

const int N = 1e5 + 10, M = N * 2;
int n, ans = N;
bool state[N];
vector<int> q[N];

void add(int a, int b){
    q[a].push_back(b);
    q[b].push_back(a);
}
int dfs(int u){
    state[u] = true;
    int sum = 1, size = 0;
    for(int i = 0; i < q[u].size(); i++){
        int j = q[u][i];
        if(state[j]) continue;
        int s = dfs(j);
        sum += s;
        size = max(size, s);
    }
    size = max(size, n - sum);
    ans = min(ans, size);
    return sum;
}

int main(){
    cin>>n;
    int a, b;
    while(cin>>a>>b){
        add(a, b), add(b, a);
    }
    dfs(1);
    cout<< ans <<endl;
    return 0;
}

4、树与图的广度优先遍历(树的层次)

#include <iostream>
#include <queue>
#include <vector>
#include <cstring>
using namespace std;

const int N = 1e5 + 10;
int n, m;
int path[N];
vector<int> h[N];
queue<int> q;

void add(int a, int b){
    h[a].push_back(b);
}

void bfs(){
    path[1] = 0;
    q.push(1);
    
    while(q.size()){
        auto t = q.front();
        q.pop();
        for(int i = 0; i < h[t].size(); i++){
            int j = h[t][i];
            if(path[j] != -1) continue;
            path[j] = path[t] + 1;
            q.push(j);
        }
    }
}

int main(){
    memset(path, -1, sizeof path);
    cin>>n>>m;
    while(m --){
        int a, b;
        cin>>a>>b;
        add(a, b);
    }
    bfs();
    cout << path[n] <<endl;
    return 0;
}

5、有向图的拓扑序列

#include <iostream>
#include <vector>
#include <cstring>
using namespace std;

const int N = 1e5 + 10;
int n, m;
int d[N], q[N];
vector<int> h[N];

void add(int a, int b){
    h[a].push_back(b);
    d[b] ++;
}

bool topsort(){
    int hh = 0, tt = -1;
    for(int i = 1; i <= n; i++){
        if(!d[i]){
            q[++ tt] = i;
        }
    }
    while(hh <= tt){
        int t = q[hh ++];
        for(int i = 0; i < h[t].size(); i++){
            int j = h[t][i];
            if(-- d[j] == 0){
                q[++ tt] = j;
            }
        }   
    }
    return tt == n - 1;
}


int main(){
    cin>>n>>m;
    int a, b;
    while(cin>>a>>b){
        add(a, b);
    }
    if(!topsort())  cout<<"-1 "<<endl;
    else{
        
        for(int i = 0; i < n; i++)
            cout<<q[i]<<" ";
        
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值