C++ 笔试常用算法模板

C++ 笔试常用算法模板

  • 一、二叉树遍历
    • DFS
    • BFS
  • 二、回溯模板
  • 三、动态规划
    • 01背包
      • 朴素版本
      • 滚动数组优化
    • 完全背包
      • 朴素版本
      • 滚动数组优化
    • 最长递增子序列
      • 朴素版本
      • 贪心+二分优化
    • 最长公共子序列
    • 最长回文子串
  • 四、图
    • 建图
      • 邻接矩阵
      • 邻接表
    • 图的遍历
      • DFS
      • BFS
    • 拓扑排序
    • 并查集
    • 最小生成树
      • Kruskal
      • prim
    • 最短路径
      • BFS
      • 迪杰斯特拉 Dijkstra
        • 朴素版本
        • 堆优化版本
      • 弗洛伊德 Floyd-Warshall
  • 五、数组\区间
    • 前缀和
    • 二维前缀和
    • 差分
    • 二维差分
    • 树状数组
    • 线段树
    • 滑动窗口
    • 二分查找
    • 单调栈
    • 单调队列
  • 六、其他
    • 求质数/素数
    • 求约数
    • 快速幂
    • 离散化
    • 优先队列
    • string 删除与分割
    • C++ 进制
    • vector计数
    • vector逆序
    • vector二维数组排序

去年找工作时记录的一些常用模板(套路),实测能应付大多笔试或面试手撕题目。使用的前提是已经理解各类算法,仅做备忘和提示用。

关于面试手撕,一般以简单和中等难度题为主,大多都可以通过暴力求解,因为通常实在本地IDE或者其他Web IDE上手撕,没有LeetCode上那种时间或空间复杂度的限制。但是正是因为可以暴力求解,好的面试官会一步步引导或者提问有没有更优的解法(这时候即使忘了怎么写,但知道思路也比不知道好)、现有的复杂度是怎样的、是否考虑特殊情况或边界等等,算是了解基础的一个手段。
工作了后会发现,在面向工程的时候,的确需要精益求精来压榨算法的最大潜能,并且需要应对各种特殊情况(不要把用户想象得很聪明)。


一、二叉树遍历

DFS

void dfs(TreeNode* node) {if (!node) return;//相应的处理dfs(node->left);dfs(node->right);
}

如前序遍历:

class Solution {
public:void traversal(TreeNode* cur, vector<int>& vec) {if (cur == NULL) return;vec.push_back(cur->val);    // 中traversal(cur->left, vec);  // 左traversal(cur->right, vec); // 右}vector<int> preorderTraversal(TreeNode* root) {vector<int> result;traversal(root, result);return result;}
};

BFS

void bfs(TreeNode* root) {queue <TreeNode*> q;q.push(root);
while (!q.empty()) {int currentLevelSize = q.size();for (int i = 1; i <= currentLevelSize; ++i) {auto node = q.front(); q.pop();ret.back().push_back(node->val);if (node->left) q.push(node->left);if (node->right) q.push(node->right);}}
}

如层序遍历:

class Solution {
public: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;// 这里一定要使用固定大小size,不要使用que.size(),因为que.size是不断变化的for (int i = 0; i < size; i++) {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;}
};

二、回溯模板

适用场景用于解决暴力枚举的场景,例如枚举组合、排列等。

回溯三步:

