408常用算法模板

目录

基础算法

        双指针

        动态规划

树的遍历算法

        深度优先遍历 (DFS):递归实现

        广度优先遍历(BFS):队列实现框架

图论算法

         朴素迪杰斯特拉算法

         堆优化版本dijkstra

         Floyd算法

         prim算法

         kruska算法

         拓扑排序

 排序算法

        快速排序

        归并排序

查找算法

        二分查找

        哈希查找

待更新。。。       


基础算法

树的遍历算法


        广度优先遍历 (dfs):递归实现

        前序遍历 中序后序只需要改变访问代码段的位置即可,省略了

   

typedef struct node {
    int left, right, w=-1; // w存储结点序号,初始化为-1,-1代表不存在
} node;
bool vis[1005];
void dfs(int root) {
    if (root != -1) {
        vis[root] = 1;
        dfs(root.left);
        dfs(root.right);
    }
}
  • 广度优先遍历(BFS):队列实现框架
  • 邻接矩阵实现
  • int g[1005][1005]; // 邻接矩阵存图
    bool vis[1005];    // 数组大小考试的时候随便开个大的就行
    memset(vis,0,sizeof(vis));//访问数组初始化
    void bfs(int x) {
        // 从x节点开始遍历
        queue<int> qe;  // 队列;
        qe.push(x); // 入队
        while (qe.size()) {
            // 队列不为空
            int j = qe.front();           // 取队头
            qe.pop();                     // 出队
            vis[j] = 1;                   // 标记j结点已经访问过
            for (int i = 0; i < n; i++) { // n是顶点数
                if (g[j][i] == 1 && vis[i] == 0) { // 遍历第j行,把与j相连且未访问过的顶点入队
                    qe.push(i);
                }
            }
        }
    }
  • 邻接表实现
  • bool vis[1005];    // 数组大小考试的时候随便开个大的就行
    vector<int> g[1005];//建立邻接表
    /*
    建图过程省略....
    如果想在i结点后加j顶点,则执行g[i].push_back(j);
    */
    void init(){
        memset(vis,0,sizeof(vis));
    }
    void bfs(int x) {
        // 从x节点开始遍历
        queue<int> qe;  // 队列;
        qe.push(x); // 入队
        while (qe.size()) {
            // 队列不为空
            int j = qe.front();           // 取队头
            qe.pop();                     // 出队
            vis[j] = 1;                   // 标记j结点已经访问过
            for(int i=0;i<g[j].size();i++){
                if(vis[i]==0)push(i);//未被访问过的话,入队
            }
        }
        return 0;
    }

图论算法

  • 最短路径算法:Dijkstra模板、Floyd-Warshall框架
  • (spfa算法可解决带负环的问题,感兴趣可了解,408不考)
        朴素迪杰斯特拉算法

(时间复杂度为o(n^2+m))//n为顶点数,m为边数(熟悉掌握)

const int N=1005;
int g[N][N];  // 存储每条边
int dist[N];  // 存储x号点到每个点的最短距离
bool vis[N];   // 存储每个点的最短路是否已经确定

// 求x号点到n号点的最短路,如果不存在则返回-1
int dijkstra(int x){
    memset(dist, 0x3f, sizeof dist);//将x节点到各顶点的距离初始化为无穷
    dist[x] = 0;

    for (int i = 0; i < n - 1; i ++ ){
        int t = -1;     // 在还未确定最短路的点中,寻找距离最小的点
        for (int j = 0; j < n; j ++ )//遍历邻接矩阵
            if (!vis[j] && (t == -1 || dist[t] > dist[j]))
                t = j;//如果j结点没有被访问过且距离已经存下来的最短距离还要小的话,更新顶点

        // 用t更新其他点的距离
        for (int j = 0; j < n; j ++ )
            dist[j] = min(dist[j], dist[t] + g[t][j]);

        vis[t] = true;//标记t结点被访问过
    }
    return dist[n];//如果dis[x][j]返回的是0x3f,则说明x到j没有路
}
堆优化版本dijkstra

