目录
基础算法
-
双指针
-
for (int i = 0, j = 0; i < n; i ++ ) { while (j < i && check(i, j)) j ++ ; // 具体问题的逻辑 } 常见问题分类: (1) 对于一个序列,用两个指针维护一段区间 (2) 对于两个序列,维护某种次序,比如归并排序中合并两个有序序列的操作 //第二种 for (int i = 0, j < n; i < j;) { if (check()) i++; else j--; // check函数为具体问题判断逻辑 } -
动态规划
-
分享丨【算法题单】动态规划(入门/背包/划分/状态机/区间/状压/数位/树形/优化)- 讨论 - 力扣(LeetCode)(可当做扩展)
树的遍历算法
广度优先遍历 (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; }



7396

被折叠的 条评论
为什么被折叠?