  1. 递归函数的返回值以及参数
  2. 回溯函数终止条件
  3. 单层搜索的过程
vector<vector<int>> res;vector<int> path;
void dfs(参数) {if (满足递归结束) {res.push_back(path);return;}//递归方向for (xxx) {path.push_back(val);dfs();path.pop_back();}
}

void backtracking(参数) {if (终止条件) {存放结果;return;}for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {处理节点;backtracking(路径,选择列表); // 递归回溯,撤销处理结果}
}

如组合问题:

class Solution {
private:vector<vector<int>> result; // 存放符合条件结果的集合vector<int> path; // 用来存放符合条件结果void backtracking(int n, int k, int startIndex) {if (path.size() == k) {result.push_back(path);return;}for (int i = startIndex; i <= n; i++) {path.push_back(i); // 处理节点backtracking(n, k, i + 1); // 递归path.pop_back(); // 回溯,撤销处理的节点}}
public:vector<vector<int>> combine(int n, int k) {result.clear(); // 可以不写path.clear();   // 可以不写backtracking(n, k, 1);return result;}
};

三、动态规划

动规五步:

  1. 确定dp数组的定义
  2. dp数组的递推公式
  3. dp数组如何初始化
  4. dp数组遍历顺序
  5. 举例推导dp数组

01背包

适用场景

给出若干个物品,每个物品具有一定的价值价格,求解在限定的总额下可以获取的最大价值,注意,每个物品只能选取一次。

朴素版本

int n, C; //n个物品, C表示背包容量
int v[],  w[]; //v[i]表示第i个物品的价格/体积    w[i]表示第i个物品的价值
int dp[n+1][C+1]; //容器规模
//初始化 dp[0][j] j∈[0,C]
for (int i = 1 ; i <= n ; i++) {for (int j = 0 ; j <= C ; j++) {if (j >= v[i - 1]) dp[i][j] = max(dp[i-1][j], dp[i-1][j-v[i-1]] + w[i - 1]);else dp[i][j] = dp[i - 1][j];}
}
return dp[n][C];

先遍历物品,然后遍历背包重量的代码。

// weight数组的大小 就是物品个数
for(int i = 1; i < weight.size(); i++) { // 遍历物品for(int j = 0; j <= bagweight; j++) { // 遍历背包容量if (j < weight[i]) dp[i][j] = dp[i - 1][j];else dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);}
}

滚动数组优化

int n, C; //n个物品, C表示背包容量
int v[],  w[]; //v[i]表示第i个物品的价格/体积    w[i]表示第i个物品的价值
int dp[C+1]; //容器规模
//初始化 dp[j] j∈[0,C]
for (int i = 1 ; i <= n ; i++) {for (int j = C ; j >=  v[i - 1] ; j--) {dp[j] = max(dp[j], dp[j-v[i-1]] + w[i - 1]);}
}
return dp[C];

一维dp数组遍历顺序

for(int i = 0; i < weight.size(); i++) { // 遍历物品for(int j = bagWeight; j >= weight[i]; j--) { // 遍历背包容量dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);}
}

完全背包

适用场景

给出若干个物品,每个物品具有一定的价值价格,求解在限定的总额下可以获取的最大价值,注意,每个物品不限制选取次数。

朴素版本滚动数组优化的区别主要在于空间复杂度上,时间复杂度差不多,所以笔试的时候基本上没差别(空间很少会被卡)。

朴素版本

int n, C; //n个物品, C表示背包容量
int v[],  w[]; //v[i]表示第i个物品的价格/体积    w[i]表示第i个物品的价值
int dp[n + 1][C + 1]; //容器规模
//初始化 dp[0][j] j∈[0,C]
for (int i = 1 ; i <= n ; i++) {for (int j = 0 ; j <= C ; j++) {if (j >= v[i - 1]) dp[i][j] = max(dp[i-1][j], dp[i][j-v[i-1]] + w[i - 1]);else dp[i][j] = dp[i - 1][j];}
}
return dp[n][C];

滚动数组优化

int n, C; //n个物品, C表示背包容量
int v[],  w[]; //v[i]表示第i个物品的价格/体积    w[i]表示第i个物品的价值
int dp[C+1]; //容器规模
//初始化 dp[j] j∈[0,C]
for (int i = 1 ; i <= n ; i++) {for (int j = v[i-1] ; j <= C ; j++) {dp[j] = max(dp[j], dp[j-v[i-1]] + w[i - 1]);}
}
return dp[C];

最长递增子序列

适用场景

给定一个数组,求数组最长上升子序列的长度。

朴素版本可以求解的数据规模约为 1000。如果题目数据给到了10000或者更大,需要使用贪心+二分进行优化。

朴素版本

int lengthOfLIS(vector<int>& nums) {int dp[nums.size()];int ans = 1;dp[0] = 1;for (int i = 1 ; i < nums.size() ; i++) {dp[i] = 1;for (int j = 0 ; j < i ; j++) {if (nums[j] < nums[i]) {dp[i] = max(dp[j] + 1, dp[i]);}}ans = max(ans, dp[i]);}return ans;
}

贪心+二分优化

int lengthOfLIS(vector<int>& nums) {vector<int> ls;for (int num : nums) {auto it = lower_bound(ls.begin(), ls.end(), num);if (it == ls.end()) ls.push_back(num);else *it = num;}return ls.size();
}
class Solution {
public:int lengthOfLIS(vector<int>& nums) {if (nums.size() <= 1) return nums.size();vector<int> dp(nums.size(), 1);int result = 0;for (int i = 1; i < nums.size(); i++) {for (int j = 0; j < i; j++) {if (nums[i] > nums[j]) dp[i] = max(dp[i], dp[j] + 1);}if (dp[i] > result) result = dp[i]; // 取长的子序列}return result;}
};

最长公共子序列

适用场景

求两个数组或者字符的最长公共的子序列的长度。时间复杂度为O(n^2)

int longestCommonSubsequence(string text1, string text2) {int len1 = text1.size(), len2 = text2.size();vector<vector<int>> dp(len1 + 1, vector<int>(len2 + 1, 0));for (int i = 1 ; i <= len1 ; i++) {for (int j = 1 ; j <= len2 ;j++) {if (text1[i - 1] == text2[j - 1]) dp[i][j] = dp[i - 1][j - 1] + 1;else dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);}}return dp[len1][len2];
}

最长回文子串

适用场景

求解一个数组/字符串的最长回文子串的长度,时间复杂度为O(n^2)。

int longestPalindrome(string s) {int n = s.size();bool dp[n][n];int  mxlen = -1;for (int j = 0 ; j < n ; j++) {for (int i = j ; i >= 0 ; i--) {if (i == j) dp[i][j] = true;else if (i + 1 == j) dp[i][j] = s[i] == s[j];else {dp[i][j] = s[i] == s[j] && dp[i + 1][j - 1];}if (dp[i][j] && j - i + 1 > mxlen) {mxlen = j - i + 1;}}}return mxlen;
}

四、图

建图

建图的方式一般有两种,邻接矩阵和邻接表;

1.邻接矩阵,适用于稠密图【边的数量远大于点】

2.邻接表,适用于稀疏图【点的数量远大于边】

邻接矩阵

int graph[n][n];
for (int i = 0 ; i < m ; i++) {int a,b,w; //a和b存在一条边,权重为wcin >> a >> b >> w;graph[a][b] = graph[b][a] = w; // 如果是有向图则不需要建立双向边
}

邻接表

vector<vector<pair<int,int>>> graph(n, vector<int>(0));
for (int i = 0 ; i < m ; i++) {int a,b,w; //a和b存在一条边,权重为wgraph[a].push_back({b,w});graph[b].push_back({a,w});  // 如果是有向图则不需要建立双向边
}

图的遍历

DFS

vector<vector<pair<int,int>> graph;
vector<bool> vst;
void dfs(int node) {for (auto p: graph[node]) {int next = p.first, weight = p.second;if (!vst[next]) {vst[next] = true;dfs(next);// 如果需要回溯的话 , vst[next] = false;}}
}

BFS

vector<vector<pair<int,int>> graph;
vector<bool> vst;
void bfs() {queue<int> q;q.push(start);vst[start] = true;while (!q.size()) {int node = q.front();q.pop();for (int p : graph[node]) {int next = p.first, weight = p.second;if (!vst[next]) {vst[next] = true;q.push_back(next);}}}
}

岛屿最大面积

// 版本一
class Solution {
private:int count;int dir[4][2] = {0, 1, 1, 0, -1, 0, 0, -1}; // 四个方向void dfs(vector<vector<int>>& grid, vector<vector<bool>>& visited, int x, int y) {for (int i = 0; i < 4; i++) {int nextx = x + dir[i][0];int nexty = y + dir[i][1];if (nextx < 0 || nextx >= grid.size() || nexty < 0 || nexty >= grid[0].size()) continue;  // 越界了,直接跳过if (!visited[nextx][nexty] && grid[nextx][nexty] == 1) { // 没有访问过的 同时 是陆地的visited[nextx][nexty] = true;   // 标记访问count++;                        // 面积加一dfs(grid, visited, nextx, nexty);   //继续进行dfs}}}public:int maxAreaOfIsland(vector<vector<int>>& grid) {int m = grid.size(), n = grid[0].size();  //m行n列vector<vector<bool>> visited = vector<vector<bool>>(m, vector<bool>(n, false)); // m行n列的访问矩阵,默认为falseint result = 0; //最大岛屿面积for (int i = 0; i < m; i++) {   for (int j = 0; j < n; j++) {   //两层循环遍历矩阵内每个元素if (!visited[i][j] && grid[i][j] == 1) {    // 如果该区域没有被访问过,且是陆地count = 1;  // 统计陆地的面积,因为dfs处理下一个节点,所以这里遇到陆地了就先计数,dfs处理接下来的相邻陆地visited[i][j] = true;dfs(grid, visited, i, j); // 将与其链接的陆地都标记上 trueresult = max(result, count);    //求的是最大面积}}}return result;}
};

拓扑排序

适用场景

求解有向图的拓扑序、有向图判断是否成环。

vector<vector<int> graph;
vector<int> indegre; //存储每个节点的入度
queue<int> q;
for (int i = 0 ; i < n ; i++) {if (indegre[i] == 0)q.push(i);
}while(!q.size()) {int node = q.front(); q.pop();for (int next : graph[node]) {indegre[next]--;if (indegre[next] == 0) q.push(next);}
}

并查集

适用场景

用于解决 连通性问题。比如a和b相邻,b和c相邻,可以判断出a和c相邻。

class UF
{
private:vector<int> father;public:UF(int n){// 初始化for (int i = 0; i < n; i++){father.push_back(i);}}int find(int x){// 当节点的父节点是它本身时,说明找到了根节点if (father[x]!=x){father[x]=find(father[x]);}return father[x];}void join(int x, int y){father[find(x)] = find(y);}bool isConnected(int x, int y){return father[find(x)] == find(y);}};

最小生成树

适用场景

连接无向图中所有的节点的最小费用。

常见的算法有2种:

  1. kruskal:稀疏图,时间复杂度是 O ( m l o g m ) O(mlogm) O(mlogm)
  2. prim:稠密图,时间复杂度是 O ( n 2 ) O(n^2) O(n2)

n是点的数量,m是边的数量

Kruskal

int N = 1e5; //点的数量
int fa[N];void init(int n) {for (int i = 0;i < n ; i++) fa[i] = i;
}//找到x的根节点
int find(int x) {return x == fa[x] ? x : (fa[x] = find(fa[x]));
}//合并两个节点
void union(int x, int y) {fa[find(x)] = find(y);
}int kruskal(vector<vector<int>>& edges, int n, int m) {// edges[i] = {a,b,w} 表示a和b之间存在有一条边,权重为winit(n);sort(edges.begin(), edges.end(), [](const vector<int>& a, const vector<int>& b){return a[2]<b[2];});int ans = 0;for (auto arr : edges) {int a = arr[0], b = arr[1], w = arr[2];if (find(a) != find(b)) {union(a,b);ans += w;}}return ans;
}

prim

int prim(vector<vector<int>>& graph, int n) {vector<int> dis(n,INT_MAX);vector<bool> vst(n, false);int res = 0;for (int i = 0 ; i < n ; i++) {int min_index = -1;for (int j = 0 ; j < n ; j++) {if (!vst[j] && (min_index == -1 || dis[min_index] > dis[j]) min_index = j;}if (i != 0) res += dis[min_index];vst[min_index] = true;for (int j = 0 ; j < n ; j++) dis[j] = min(dis[j], graph[min_index][j]);}return res;
}

最短路径

适用场景

如果图中的节点的不存在边权(边权均为1),那么直接BFS即可求出最短路。

BFS

// 返回从st到达target的最短路径
int bfs(int st, int target, int n, vector<vector<int>>& graph) {queue<int> q;vector<bool> vst(n, false);q.push(st);vst[st] = true;int cnt = 0while (!q.size()) {int size = q.size();while(size--) {int node = q.front();q.pop();if (node == target) return cnt;for (int next: graph[node]) {if (vst[next]) continue;vst[next] = true;q.push(next);}}cnt++;}return -1;
}

迪杰斯特拉 Dijkstra

朴素版本
//求st为起点的最短路
//graph[i][j]: i到j的距离,不存在则初始化成最大值
//n表示节点的数量
void dijkstra(int st, int n, vector<vector<int>>& graph) {vector<int> dis(n, INT_MAX);vector<bool> vst(n, false);dis[st] = 0;for (int i = 0 ; i < n ; i++) {int x = -1;for (int j = 0 ; j < n ; j++) {if (!vst[j] && (x == -1 || dis[j] < dis[x])) x=j;}vst[x] = true;for (int j = 0 ; j < n ; j++) {dis[j] = min(dis[j], dis[x] + graph[x][j]);}}
}
堆优化版本
void dijkstra(int st, int n, vector<vector<pair<int,int>>>& graph) {vector<int> dis(n, INT_MAX);vector<bool> vst(n, false);dis[st] = 0;priority_queue<pair<int, int>, vector<pair<int, int>>, greater<>> pq; pq.push({0, st});while (!pq.size()) {int d = pq.top().first();int u = pq.top().second();pq.pop();if (vst[u]) continue;vist[u] = true;for (auto [v,w] : graph[u]) {if (dis[v] > dis[u] + w) {dis[v] = dis[u] + w;pq.push({dis[v], v});}}}
}

弗洛伊德 Floyd-Warshall

适用场景

多源最短路,可以在 O ( n 3 ) O(n^3) O(n3)的时间内求出任意两个点的最短距离。

vector<vector<int>> dp;
for (int i = 0 ; i < n ; i++) {for (int j = 0 ; j < n ; j++) {dp[i][j] = graph[i][j];}
}for (int k = 0 ; k < n ; k++) {for (int i = 0 ; i < n ; i++) {for (int j = 0 ; j < n ; j++) {dp[i][j] = min(dp[i][j], dp[i][k]+dp[k][j]);}}
}

五、数组\区间

前缀和

适用场景

多次求区间和。 O ( n ) O(n) O(n)的时间预处理出前缀和数组后,可以 O ( 1 ) O(1) O(1)求出区间的和。不支持区间修改。

vector<int> nums;
int n;vector<int> pre_sum(n + 1, 0);for (int i = 1 ; i <= n ; i++ ) pre_sum[i] = pre_sum[i-1] +nums[i-1];//查询区间和[left, right], 其中left,right是下标。
int sum = pre_sum[right+1] - pre_sum[left];

二维前缀和

vector<vector<int>> matrix;
int m = matrix.size(), n = matrix[0].size();
vector<vector<int>> pre(m + 1, vector<int>(n + 1));
for (int i = 1; i <= m; i++) {for (int j = 1; j <= n; j++) {pre[i][j] = pre[i - 1][j] + pre[i][j - 1] - pre[i - 1][j - 1] + matrix[i - 1][j - 1];}
}# 查询子矩阵的和 [x1,y1] [x2,y2]表示子矩阵的左上和右下两个顶点
int sum = pre[x2 + 1][y2 + 1] - pre[x1][y2 + 1] - pre[x2 + 1][y1] + pre[x1][y1];

差分

适用场景

给定一个数组和多次区间修改的操作,求修改后的数组。

int diff[10001];void update(int l, int r, int val) {diff[l] += v;if (r+1 <n) diff[r+1] -= v;
}int main() {int nums[] = {1,3,2,4,5};int n = sizeof(nums) / sizeof(int);diff[0] = nums[0];for(int i = 1; i < n; i++) diff[i] = nums[i] - nums[i - 1];//多次调用update后,对diff数组求前缀和可以得出 多次修改后的数组int* res = new int[n]; //修改后的数组res[0] = diff[0];for (int i=1 ; i<=n ; i++) res[i] += res[i-1] + diff[i];
}

二维差分

vector<vector<int>> matrix; //原数组int n, m ; //长宽vector<vector<int>> diff(n+1, vector<int>(m+1, 0));void insert(int x1, int y1, int x2, int y2, int d) {diff[x1][y1] += d;diff[x2+1][y1] -= d;diff[x1][y2+1] -= d;diff[x2+1][y2+1] += d;
}for (int i = 1 ; i <= n ; i++ ) {for (int j = 1 ; j <= m ; j++) {insert(i,j,i,j,matrix[i][j]);}
}int q; //修改次数
cin >> q;
while (q--) {int x1,y1,x2,y2,d;cin >> x1 >> y1 >> x2 >> y2 >> d;insert(x1,y1,x2,y2,d);
}for (int i = 1 ; i <= n ; i++) {for (int j = 1 ; j <= m ; j++) {matrix[i][j] = matrix[i-1][j] + matrix[i][j-1] - matrix[i-1][j-1] + diff[i][j];}
}// matrix就是复原后的数组

树状数组

适用场景

当我们进行 单点修改,然后进行区间 区间查询、单点查询的时候,适用树状数组可以在 O ( l o g n ) O(logn) O(logn)的复杂度求解。

vector<int> tree(MAXN, 0); int lowbit(int x) {return x&(-x);
}
// 单点修改,第i个元素增加x
inline void update(int i, int x)
{for (int pos = i; pos < MAXN; pos += lowbit(pos))tree[pos] += x;
}
// 查询前n项和
inline int query(int n)
{int ans = 0;for (int pos = n; pos; pos -= lowbit(pos))ans += tree[pos];return ans;
}// 查询区间[a,b]的和
inline int query(int a, int b)
{return query(b) - query(a - 1);
}

线段树

适用场景

当我们需要区间修改、区间查询、单点查询的时候,可以使用线段树,能够在 O ( l o g n ) O(logn) O(logn)的复杂度下求解。

#define MAXN 100005
typedef long long ll;
ll n, m, A[MAXN], tree[MAXN * 4], mark[MAXN * 4]; // A原数组、 tree线段树数组、mark懒标记数组
inline void push_down(ll p, ll len)
{mark[p * 2] += mark[p];mark[p * 2 + 1] += mark[p];tree[p * 2] += mark[p] * (len - len / 2);tree[p * 2 + 1] += mark[p] * (len / 2);mark[p] = 0;
}
// 建树
void build(ll l = 1, ll r = n, ll p = 1)
{if (l == r)tree[p] = A[l];else{ll mid = (l + r) / 2;build(l, mid, p * 2);build(mid + 1, r, p * 2 + 1);tree[p] = tree[p * 2] + tree[p * 2 + 1];}
}
void update(ll l, ll r, ll d, ll p = 1, ll cl = 1, ll cr = n)
{if (cl > r || cr < l)return;else if (cl >= l && cr <= r){tree[p] += (cr - cl + 1) * d;if (cr > cl)mark[p] += d;}else{ll mid = (cl + cr) / 2;push_down(p, cr - cl + 1);update(l, r, d, p * 2, cl, mid);update(l, r, d, p * 2 + 1, mid + 1, cr);tree[p] = tree[p * 2] + tree[p * 2 + 1];}
}
ll query(ll l, ll r, ll p = 1, ll cl = 1, ll cr = n)
{if (cl > r || cr < l)return 0;else if (cl >= l && cr <= r)return tree[p];else{ll mid = (cl + cr) / 2;push_down(p, cr - cl + 1);return query(l, r, p * 2, cl, mid) + query(l, r, p * 2 + 1, mid + 1, cr);}
}
/**
1.输入数组A,注意下标从[1,n]。
2.调用update(l,r,d)函数,这里的l和r并不是下标。
3.调用query(l,r) 这里的l和r并不是下标
*/

滑动窗口

适用场景

求解数组/字符串 满足某个约束的最长/最短子数组/子串。需要满足二段性才可以使用。

for (int l = 0, r = 0 ; r < n ; r++) {//如果右指针的元素加入到窗口内后,根据题目判断进行滑动左指针while (l <= r && check()) l++;
}

二分查找

适用场景

满足二段性的数列中,求某一个值的位置、大于某个值的最小值、小于某个值的最大值。时间复杂度为 O ( l o g n ) O(logn) O(logn)

// 区间划分为[l,mid] 和 [mid+1,r],选择此模板
int bsec1(int l, int r)
{while (l < r){int mid = (l + r)/2;if (check(mid)) r = mid;else l = mid + 1;}return r;
}// 区间划分为[l,mid-1] 和 [mid,r],选择此模板
int bsec2(int l, int r)
{while (l < r){int mid = ( l + r + 1 ) /2;if (check(mid)) l = mid;else r = mid - 1;}return r;
}

单调栈

适用场景

求序列中下一个更大、更小的元素。时间复杂度 O ( n ) O(n) O(n)

stack<int> s;
for (int i = 0; i < n; ++i) {while (!s.empty() && nums[i] > nums[s.top()]) {int top = s.top();s.pop();//此时说明 nums[top]的下一个更大的元素为nums[i]}s.push(i);
}

单调队列

适用场景

求解移动区间的最值问题。时间复杂度 O ( n ) O(n) O(n)

vector<int> res(nums.size() - k + 1); //存储长长度为k的每一个区间的最值
deque<int> queue;
for (int i = 0; i < nums.size(); i++) {if (!queue.empty() && i - k + 1 > queue.front()) queue.pop_front();while (!queue.empty() && nums[queue.back()] < nums[i]) queue.pop_back();queue.push_back(i);if (i >= k - 1) res[i - k + 1] = nums[queue.front()]; 
}
return res;

六、其他

求质数/素数

适用场景

筛法求质数,时间复杂度约为 O ( n ) O(n) O(n)

int cnt;
vector<int> primes;
bool st[N];
void get_primes(int n) {for (int i = 2 ; i <= n ; i++) {if (!st[i]) {primes.push_back(i);for (int j = i + i ; j <= n ; j += i) st[j] = true;}}
}

求约数

适用场景

根号N的时间复杂度下求出一个数字的所有约数。

vector<int> get_divisors(int n) {vector<int> res;for (int i = 1; i <= n / i ; i++) {if (n % i == 0) {res.push_back(i);if (i != n / i) res.push_back(n / i);}}sort(res.begin(), res.end());return res;
}

快速幂

适用场景

快速的求出x的y次方。时间复杂度 O ( l o g n ) O(logn) O(logn)

long long fast_pow(int x, int y, int mod) {long long res = 1;while (y > 0) {if (y % 2 == 1) {res = (long long)res * x % mod;}x = (long long)x * x % mod;y /= 2;}return res;
}

离散化

适用场景

当数据值比较大的时候,可以映射成更小的值。例如[101,99,200] 可以映射成[1,0,2]。

int A[MAXN], C[MAXN], L[MAXN]; //原数组为A
memcpy(C, A, sizeof(A)); // 复制原数组到C中
sort(C, C + n); // 数组排序
int l = unique(C, C + n) - C; // 去重
for (int i = 0; i < n; ++i)L[i] = lower_bound(C, C + l, A[i]) - C + 1; // 二分查找

优先队列

priority_queue<int, vector<int>, [](int a, int b) {return a>b;}; // 队列存储int类型比较
priority_queue<int, vector<vector<int>>, [](const vector<int>& a, const vector<int>& b) {return a[1]>b[1];}; // 队列存储vector类型,按照第二个元素进行排序

string 删除与分割

C++中要从string中删除所有某个特定字符, 可用如下代码

str.erase(std::remove(str.begin(), str.end(), 'a'), str.end());

字符串分割:

// 使用字符串分割
void Stringsplit(const string& str, const string& splits, vector<string>& res)
{if (str == "")		return;//在字符串末尾也加入分隔符,方便截取最后一段string strs = str + splits;size_t pos = strs.find(splits);int step = splits.size();	//记录分隔字符串的长度// 若找不到内容则字符串搜索函数返回 nposwhile (pos != strs.npos){string temp = strs.substr(0, pos);if(temp.size()!=0)res.push_back(temp);//去掉已分割的字符串,在剩下的字符串中进行分割strs = strs.substr(pos + step, strs.size());pos = strs.find(splits);}
}

C++ 进制

C++中以四种进制进行输出

#include <iostream>
#include <bitset>using namespace std;int main()
{int a=64;cout<<(bitset<32>)a<<endl;//二进制32位输出cout<<oct<<a<<endl;//八进制cout<<dec<<a<<endl;//十进制cout<<hex<<a<<endl;//十六进制return 0;
}

vector计数

//记录与首元素相同的数字的个数,因为数组有序,并且最多出现4次,所以不用遍历所有区间int cnt = count(nums.begin(), nums.begin() + 4, nums[0]);
vector<int> nums_new(nums.begin() + 1,nums.end());//去掉一个首元素(顺子的第一个)
nums_new.erase(find(nums_new.begin(), nums_new.end(),nums[0] + 1));//去掉一个首元素+1(顺子的第二个)                          

vector逆序

reverse(ans.begin(),ans.end()){ans.rbegin(),ans.rend()}

vector二维数组排序

#include <iostream>
#include <vector>
#include <algorithm>bool customSort(const std::vector<int>& a, const std::vector<int>& b) {if (a[0] < b[0]) {return true;  // 第一列升序排列} else if (a[0] > b[0]) {return false;} else {return a[1] > b[1];  // 第二列降序排列}
}void sortTwoDimensionalArray(std::vector<std::vector<int>>& arr) {std::sort(arr.begin(), arr.end(), customSort);
}

可以打印下来,笔试前可以抱抱佛脚抢记一波。

话说现在GPT工具如此方便,简单中等题秒出答案,总感觉失去了考核的意义。就像开挂一样,在没有监管或监管不力的情况下,对于诚信者怎么不算是一种劣币驱逐良币呢。
现在这种情况,是不是某种程度上也在筛选会不会变通、会不会使用工具的候选人呢?求职者如过江之鲫,会有企业care公不公平吗?

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/147265.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

CTC loss 博客转载

论文地址&#xff1a; https://www.cs.toronto.edu/~graves/icml_2006.pdf 为了对应这个图&#xff0c;我们假设一种符合的模型情况&#xff1a; 英文OCR&#xff0c;37个类别&#xff08;26个小写字母10个汉字空格&#xff09;&#xff0c;最大输出长度8个字符 模型预测结果…

PCL 计算点云的平均密度(方法一)

目录 一、概述 1.1原理 1.2实现步骤 1.3应用场景 二、代码实现 2.1关键函数 2.2完整代码 三、实现效果 PCL点云算法汇总及实战案例汇总的目录地址链接&#xff1a; PCL点云算法与项目实战案例汇总&#xff08;长期更新&#xff09; 一、概述 本文将介绍如何计算点云的…

如何避开学习和研究机器人方向无价值的知识节约时间

往昔 这是一篇十年前就想写&#xff0c;但是一直没有实力和勇气落笔的文字。 如今 简约 授之以鱼&#xff0c;不如授之以渔。 啰嗦 机器人方向如何简单判定这个知识是否有价值。 只谈一个方向&#xff0c;就是这个知识点是“死”还是“活”&#xff1f; 什么是“死”&am…

element-ui表格操作大全

一、基础表格展示 数据绑定&#xff1a; 在el-table元素中注入data对象数组&#xff0c;在el-table-column&#xff08;列&#xff09;中使用prop属性来对应对象中的键名&#xff0c;使用label属性定义列名 元素案例内容&#xff1a; <el-table border :data"userL…

举例说明:自然语言处理实战项目

自然语言处理&#xff08;Natural Language Processing, NLP&#xff09;是人工智能领域的一个重要分支&#xff0c;旨在使计算机能够理解、解释和生成人类语言。以下是一些NLP实战项目的示例&#xff1a; 1. 情感分析&#xff08;Sentiment Analysis&#xff09; 项目描述: …

【LLM学习之路】9月16日 第六天

【LLM学习之路】9月16日 第六天 损失函数 L1Loss 可以取平均也可以求和 参数解析 input &#xff08;N&#xff0c;*&#xff09; N是batchsize&#xff0c;星号代表可以是任意维度 不是输入的参数&#xff0c;只是描述数据 target 形状要同上 MSELoss平方差 CrossEntr…

(done) 声音信号处理基础知识(5) (Types of Audio Features for Machine Learning)

参考&#xff1a;https://www.youtube.com/watch?vZZ9u1vUtcIA 声学特征描述了声音&#xff0c;不同特征捕捉声音的不同方面性质 声学特征有助于我们构建智能声学系统 声学特征分类有&#xff1a; 1.抽象等级 2.时域视野 3.音乐的部分 4.信号域 5.机器学习方法 如下图展示…

力扣中等 33.搜索旋转排序数组

文章目录 题目介绍题解 题目介绍 题解 首先用 153. 寻找旋转排序数组中的最小值 的方法&#xff0c;找到 nums 的最小值的下标 i。 然后分类讨论&#xff1a; 如果 target>nums[n−1]&#xff0c;在 [0,i−1] 中二分查找 target。 如果 target≤nums[n−1]&#xff0c;那…

51单片机——独立按键

一、独立按键对应单片机P3管脚&#xff0c;如图 二、按键点亮LED灯 #include <STC89C5xRC.H> void main() { while(1) { if(P300) { P200; } else { P201; } } } 当按键为0时&#xff0c;代表按下&#xff0c;所以当P30按下时&#xff0c;让P20&#xff1d;0&#…

二叉树(二)深度遍历和广度遍历

一、层序遍历 广度优先搜索&#xff1a;使用队列&#xff0c;先进先出 模板&#xff1a; 1、定义返回的result和用于辅助的队列 2、队列初始化&#xff1a; root非空时进队 3、遍历整个队列&#xff1a;大循环while(!que.empty()) 记录每层的size以及装每层结果的变量&a…

leetcode第十三题:罗马数字转整数

罗马数字包含以下七种字符: I&#xff0c; V&#xff0c; X&#xff0c; L&#xff0c;C&#xff0c;D 和 M。 字符 数值 I 1 V 5 X 10 L 50 C 100 D 500 M 1000 例如&#x…

LeetCode[中等] 215. 数组中的第 K 个最大元素

给定整数数组 nums 和整数 k&#xff0c;请返回数组中第 k 个最大的元素。 请注意&#xff0c;你需要找的是数组排序后的第 k 个最大的元素&#xff0c;而不是第 k 个不同的元素。 你必须设计并实现时间复杂度为 O(n) 的算法解决此问题。 思路&#xff1a;基于快排改进的快速…

【全网最全】2024华为杯数学建模C题高质量成品查看论文!【附带全套代码+数据】

题 目&#xff1a; ___基于数据驱动下磁性元件的磁芯损耗建模 完整版获取&#xff1a; 点击链接加入群聊【2024华为杯数学建模助攻资料】&#xff1a;http://qm.qq.com/cgi-bin/qm/qr?_wv1027&kxtS4vwn3gcv8oCYYyrqd0BvFc7tNfhV7&authKeyedQFZne%2BzvEfLEVg2v8FOm%…

计算机基础(Computer Fundamentals)

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:Linux运维老纪的首页…

【学习笔记】手写Tomcat 四

目录 一、Read 方法返回 -1 的问题 二、JDBC 优化 1. 创建配置文件 2. 创建工具类 3. 简化 JDBC 的步骤 三、修改密码 优化返回数据 创建修改密码的页面 注意 测试 四、优化响应动态资源 1. 创建 LoginServlet 类 2. 把登录功能的代码放到 LoginServlet 类 3. 创…

关于有源蜂鸣器及无源蜂鸣器的区别及驱动各类单片机案例

关于有源蜂鸣器及无源蜂鸣器的区别及驱动各类单片机案例 有源蜂鸣器与无源蜂鸣器区别有源蜂鸣器无源蜂鸣器模块化有源蜂鸣器及无源蜂鸣器驱动方式的说明 有源、无源蜂鸣器代码驱动总结 有源蜂鸣器与无源蜂鸣器区别 有源蜂鸣器与无源蜂鸣器区别在于是否有振荡源。 有源蜂鸣器即…

【BEV 视图变换】Ray-based(2): 代码复现+画图解释 基于深度估计、bev_pool

paper&#xff1a;Lift, Splat, Shoot: Encoding Images from Arbitrary Camera Rigs by Implicitly Unprojecting to 3D code&#xff1a;https://github.com/nv-tlabs/lift-splat-shoot 一、完整复现代码(可一键运行)和效果图 import torch import torch.nn as nn import mat…

8587 行编辑程序

### 思路 1. **初始化栈**&#xff1a;创建一个空栈用于存储有效字符。 2. **读取输入**&#xff1a;读取输入的行数 n&#xff0c;然后逐行读取字符。 3. **处理字符**&#xff1a; - 如果是 #&#xff0c;则弹出栈顶字符&#xff08;如果栈不为空&#xff09;。 - 如果…

谷歌的AI反击战:创始人谢尔盖·布林的回归与大模型组合的未来

近年来&#xff0c;随着AI技术的迅猛发展&#xff0c;尤其是ChatGPT等大语言模型的出现&#xff0c;全球科技格局正发生剧烈变化。作为曾经引领AI潮流的谷歌&#xff0c;在这场竞争中逐渐失去了领头羊的地位。然而&#xff0c;谷歌的创始人之一谢尔盖布林&#xff08;Sergey Br…

黑马智数Day1

src文件夹 src 目录指的是源代码目录&#xff0c;存放项目应用的源代码&#xff0c;包含项目的逻辑和功能实现&#xff0c;实际上线之后在浏览器中跑的代码就是它们 apis - 业务接口 assets - 静态资源 &#xff08;图片&#xff09; components - 组件 公共组件 constants…