(时间复杂度o(mlogn))(可了解扩展,适用于稀疏图)

    vector<int> g[105];//邻接表
    int w[105][105];
    int dist[105];
    bool vis[105];
    int dijkstra(int x,int n){
    memset(dist, 0x3f, sizeof dist);
    dist[x] = 0;
    priority_queue<PII, vector<PII>, greater<PII>> heap;//优先队列,小根堆
    heap.push({0, x});      // first存储距离,second存储节点编号
    while (heap.size()){
        auto t = heap.top();
        heap.pop();
        int ver = t.second, distance = t.first;
        //获取当前节点和距离
        if (vis[ver]) continue;
        vis[ver] = true;
        for (int i = 0; i < g[ver].size(); i++){   
            //if(g[ver][i]<0)continue;
            int j=g[ver][i];
            if (dist[j] > dist[ver] + w[ver][j]){
                dist[j] = dist[ver] + w[ver][j];
                heap.push({dist[j], j});
            }
        }
    }
    if (dist[n] == 0x3f3f3f3f) return -1;
    return dist[n];
}
 Floyd算法

(多源最短路,时间复杂度o(n^3))

初始化:
    for (int i = 1; i <= n; i ++ )
        for (int j = 1; j <= n; j ++ )
            if (i == j) d[i][j] = 0;
            else d[i][j] = INF;

// 算法结束后,d[a][b]表示a到b的最短距离
void floyd()
{
    for (int k = 1; k <= n; k ++ )
        for (int i = 1; i <= n; i ++ )
            for (int j = 1; j <= n; j ++ )
                d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
}
  • 并查集(会在克鲁斯卡尔中用到)
  • int p[N];         // 存储每个点的祖宗节点
    int find(int x) { // 返回x所在集合的代理人
        if (p[x] == x)
            return p[x];
        return p[x] = find(p[x]);
    }
    void join(int a, int b) { // 合并a和b所在的集合
        p[find(a)] = find(b); // 合并
    }
    signed main() {
        // 初始化,假定节点编号是1~n
        for (int i = 1; i <= n; i++)
            p[i] = i;
        return 0;
    }
    

  • 最小生成树:Prim算法模板、Kruskal算法框架
  • prim算法
  • (时间复杂度o(n^2+m))适用于稀疏图
  • int n;      // n表示点数
    int g[N][N];        // 邻接矩阵,存储所有边,g[i][j]存储的是i和j之间的权值
    int dist[N];        // 存储其他点到当前最小生成树的距离
    bool vis[N];     // 存储每个点是否已经在生成树中
    
    // 如果图不连通,则返回INF(值是0x3f3f3f3f), 否则返回最小生成树的树边权重之和
    int prim(){
        memset(dist, 0x3f, sizeof dist);//初始化各个顶点到最小树的距离为很大(理解为书上的无穷)
    
        int res = 0;//树的最小权重
        for (int i = 0; i < n; i ++ ){
            int t = -1;
            for (int j = 0; j < n; j ++ )//找到一个到最小生成树最近的顶点
                if (!vis[j] && (t == -1 || dist[t] > dist[j]))
                    t = j;
            //i从1开始。n次迭代,加n-1条边,第一次找到距离最短的边。
            if (i && dist[t] == INF) return INF;
            //如果加边过程中找到的可加入最短距离还是无穷,则不存在
    
            if (i) res += dist[t];
            vis[t] = true;
    
            for (int j = 0; j < n; j ++ ) dist[j] = min(dist[j], g[t][j]);
            //更新各个节点到最小生成树的最短距离
        }
    
        return res;
    }
    kruska算法
  • (并查集实现,时间复杂度o(mlogn))

  • int n, m;       // n是点数,m是边数
    int p[N];       // 并查集的父节点数组
    
    struct Edge
    {
        int a, b, w;
    
        bool operator< (const Edge &W)const{
            return w < W.w;//排序的时候按照边从小到大排序
        }
    }edges[M];
    
    int find(int x)     // 并查集核心操作
    {
        if (p[x] == x) return x;
        return p[x]=find(p[x]);
    }
    void join(int a,int b){
        p[find(a)]=find(b);
    }
    int kruskal(){
        sort(edges, edges + m);//将已经存储的边从小到大排序.sort函数直接默写快排模板即可
    
        for (int i = 1; i <= n; i ++ ) p[i] = i;    // 初始化并查集
    
        int res = 0, cnt = 0;//res存储最小生成树权重,cnt存储已经加入的边
        for (int i = 0; i < m; i ++ )
        {
            int a = edges[i].a, b = edges[i].b, w = edges[i].w;
    
            if (find(a) != find(b))     // 如果两个连通块不连通,则将这两个连通块合并
            {
                join(a,b);
                res += w;
                cnt ++ ;
            }
        }
    
        if (cnt < n - 1) return INF;//无法构造出最小生成树
        return res;
    }。
  • 拓扑排序
  • 有向无环图排序模板(主要用bfs实现)
  • queue<int> qe;
    int g[105][105]; // 邻接矩阵
    bool topsort() {
        // d[i] 存储点i的入度
        int cnt = 0;
        for (int i = 0; i < n - 1; i++)
            if (!d[i]) {
                qe.push(i);
                cnt++;
            } // 初始化,把入度为0的结点全都加入队列中
    
        while (qe.size()) {
            int t = qe.front(); // 取队头
            qe.pop();           // 出队
            for (int i = 0; i < n; i++) {
                int j = e[i];
                if (--d[j] == 0) {
                    qe.push(j);
                    cnt++;
                } // 当前入度为1的点,在取掉j后,入度就变成了0,入队
            }
        }
        // 如果所有点都入队了,说明存在拓扑序列;否则不存在拓扑序列。
        return cnt==n;//cnt个数为n则说明top排序存在
    }
    
  • 图的遍历:DFS递归与非递归模板、BFS队列实现框架

