代码改变世界

算法模板[c++]

2022-05-05 21:17  幻霞  阅读(22)  评论(0编辑  收藏  举报

二分查找:

class Solution {
public:// l:left,r:right,m:middle,t:target 
    int binarySearch(vector<int>& nums, int t) {
        //定义t在左右闭合的区间里[l, r)  
        int l = 0,r = nums.size();
        while (l < r) {
            int m = l + ((r - l) >> 1);// >>1等价于除以2,但是位运算更快 
            if (nums[m] > t) {
                r = m;
            } else if (nums[m] < t) {
                l = m + 1;
            } else { 
                return m;
            }
        }
        return -1;
    }
};

KMP(Knuth-Morris-Pratt)字符串匹配算法:

这个其实是求模式串的next数组,数组中的每个值直接决定当字符不等价时应该从哪里重新去比对而不是仅仅把开始位置从i=0迭代为i=1(即暴力法).

void kmp(int* next, const string& s){
    int j = next[0] = -1;
    for(int i = 1; i < s.size(); i++){
        while (j>=0 && s[i] != s[j + 1]) j = next[j];
        if(s[i] == s[j + 1]) j++; 
        next[i] = j;
    }
}
// next[0] = -1;//第一个肯定没出现过,所以是-1,next数组-1和0都代表下一次的比较只能和暴力一样,没有可以节省的步数 
//  s[i] == s[j + 1]:判断当前位置和上一个串的对应位置是否相同 
//while (j>=0 && !(s[i] == s[j + 1])) j = next[j];如果不相同且上一个串没有子串 就  更新j的位置为其本身的回退值直到j到了一个无重复子串或者和当前字符一致

深度优先遍历和广度优先遍历以及递归回溯:

struct TreeNode {
    int val;
    TreeNode *left;
    TreeNode *right;
    TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
//三种遍历方式(前序遍历如下就是根在前面(根左右),中序就是根在中间(左根右),后续就是根在后面(左右根)) 
void traversal(TreeNode* cur, vector<int>& vec) {
    if (cur == NULL) return;
    vec.push_back(cur->val);    // 中 ,同时也是处理节点逻辑的地方
    traversal(cur->left, vec);  //
    traversal(cur->right, vec); //
}
//求深度 
int getDepth(TreeNode* node) {
    if (node == NULL) return 0;
    return 1 + max(getDepth(node->left), getDepth(node->right));
}
//求节点数
int countNodes(TreeNode* root) {
    if (root == NULL) return 0;
    return 1 + countNodes(root->left) + countNodes(root->right);
} 
// 深度优先遍历 (前序)
vector<int> preorderTraversal(TreeNode* root) {
    vector<int> result;
    stack<TreeNode*> st;
    if (root != NULL) st.push(root);
    while (!st.empty()) {
        TreeNode* node = st.top();
        if (node != NULL) {
            st.pop();
            if (node->right) st.push(node->right);  //
            if (node->left) st.push(node->left);    //
            st.push(node);                          //
            st.push(NULL);                          
        } else {
            st.pop();
            node = st.top();
            st.pop();
            result.push_back(node->val);            // 节点处理逻辑
        }
    }
    return result;
}
//  大概思路就是根在栈顶,用一个NULL节点来标记判断要处理的每一个节点
//  总的来说递归比这清晰简洁太多了,如果没有特别要求还是用递归遍历比较省事,不过递归太多层可能会爆函数栈(比如单子树深树),而这个不会 

//广度优先遍历(层序) 
vector<vector<int>> levelOrder(TreeNode* root) {
    queue<TreeNode*> que;
    if (root != NULL) que.push(root);
    vector<vector<int>> result;
    while (!que.empty()) {
        int size = que.size();
        vector<int> vec;
        for (int i = 0; i < size; i++) {// 这里一定要使用固定大小size,不要使用que.size()
            TreeNode* node = que.front();
            que.pop();
            vec.push_back(node->val);   // 节点处理的逻辑
            if (node->left) que.push(node->left);
            if (node->right) que.push(node->right);
        }
        result.push_back(vec);
    }
    return result;
}
// 代码的思路和笔者之前使用的不太一样,笔者是使用一个标记点来处理新的层,这里使用当前的长度其实更好(当前的长度即代表层节点数),
// 只是要注意循环中的操作会使队列长度变化,因此不能使用que.size()

//递归回溯的基本结构 
void backtracking(参数) {
    if (终止条件) {
        存放结果;
        return;
    }

    for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
        处理节点;
        backtracking(路径,选择列表); // 递归
        回溯,撤销处理结果
    }
}
//递归回溯其实和深度优先遍历很类似,只不过一些算法题目中的递归回溯往往需要剪枝,因为路径可能重复,循环或者有无数条,深度优先遍历往往是去遍历已知的解集,当然也可以剪枝来有意识地去搜索 

并查集:

    int n = 1005; // 根据题意而定 
    int root[1005];

    // 并查集初始化
    void init() {
        for (int i = 0; i < n; ++i) {
            root[i] = i;
        }
    }
    //每个节点的父节点都是自己,这是最初的状态,即互不连接的状态 
    
    
    // 并查集里寻根的过程
    int find(int u) {
        return u == root[u] ? u : root[u] = find(root[u]);
    }
    //如果u与root[u]相等,那么说明没有进行连接,否则的话,看看它的根节点是否还能再寻找到根,如果是就更新并返回根节点,否则直接返回. 
    
    
    // 将v->u 这条边加入并查集
    void join(int u, int v) {
        u = find(u);
        v = find(v);
        if (u == v) return ;
        root[v] = u;
    }
    //探寻两点的根,如果是一样的,就不用管,如果不是,那v的根应该为u
    //这里注意,这个逻辑是单向的,并查集的目的是找到几团不相连的路径集,如果再赋值 root[u] = v;则会让寻根函数进入死循环 
    
    
    // 判断 u 和 v是否找到同一个根
    bool same(int u, int v) {
        u = find(u);
        v = find(v);
        return u == v;
    }

参考:leetcode-master/算法模板.md at master · youngyangyang04/leetcode-master (github.com)