1.树的遍历
1004 Counting Leaves (30分)
基本的数据结构——树,复习了链式前向星,bfs遍历判断即可
#include <cstdio> #include <cstring> #include <string> #include <algorithm> #include <iostream> #include <map> #include <queue> #include <cmath> #define ll long long #define inf 0x3f3f3f #define pii pair<int, int> using namespace std; const int maxn = 1e4+100; int n, m, k, par, child; int cnt, head[maxn]; int level, ans[maxn]; struct node{ int to, nxt; }e[maxn]; void add(int u, int v){ e[++cnt].nxt = head[u]; e[cnt].to = v; head[u] = cnt; } void bfs(){ queue<pii> que; que.push({1, 0}); while(!que.empty()){ pii tmp = que.front(); que.pop(); int u = tmp.first, step = tmp.second; level = max(level, step); if(head[u]){ for(int i = head[u]; i; i = e[i].nxt) que.push({e[i].to, step+1}); } else ans[step]++; } } int main(){ scanf("%d%d", &n, &m); while(m--){ scanf("%d%d", &par, &k); while(k--){ scanf("%d", &child); add(par, child); } } bfs(); for(int i = 0; i <= level; i++){ printf("%d", ans[i]); if(i!=level) printf(" "); } }
1053 Path of Equal Weight (30分)
树的遍历,需要思考的点在于如何记录路径并且排序输出
#include <cstdio> #include <cstring> #include <string> #include <algorithm> #include <iostream> #include <cmath> #include <map> #include <queue> #include <vector> #include <set> #define ll long long #define inf 0x3f3f3f #define pii pair<int, int> #define pb push_back using namespace std; const int maxn = 1e4+100; int n, m, s, cnt; int val[maxn], fa[maxn]; int t, head[maxn]; vector<int> path[maxn]; struct node{ int to, nxt; }e[maxn]; //bool cmp(vector<int> a, vector<int> b){ // reverse(a.begin(), a.end()); // reverse(b.begin(), b.end()); // return //} void add(int u, int v){ e[++t].to = v; e[t].nxt = head[u]; head[u] = t; } void dfs(int u, int sum){ if(!head[u]&&sum==s) { while(u!=-1) path[cnt].pb(val[u]), u = fa[u]; reverse(path[cnt].begin(), path[cnt].end()); cnt++; } for(int i = head[u]; i; i = e[i].nxt){ int v = e[i].to; if(v==fa[u]) continue; dfs(v, sum+val[v]); } } int main(){ scanf("%d%d%d", &n, &m, &s); for(int i = 0; i <= n-1; i++) scanf("%d", &val[i]); fa[0] = -1; while(m--){ int u, v, k; scanf("%d%d", &u, &k); while(k--){ scanf("%d%", &v); add(u, v), fa[v] = u; } } dfs(0, val[0]); sort(path, path+cnt); for(int i = cnt-1; i >= 0; i--) { int len = path[i].size(); for(int j = 0; j < len; j++) cout << path[i][j] << (j==len-1 ? "" : " "); cout << endl; } }
Reference:
https://www.cnblogs.com/vranger/p/3502885.html
1079 Total Sales of Supply Chain (25 分)
树的遍历,不过需要特别注意每次利润点是r%而不是r
#include <cstdio> #include <cstring> #include <algorithm> #include <iostream> #define ll long long using namespace std; const int maxn = 1e5+100; int n; ll num[maxn]; double p, r, ans; int head[maxn], t; struct node{ int to, nxt; }e[maxn]; void add(int u, int v){ e[++t] = {v, head[u]}; head[u] = t; } void dfs(int u, double pri){ if(head[u]==0) ans += pri*num[u]; for(int i = head[u]; i; i = e[i].nxt){ int v = e[i].to; dfs(v, pri*(1+r*0.01)); } } int main(){ scanf("%d%lf%lf", &n, &p, &r); for(int u = 0; u < n; u++){ int m, v; scanf("%d", &m); if(m==0) scanf("%lld", &num[u]); while(m--){ scanf("%d", &v); add(u, v); } } dfs(0, p); printf("%.1lf", ans); }
1090 Highest Price in Supply Chain (25 分)
树的遍历,应当仔细审题,要求拥有最大销售价格的零售商数目,而不是编号
#include <cstdio> #include <cstring> #include <algorithm> #include <iostream> #define ll long long using namespace std; const int maxn = 1e5+100; int n, s; ll cnt, num[maxn]; double p, r, ans; int head[maxn], t; struct node{ int to, nxt; }e[maxn]; void add(int u, int v){ e[++t] = {v, head[u]}; head[u] = t; } void dfs(int u, double pri){ if(head[u]==0) { if(ans==pri) cnt++; else if(ans<pri) ans = pri, cnt = 1; } for(int i = head[u]; i; i = e[i].nxt){ int v = e[i].to; dfs(v, pri*(1+r*0.01)); } } int main(){ scanf("%d%lf%lf", &n, &p, &r); for(int u = 0; u < n; u++){ int m, v; scanf("%d", &v); if(v==-1) s = u; else add(v, u); } dfs(s, p); printf("%.2lf %d", ans, cnt); }
1106 Lowest Price in Supply Chain (25 分)
树的遍历,和前两题是一个系列的,代码稍作修改即可
#include <cstdio> #include <cstring> #include <algorithm> #include <iostream> #define ll long long #define inf 0x3f3f3f3f using namespace std; const int maxn = 1e5+100; int n, s, cnt; ll num[maxn]; double p, r, ans = 1e10+100; int head[maxn], t; struct node{ int to, nxt; }e[maxn]; void add(int u, int v){ e[++t] = {v, head[u]}; head[u] = t; } void dfs(int u, double pri){ if(head[u]==0) { if(ans==pri) cnt++; else if(ans>pri) ans = pri, cnt = 1; } for(int i = head[u]; i; i = e[i].nxt){ int v = e[i].to; dfs(v, pri*(1+r*0.01)); } } int main(){ scanf("%d%lf%lf", &n, &p, &r); for(int u = 0; u < n; u++){ int m, v; scanf("%d", &m); while(m--){ scanf("%d", &v); add(u, v); } } dfs(0, p); printf("%.4lf %d", ans, cnt); }
1094 The Largest Generation (25 分)
树的遍历,简直是个大水题
#include <cstdio> #include <cstring> #include <algorithm> #include <iostream> #define ll long long #define inf 0x3f3f3f3f using namespace std; const int maxn = 1e5+100; int n, m; int resLev, maxLev, res, cnt[maxn]; int head[maxn], t; struct node{ int to, nxt; }e[maxn]; void add(int u, int v){ e[++t] = {v, head[u]}; head[u] = t; } void dfs(int u, int lev){ cnt[lev]++, maxLev = max(maxLev, lev); for(int i = head[u]; i; i = e[i].nxt){ int v = e[i].to; dfs(v, lev+1); } } int main(){ scanf("%d%d", &n, &m); while(m--){ int u, v, num; scanf("%2d %d", &u, &num); while(num--){ scanf("%d", &v); add(u, v); } } dfs(1, 1); for(int i = 1; i <= maxLev; i++) if(res<cnt[i]) resLev = i, res = cnt[i]; printf("%d %d", res, resLev); }
2.二叉查找树
1043 Is It a Binary Search Tree (25 分)
一种方法是用给出的输入构造出一棵二叉搜索树,然后再对这棵树进行前序遍历,判断得到的结果是否和输入一致,如果不一致,那就输出 NO,如果一致就输出这这棵树的后序遍历结果。
#include <cstdio> #include <cstring> #include <algorithm> #include <iostream> #include <vector> #define ll long long #define inf 0x3f3f3f3f #define pb push_back using namespace std; const int maxn = 1e5+100; int n, data; vector<int> orgin, pre, preMir, post, postMir; struct node{ int data; node *lch, *rch; }; //构建BST,需要加& void insert(node* &root, int data){ if(root==NULL){ root = new node; root->data = data; root->lch = root->rch = NULL; return; } if(data<root->data) insert(root->lch, data); else insert(root->rch, data); } void preOrder(node *root){ if(root!=NULL){ pre.pb(root->data); // printf("%d ", root->data); preOrder(root->lch); preOrder(root->rch); } } void preMirOrder(node *root){ if(root!=NULL){ preMir.pb(root->data); preMirOrder(root->rch); preMirOrder(root->lch); } } void postOrder(node *root){ if(root!=NULL){ postOrder(root->lch); postOrder(root->rch); post.pb(root->data); } } void postMirOrder(node *root){ if(root!=NULL){ postMirOrder(root->rch); postMirOrder(root->lch); postMir.pb(root->data); } } void test(){ for(int i = 0; orgin[i]; i++) { printf("%d", orgin[i]); if(i!=orgin.size()-1) printf(" "); else printf(" "); } for(int i = 0; pre[i]; i++) { printf("%d", pre[i]); if(i!=pre.size()-1) printf(" "); else printf(" "); } } int main(){ scanf("%d", &n); node *root = NULL; while(n--){ scanf("%d", &data); orgin.pb(data); insert(root, data); } preOrder(root); preMirOrder(root); // test(); if(orgin==pre){ printf("YES "); postOrder(root); int len = post.size(); for(int i = 0; i < len; i++) { printf("%d", post[i]); if(i!=len-1) printf(" "); } } else if(orgin==preMir){ printf("YES "); postMirOrder(root); int len = postMir.size(); for(int i = 0; i < len; i++) { printf("%d", postMir[i]); if(i!=len-1) printf(" "); } } else printf("NO "); }
另一种方法是假设这个输入序列是对的,然后利用这个输入序列去得到后序序列并保存,如果最终得到的结果中节点个数不够(这种情况下部分节点不会被遍历到,e.g 8 6 8 5 10 9 11),那说明它不是正确的前序遍历,否则就直接输出得到的结果。这种方法基于以下前提:
一般情况下,我们要根据一棵二叉树的前序遍历结果和中序遍历结果才能得到它的后序遍历结果。但由于这里是BST,因此根据左子树所有节点的键值小于根节点这个特点就可以划分左右子树进行遍历,从而得出后序遍历结果
#include <cstdio> #include <cstring> #include <algorithm> #include <iostream> #include <vector> #define ll long long #define inf 0x3f3f3f3f #define pb push_back using namespace std; const int maxn = 1e5+100; int n; bool isMirror; vector<int> pre, post; void getpost(int root, int tail){ if(root>tail) return; int i = root + 1, j = tail; if(isMirror){ while(i<=tail&&pre[i]<pre[root]) i++; while(j>=root+1&&pre[j]>=pre[root]) j--; } else{ while(i<=tail&&pre[i]>=pre[root]) i++; while(j>=root+1&&pre[j]<pre[root]) j--; } getpost(root+1, j); getpost(i, tail); post.pb(pre[root]); } int main(){ scanf("%d", &n); pre.resize(n);//使用vector输入数据要预先分配内存 for(int i = 0; i <= n-1; i++) scanf("%d", &pre[i]); getpost(0, n-1); if(post.size()!=n){ isMirror = true, post.clear(); getpost(0, n-1); } if(post.size()==n){ printf("YES %d", post[0]); for(int i = 1; i <= n-1; i++) printf(" %d", post[i]); } else printf("NO "); }
Reference:
树的前序、中序、后续遍历(以根节点的访问顺序为界定):
https://www.cnblogs.com/wizarderror/p/10816635.html
如何唯一确定一棵二叉树&&先序遍历和后序遍历为什么不能唯一地确定一棵二叉树?(最简单的办法:反证法)
https://www.zybang.com/question/fc1b72ae09eadcb6b95ffbf2e675e432.html
https://www.nowcoder.com/questionTerminal/e77e06b71aa548ed8aefd030edb9b4a2
https://blog.csdn.net/chaoyue1216/article/details/7609689
https://zhuanlan.zhihu.com/p/73438175(见评论)
题解:
https://www.cnblogs.com/codervivi/p/13126320.html(推荐)
https://blog.csdn.net/qq_41528502/article/details/104389771
https://www.liuchuo.net/archives/2153
1099 Build A Binary Search Tree (30 分)
BST的中序遍历我们可以通过排序来得到,由此遍历二叉树时加上节点所在层数的信息,我们就能得出层次遍历的序列,最后输出即可
#include <cstdio> #include <cstring> #include <algorithm> #include <iostream> #include <vector> #define ll long long #define inf 0x3f3f3f3f #define pb push_back using namespace std; const int maxn = 1e5+100; int n, t, val[105], maxDepth; vector<int> g[105], lev[105]; void dfs(int u, int dep){ if(u==-1) return; maxDepth = max(maxDepth, dep); dfs(g[u][0], dep+1); lev[dep].pb(val[t++]); dfs(g[u][1], dep+1); } int main(){ scanf("%d", &n); for(int i = 0; i <= n-1; i++){ int u, v; scanf("%d%d", &u, &v); g[i].pb(u), g[i].pb(v); } for(int i = 0; i <= n-1; i++) scanf("%d", &val[i]); sort(val, val+n), dfs(0, 0); for(int i = 0; i <= maxDepth; i++){ for(int j = 0; j < lev[i].size(); j++){ printf("%d", lev[i][j]); if(i==maxDepth&&j==lev[i].size()-1) continue; else printf(" "); } } }
1064 Complete Binary Search Tree (30 分)
1099的完全二叉树版本。使用数组来存放完全二叉树,对于某一结点u, 其左右孩子的编号分别为2*u和2*u+1(根节点为1),观察易发现数组会按照层序来存放完全二叉树的n个节点。
#include <cstdio> #include <cstring> #include <algorithm> #include <iostream> #include <vector> #define ll long long #define inf 0x3f3f3f3f #define pb push_back using namespace std; const int maxn = 1e5+100; int n, t, val[1005], tree[1005]; void dfs(int u, int dep){ if(u>n) return; dfs(2*u, dep+1); tree[u] = val[++t]; dfs(2*u+1, dep+1); } int main(){ scanf("%d", &n); for(int i = 1; i <= n; i++) scanf("%d", &val[i]); sort(val+1, val+1+n), dfs(1, 0); printf("%d", tree[1]); for(int i = 2; i <= n; i++) printf(" %d", tree[i]); }
3.二叉树的遍历
1020 Tree Traversals (25 分)
利用后序遍历和中序遍历来构建二叉树,按照两种序列的特性递归划分子树,由此来确定节点即可
#include <cstdio> #include <cstring> #include <algorithm> #include <iostream> #include <vector> #define ll long long #define inf 0x3f3f3f3f #define pb push_back using namespace std; const int maxn = 1e5+100; int n, post[maxn], in[maxn], maxDep; vector<int> lev[maxn]; void dfs(int l1, int r1, int l2, int r2, int dep){ if(l1>r1||l2>r2) return; int now = l2, dis; for(int i = l2; i <= r2; i++) if(in[i]==post[r1]) now = i; lev[dep].pb(in[now]), maxDep = max(maxDep, dep); dis = now-l2; dfs(l1, l1+dis-1, l2, l2+dis-1, dep+1); dfs(l1+dis, r1-1, l2+dis+1, r2, dep+1); } int main(){ scanf("%d", &n); for(int i = 1; i <= n; i++) scanf("%d", &post[i]); for(int i = 1; i <= n; i++) scanf("%d", &in[i]); dfs(1, n, 1, n, 0); for(int i = 0; i <= maxDep; i++) for(int j = 0; j < lev[i].size(); j++){ printf("%d", lev[i][j]); if(i==maxDep&&j==lev[i].size()-1) continue; else printf(" "); } }
1086 Tree Traversals Again (25 分)
首先要明确一点:先序、中序和后序遍历过程,其经过节点的路线一样,只是访问节点的时机不一样
这里Push的次序我们可以看作是先序遍历,Pop按照题意为中序遍历,于是问题就转化成了如何由先序遍历和中序遍历求后续遍历
最后注意处理输入即可
#include <cstdio> #include <cstring> #include <string> #include <algorithm> #include <iostream> #include <vector> #define ll long long #define inf 0x3f3f3f3f #define pb push_back using namespace std; const int maxn = 1e5+100; int n, val; int pre[maxn], t1, in[maxn], t2, post[maxn], t3; int t, sta[maxn]; string str; void dfs(int l1, int r1, int l2, int r2){ if(l1>r1) return; int now = l2, dis; for(int i = l2; i <= r2; i++) if(in[i]==pre[l1]) now = i; dis = now-l2; dfs(l1+1, l1+1+dis-1, l2, l2+dis-1); dfs(l1+1+dis, r1, l2+dis+1, r2); post[++t3] = in[now]; } int main(){ scanf("%d", &n); int time = 2*n; while(time--){ cin >> str; if(str=="Push"){ scanf("%d", &val); pre[++t1] = sta[++t] = val; } else in[++t2] = sta[t--]; } dfs(1, n, 1, n); printf("%d", post[1]); for(int i = 2; i <= t3; i++) printf(" %d", post[i]); }
1102 Invert a Binary Tree (25 分)
An inversion, or mirror, of a Binary Tree (T), is just a Binary Tree M(T) whose left and right children (of all non-leaf nodes) are swapped.
按题目要求模拟即可,注意题目中名词的概念和数据输入
#include <cstdio> #include <cstring> #include <string> #include <algorithm> #include <iostream> #include <vector> #define ll long long #define inf 0x3f3f3f3f #define pb push_back using namespace std; const int maxn = 1e5+100; int n, maxDep; int in[maxn], t; vector<int> g[maxn], lev[maxn]; bool vis[maxn]; void dfs(int u, int dep){ if(u==-1) return; dfs(g[u][0], dep+1); in[++t] = u; lev[dep].pb(u), maxDep = max(maxDep, dep); dfs(g[u][1], dep+1); } int main(){ scanf("%d", &n); for(int i = 0; i <= n-1; i++){ char u, v; scanf(" %c %c", &u, &v); v=='-' ? g[i].pb(-1) : g[i].pb(v-'0'), vis[v-'0'] = 1; u=='-' ? g[i].pb(-1) : g[i].pb(u-'0'), vis[u-'0'] = 1; } for(int i = 0; i <= n-1; i++) if(!vis[i]) dfs(i, 1); for(int i = 1; i <= maxDep; i++) for(int j = 0; j < lev[i].size(); j++){ printf("%d", lev[i][j]); if(i==maxDep&&j==lev[i].size()-1) continue; else printf(" "); } printf(" %d", in[1]); for(int i = 2; i <= n; i++) printf(" %d", in[i]); }
4.平衡二叉树
1066. Root of AVL Tree (25)
泛泛的了解了下AVL树,看了慕课上视频和一些Wiki资料、博客啥的,当然还有些Splay、Treap、红黑树等等。目前不打算深入,一是没有对应的应用场景去驱动;二是要是以考证为目的来说,这些知识点有点小复杂,掌握需要花费过多的时间和精力而PAT不常考。
对AVL树的旋转操作感性理解就是:要使树达到平衡,其左右子树大小分布要尽可能的均匀,由于是二叉搜索树所以让中位数或者其附近的数作为根节点是个不错的选择。当其平衡性遭到破坏的时候,通过选取更合理的根节点来达到重新平衡的目的,旋转操作就是用尽可能简单的方法达到这个效果,四种不同的旋转操作只是对节点插入的位置做了分类讨论而已。
这道题目直接套用AVL树基本操作的模板即可
#include <cstdio> #include <cstring> #include <string> #include <algorithm> #include <iostream> #include <vector> #define ll long long #define inf 0x3f3f3f3f #define pb push_back using namespace std; const int maxn = 1e5+100; int n, val; struct node{ int val; node *lch, *rch; }; int getHeight(node* root){ if(root==NULL) return 0; return max(getHeight(root->lch), getHeight(root->rch))+1; } node* rotateLeft(node* root){ node* now = root->rch; root->rch = now->lch; now->lch = root; return now; } node* rotateRight(node* root){ node* now = root->lch; root->lch = now->rch; now->rch = root; return now; } node* rotateLeftRight(node* root){ root->lch = rotateLeft(root->lch); return rotateRight(root); } node* rotateRightLeft(node* root){ root->rch = rotateRight(root->rch); return rotateLeft(root); } void insert(node* &root, int val){ if(root==NULL){ root = new node; root->val = val; root->lch = root->rch = NULL; return; } if(val<root->val){ insert(root->lch, val); if(getHeight(root->lch)-getHeight(root->rch)==2) root = val < root->lch->val ? rotateRight(root) : rotateLeftRight(root); } else{ insert(root->rch, val); if(getHeight(root->lch)-getHeight(root->rch)==-2) root = val >= root->rch->val ? rotateLeft(root) : rotateRightLeft(root); } } int main(){ scanf("%d", &n); node* root = NULL; while(n--){ scanf("%d", &val); insert(root, val); } printf("%d", root->val); }
Reference:
AVL树:
https://blog.csdn.net/qq_25343557/article/details/89110319
https://en.wikipedia.org/wiki/AVL_tree
https://www.tutorialspoint.com/data_structures_algorithms/avl_tree_algorithm.htm
题解:
https://www.liuchuo.net/archives/2178(柳婼确实写的很不错)
5.堆
1098 Insertion or Heap Sort (25 分)
这题主要考察的是堆这种数据结构,又去把浙大数据结构的慕课看了看,解出此题需要掌握堆的概念及其各种操作,还有排序相关知识
此外,最后提交的时候测试样例2段错误
对于插入排序的下一次迭代的位置,得从前往后找,不能从后往前找 否则测试样例2错误。 例如: 4 3 4 2 1 3 4 2 1 ----------- 正确答案: Insertion Sort 2 3 4 1
#include <cstdio> #include <cstring> #include <string> #include <algorithm> #include <iostream> #include <vector> #define ll long long #define inf 0x3f3f3f3f #define pb push_back using namespace std; const int maxn = 1e5+100; int n, a[maxn], b[maxn]; int main(){ scanf("%d", &n); for(int i = 1; i <= n; i++) scanf("%d", &a[i]); for(int i = 1; i <= n; i++) scanf("%d", &b[i]); if(b[1]<=b[2]){ printf("Insertion Sort "); // int p = n; // while(a[p]==b[p]) p--; int p = 1; while(b[p+1]>=b[p]) p++; sort(b+1, b+1+p+1); } else{ printf("Heap Sort "); int p = n, par, child, tmp; while(b[p]>=b[0]&&b[p-1]<=b[p]) p--; swap(b[1], b[p--]), tmp = b[1]; for(par = 1; par*2 <= p; par = child){ child = par*2; if(child!=p&&b[child]<b[child+1]) child++; if(tmp>=b[child]) break; else b[par] = b[child]; } b[par] = tmp; } printf("%d", b[1]); for(int i = 2; i <= n; i++) printf(" %d", b[i]); }
Reference:
堆&排序:
Code:
借鉴mooc&&柳婼
https://blog.csdn.net/liuchuo/article/details/52252172
debug:
https://blog.csdn.net/njtechlcj/article/details/105400209
https://blog.csdn.net/ever_promise/article/details/107567183
6.并查集
1107 Social Clusters (30 分)
并查集的基本操作,需要注意读懂题目的意思:
有n个人,每个人喜欢k个活动,如果两个人有任一活动相同,就认为他们处于同一个社交网络。求这n个人一共形成了多少个社交网络
#include <cstdio> #include <cstring> #include <string> #include <algorithm> #include <iostream> #include <vector> #define ll long long #define inf 0x3f3f3f3f #define pb push_back using namespace std; const int maxn = 1e4+100; int n, m, h, tot, f[maxn], cnt[maxn]; int t, hobby[maxn]; bool vis[maxn]; vector<int> g[maxn]; int find(int x){ if(f[x]!=x) f[x] = find(f[x]); return f[x]; } void merge(int x, int y){ x = find(x), y = find(y); f[x] = y; } int main(){ scanf("%d", &n); for(int i = 1; i <= n; i++){ f[i] = i; scanf("%d:", &m); while(m--){ scanf("%d", &h); g[h].pb(i); if(!vis[h]) hobby[++t] = h, vis[h] = 1; } } for(int i = 1; i <= t; i++){ h = hobby[i]; for(int j = 1; j < g[h].size(); j++){ if(find(g[h][0])!=find(g[h][j])) merge(g[h][0], g[h][j]); } } for(int i = 1; i <= n; i++) cnt[find(i)]++; for(int i = 1; i <= n; i++) if(cnt[i]!=0) tot++; sort(cnt+1, cnt+1+n); printf("%d %d", tot, cnt[n]); for(int i = n-1; i >= n-tot+1; i--) printf(" %d", cnt[i]); }