数据结构
栈
栈 stack FILO(first in last out) 如一个试管,只有一端可以控制进入输出,且进入输出都只能在栈顶进行,将压入栈顶为push,弹出栈顶为pop
手写栈
#include <bits/stdc++.h>using namespace std;
char s[20000];int n = 0;
char pop() { return s[n--];}void push(int val) { s[++n] = val;}
int main(void) { return 0;}STL
#include <bits/stdc++.h>using namespace std;
int main(void) { stack<int> s; // 定义一个栈,栈中元素类型为int s.push(5); // 将元素5压入栈顶 s.pop(); // 弹出栈顶元素, 但不返回其值 s.top(); // 返回栈顶元素, 但不删除该元素 s.size(); // 返回栈中元素的个数 s.empty(); // 判断栈是否为空,空则返回true,非空返回false
return 0;}括号匹配
#include <bits/stdc++.h>using namespace std;int main() { stack<char> s; string str; cin >> str; for (int i = 0; i < str.length(); i++) { if (str[i] == '(') { s.push('('); } else { if (s.empty()) { cout << "no" << endl; return 0; } s.pop(); } } if (s.empty()) { cout << "yes" << endl; } else { cout << "no" << endl; } return 0;}队列
队列 FIFO(first in first out)
手写队列
#include <bits/stdc++.h>using namespace std;
int q[20000];int head = 1, tail = 0;
int pop() { if (head <= tail) { return q[head++]; }}
void push(int val) { q[++tail] = val;}
int main(void) { return 0;}STL
#include <bits/stdc++.h>using namespace std;
int main(void) { queue<int> q; // 定义一个队列,队列中元素类型为int q.push(5); // 将元素5入队,放入队尾 q.pop(); // 队首元素出队,但不返回其值 q.front(); // 返回队首元素,但不删除该元素 q.size(); // 返回队列中元素的个数 q.empty(); // 判断队列是否为空,空则返回true,非空返回false
return 0;}约瑟夫环
#include <bits/stdc++.h>using namespace std;int main() { int n, m; queue<int> q; cin >> n >> m; for (int i = 1; i <= n; i++) { q.push(i); } int cur = 0; while (!q.empty()) { cur++; if (cur == m) { cout << q.front() << " "; q.pop(); cur = 0; } else { q.push(q.front()); q.pop(); } } return 0;}图
拓扑排序(用邻接表)
课程安排
#include <bits/stdc++.h>using namespace std;unordered_map<int, vector<int>> adj; // 定义个桶(即邻接表),桶的值为整型,位置为一个vector数组int inDegree[200000];int main() { int n,m; cin>>n>>m; stack<int> s; for (int i = 1; i<= m; i++) { //该for循环为读图操作 int x,y; cin >> x >> y; adj[x].push_back(y); //邻接表操作, 在课程编号为x的课程的vector数组里放入编号为y的课程,化为图即为x -> y inDegree[y] += 1; //前置课程多了一个,等于该后置课程的入度加一 } for (int i= 1; i<= n; i++) { if (inDegree[i]==0) { //将入度为零的(即没有前置课程)的课程放入栈s中, s.push(i); } } while (!s.empty()) { //当栈s不为空时 int cur = s.top(); //cur为当前入度最少的结点 cout << s.top() << " "; s.pop(); //将该入度最少的结点弹出栈,即学习完该课程 for (int i = 0; i < adj[cur].size(); i++) { //该for循环作用为找出在刚刚弹出的结点(即刚学完的课程) 的桶中(即图中该结点指向的结点 ),在弹出该结点后入度为零的结点,将其压入栈顶,表示目前要最先学的课程 if (inDegree[adj[cur][i]] - 1 == 0) { s.push[adj[cur][i]]; } } } return 0;} //注意:有多个结点入度为零时,那栈顶弹出的只有其中之一,接下来栈顶又会压入删去刚才弹出的那个结点后入度为零的,会把最开始的剩余结点覆盖,输出顺序不太对,但没关系,不用管二叉树
二叉树 相当于一个族谱 想要知道每个家族成员,那就需要遍历
有几种不同的遍历方法,用如下二叉树来举例
1
2 3
4 5 6
7
1、层序遍历
从上往下一层一层的遍历 1 23 456 7
前序遍历
递归思想,到了无法满足顺序时返回到上一层(下同),顺序是 中左右 1 2 4 5 7 3 6
#include <bits/stdc++.h>using namespace std;struct TreeNode { int l, r;};
TreeNode tree[200000];
void preOrder(int root) { // void没有返回值 if (root == -1) { return; } preOrder(tree[root].r); preOrder(tree[root].l); printf("%d ", root);}
int main() { int n; scanf("%d", &n); for (int i = 1; i <= n; i++) { scanf("%d %d", &tree[i].l, &tree[i].r); } preOrder(1); return 0;}中序遍历
顺序是 左中右, 也可以通过所有数在竖列上的位置从左到右遍历,4 2 7 5 1 6 3
#include <bits/stdc++.h>using namespace std;struct TreeNode { int l, r;};
TreeNode tree[200000];
void preOrder(int root) { // void没有返回值 if (root == -1) { return; } preOrder(tree[root].l); printf("%d ", root); preOrder(tree[root].r);}
int main() { int n; scanf("%d", &n); for (int i = 1; i <= n; i++) { scanf("%d %d", &tree[i].l, &tree[i].r); } preOrder(1); return 0;}后序遍历
顺序是 左右中, 4 7 5 2 6 3 1
#include <bits/stdc++.h>using namespace std;struct TreeNode { int l, r;};
TreeNode tree[200000];
void preOrder(int root) { // void没有返回值 if (root == -1) { return; } preOrder(tree[root].l); preOrder(tree[root].r); printf("%d ", root);}
int main() { int n; scanf("%d", &n); for (int i = 1; i <= n; i++) { scanf("%d %d", &tree[i].l, &tree[i].r); } preOrder(1); return 0;}Hash
Hash(哈希) 数组下标下可以装字符,数字
单词统计
#include <bits/stdc++.h>//#include <tr1/unordered_map> //版本问题,比赛中加上using namespace std;//using namespace std::tr1;int main() { int n; int ans = 0; unordered_map<string, int> cnt; //第一个为元素值 第二个为元素位置 cin >> n; for (int i = 1; i <= n; i++) { string tmp; cin >> tmp; cnt[tmp]++; ans = max(ans, cnt[tmp]); } cout << ans << endl;}字母统计
#include <bits/stdc++.h>//#include <tr1/unordered_map>using namespace std;//using namespace std::tr1;int main() { string s; int ans = 0; unordered_map<char, int> cnt; cin >> s; for (int i = 0; i < s.length(); i++) { cnt[s[i]]++; ans = max(ans, cnt[s[i]]); } cout << ans << endl; return 0;}数字统计
#include <bits/stdc++.h>//#include <tr1/unordered_map>using namespace std;//using namespace std::tr1;int main() { int n; cin >> n; unordered_map<int, int> cnt; for (int i = 1; i <= n; i++) { int tmp; cin >> tmp; cnt[tmp]++; } cout << cnt.size() << endl; return 0;}算法
高精度
高精度:因为int型范围有限,无法存储大整数,所以用字符串形式输入大整数,再转成int型存入数组中,用数组进行运算
加法
#include <bits/stdc++.h>using namespace std;
int a[501], b[501], c[502];
int main() { string x, y; cin >> x >> y; int len = max(x.length(), y.length()); for (int i = len-1; i >= 0; i--) a[len-i] = x[i] - '0'; //将x倒着存进数组a,将字符转为整型 for (int i = len-1; i >= 0; i--) b[len-i] = y[i] - '0'; for (int i = 1; i <= len; i++) { c[i] += a[i] + b[i]; c[i+1] = c[i] / 10; //模拟进位 c[i] %= 10; } if (c[len+1]) { //进位可能导致长度加一 len++; } for (int i = len; i >= 1; i--) { //倒回来输出 cout << c[i]; } return 0;}乘法
#include <bits/stdc++.h>using namespace std;
int a[5010], b[5010], c[5020];
int main() { string x, y; cin >> x >> y; int lenx = x.length(); int leny = y.length(); for (int i = lenx-1; i >= 0; i--) a[lenx-i] = x[i] - '0'; for (int i = leny-1; i >= 0; i--) b[leny-i] = y[i] - '0'; for (int i = 1; i <= lenx; i++) { //模拟竖式乘法 for (int j = 1; j <= leny; j++) { c[i+j-1] += a[i] * b[j]; //错位相加 } } int len = lenx + leny; //乘得的积的长度不大于两数长度之和 for (int i = 1; i <= len; i++) { c[i+1] += c[i] / 10; //模拟进位 c[i] %= 10; } for (; !c[len] ;) { //去掉先导零 len--; } for (int i = max(1,len); i >= 1; i--) { //倒回来输出 cout << c[i]; } return 0;}重载板子
#include <bits/stdc++.h>using namespace std;struct Int{ int len; int a[155]; void init(){ len=0; memset(a,0,sizeof a); } Int(){init();} Int(int x){ init(); if(x==0) ++len; while(x){ a[++len]=x%10; x/=10; } } Int operator=(int x){ init(); if(x==0) ++len; while(x){ a[++len]=x%10; x/=10; } return *this; } Int operator=(Int x){ init(); len=x.len; for(int i=1;i<=len;i++) a[i]=x.a[i]; return *this; } void print(){ for(int i=len;i>0;i--){ printf("%d",a[i]); } }};Int operator+(const Int a,const Int b){ Int ans; ans.len=max(a.len,b.len); for(int i=1;i<=ans.len;i++){ if(i<=a.len) ans.a[i]+=a.a[i]; if(i<=b.len) ans.a[i]+=b.a[i]; ans.a[i+1]+=ans.a[i]/10; ans.a[i]%=10; } while(ans.a[ans.len+1]>0) ans.a[ans.len+1]+=ans.a[ans.len]/10,ans.a[ans.len]%=10,ans.len++; return ans;}
Int a[4] = {1,1,1,1};Int b[4] = {1,999,1,1};
int main() { (a[1]+b[1]).print(); //输出为1000 return 0;}排序
归并
主要体现分治思想,复杂度O(nlogn)
int a[MAXN]; int b[MAXN];
void merge(int l, int mid, int r) { memset(b,0,sizeof(b)); int i = l, j = mid+1, k = 0; while(i<=mid&&j<=r) { if (a[i]<=a[j]) b[++k] = a[i++]; else b[++k] = a[j++]; } while(i<=mid) b[++k] = a[i++]; while(j<=r) b[++k] = a[j++]; for (int i = 1; i <= k; i++) a[l++] = b[i];}
void mergesort(int l, int r) { if (l < r) { int mid = l+r>>1; mergesort(l,mid); mergesort(mid+1,r); merge(l,mid,r); } return;}快排
在数组中随机选择一个数作为标准,比标准小的放标准左边,大的放右边,然后递归分治
#include <bits/stdc++.h>using namespace std;
int a[2000000];
void qsort(int a[], int l, int r) { int i = l, j = r; int p = a[rand() % (r - l + 1) + l]; // ① 如何选取基准数(pivot)No.2 a[(r - l) / 2 + l] No.3 a[l] while (i <= j) { while (a[i] < p) i++; // ② 与pivot相等的数,要不要交换? while (a[j] > p) j--; if (i <= j) { // ③ 为什么要写if?为什么要加等号? swap(a[i++], a[j--]); } }
if (l < j) qsort(a, l, j); // ④ 这两条语句什么含义 if (i < r) qsort(a, i, r);}
int main(void) { int n;
scanf("%d", &n); for (int i = 1; i <= n; i++) { scanf("%d", &a[i]); }
qsort(a, 1, n);
for (int i = 1; i <= n - 1; i++) { printf("%d ", a[i]); } printf("%d\n", a[n]);}暴力枚举
枚举,很暴力! 将数据全部枚举一遍,加以判断或统计 如果全部枚举会超范围,就要考虑更换枚举的元素或剪枝(减少枚举量),即优化枚举
循环枚举-烤鸡
#include <bits/stdc++.h>#define rep(a, i, j) for (int a = i; a <= j; a++) //利用宏(define)构造语句的功能,简化for循环,前者等价于后者using namespace std;int main() { int n, a, b, c, d, e, f, g, h, i, j; scanf("%d", &n); int ans = 0; rep(a, 1, 3) rep(b, 1, 3) rep(c, 1, 3) rep(d, 1, 3) rep(e, 1, 3) //优雅的枚举 rep(f, 1, 3) rep(g, 1, 3) rep(h, 1, 3) rep(i, 1, 3) rep(j, 1, 3) if (a+b+c+d+e+f+g+h+i+j == n) ans++; printf("%d\n", ans); rep(a, 1, 3) rep(b, 1, 3) rep(c, 1, 3) rep(d, 1, 3) rep(e, 1, 3) rep(f, 1, 3)rep(g, 1, 3)rep(h, 1, 3)rep(i, 1, 3)rep(j, 1, 3) if (a+b+c+d+e+f+g+h+i+j == n) printf("%d %d %d %d %d %d %d %d %d %d\n", a, b, c, d, e, f, g, h, i, j); return 0;}排列枚举-全排列
即将每种情况排列,用STL中函数next_permutation(start,end)即可解决 next_permutation函数求的是给定范围中按顺序的下一个排列,若没有下一个排列则返回0
#include <bits/stdc++.h>using namespace std;
int a[11];
int main() { int n; cin >> n; for (int i = 1; i <= n; i++) { a[i] = i; //因为next需要数据范围,所以将i赋值到数组,而不是直接用i } do { //用do-while是为了使最后一个排序能够输出,while和for需要特判 for (int i = 1; i <= n; i++) { cout << setw(5) << a[i]; //场宽5 } cout << endl; } while (next_permutation(a+1, a+1+n)); //指针操作 next所到之处,就是当前次数下的排列 return 0;}递归
递归 就是套娃,自己算自己 相当于一个公司,用函数调用手下去算 通过函数中的return不断返回,当不断返回然后不能返回了就输出或达到某种要求输出 一定要有退出条件
递归分为两种:数值类和非数值类, 数值类更加简单,根据函数定义直接找出递归边界和递归语句(注意要用if-else连接) 非数值类则需模拟,找规律,再重复数值类步骤
斐波那契数列
#include <bits/stdc++.h>using namespace std;int calc(int a) { if (a == 1 || a == 2) { return 1; } return calc(a - 1) + calc(a - 2);}int main() { int k; cin >> k; cout << calc(k) << endl; return 0;}最大公约数
#include <bits/stdc++.h>using namespace std;int gcd(int a, int b) { if (b == 0) { return a; } return gcd(b, a % b);}int main() { int a, b; cin >> a >> b; cout << gcd(a, b) << endl; return 0;}二叉树最大节点
#include <bits/stdc++.h>using namespace std;struct TreeNode { int val; int l, r;};vector<TreeNode> tree(200000);int getMax(int root) { if (root == -1) { return INT_MIN; } int lMax = getMax(tree[root].l); int rMax = getMax(tree[root].r); int ans = max(lMax, rMax); ans = max(ans, tree[root].val); printf("%d ", ans); return ans;}int main() { int n; scanf("%d", &n); for (int i = 1; i <= n; i++) { scanf("%d %d %d", &tree[i].val, &tree[i].l, &tree[i].r); } getMax(1); return 0;}二分法
二分法,就是把一组数砍成两段,中间的点跟所给数比,如果中间数大,那么去左边的数组,如果中间数小,那么去右边的数组
排序数组中查找
#include <bits/stdc++.h>using namespace std;int a[200000];int n;int find(int t) { int l = 1, r = n; while (l <= n) { int mid = l + (r - l) / 2; if (a[mid] == t) { return mid; } if (a[mid] < t) { l = mid + 1; } if (a[mid] > t) { r = mid - 1; } }}int main() { cin >> n; for (int i = 1; i <= n; i++) { cin >> a[i]; } int m; cin >> m; while (m--) { int t; cin >> t; cout << find(t) << endl; } return 0;}DFS
又称深度优先搜索或回溯法
摸球问题
#include <bits/stdc++.h>using namespace std;int n, m;void dfs(int x, string path) { if (x == n + 1) { printf("%s\n", path.c_str()); return; } for (int i = 1; i <= m; i++) { dfs(x + 1, path + (char)(i + '0')); }}int main() { scanf("%d %d", &n, &m); dfs(1, ""); return 0;}全排列
#include <bits/stdc++.h>using namespace std;int n;bool v[100];void dfs(int x, string path) { if (x == n + 1) { printf("%s\n", path.c_str()); return; } for (int i = 1; i <= n; i++) { if (v[i]) { continue; } v[i] = true; dfs(x + 1, path + (char)(i + '0')); v[i] = false; }}int main() { scanf("%d", &n); dfs(1, ""); return 0;}