排序算法

  • 快速排序
  • int q[1005];//待排序数组
    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);//递归排序小部分
    }
    

  • 归并排序
  • 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];
    }

查找算法

  • 二分查找
  • 在有序数组中查找具体的数,如果无序的话可以先sort一下
  • bool check(int x) {/* ... */} // 检查x是否满足某种性质
    
    // 区间[l, r]被划分成[l, mid]和[mid + 1, r]时使用:
    int bsearch_1(int l, int r)
    {
        while (l < r)
        {
            int mid = l + r >> 1;
            if (check(mid)) r = mid;    // check()判断mid是否满足性质
            else l = mid + 1;
        }
        return l;
    }
    // 区间[l, r]被划分成[l, mid - 1]和[mid, r]时使用:
    int bsearch_2(int l, int r)
    {
        while (l < r)
        {
            int mid = l + r + 1 >> 1;
            if (check(mid)) l = mid;
            else r = mid - 1;
        }
        return l;
    }

  • 哈希查找
  • 冲突处理框架(开放地址法、链地址法)
  • (1) 拉链法
        int h[N], e[N], ne[N], idx;
    
        // 向哈希表中插入一个数
        void insert(int x)
        {
            int k = (x % N + N) % N;
            e[idx] = x;
            ne[idx] = h[k];
            h[k] = idx ++ ;
        }
    
        // 在哈希表中查询某个数是否存在
        bool find(int x)
        {
            int k = (x % N + N) % N;
            for (int i = h[k]; i != -1; i = ne[i])
                if (e[i] == x)
                    return true;
    
            return false;
        }
    
    (2) 开放寻址法
        int h[N];
    
        // 如果x在哈希表中,返回x的下标;如果x不在哈希表中,返回x应该插入的位置
        int find(int x)
        {
            int t = (x % N + N) % N;
            while (h[t] != null && h[t] != x)
            {
                t ++ ;
                if (t == N) t = 0;
            }
            return t;
        }
    

待更新。。。       

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值