本题考察的是由中序序列和其他一个遍历序列建树的算法。关键是写出建树的函数。由于我们要建树,所以返回值可以用node*来表示。又因为要递归表示,所以要注意结束条件。
#include<cstdio> #include<queue> #include<algorithm> using namespace std; const int N = 30; struct node { int data; node* lchild; node* rchild; }; node* creatTree(int post[], int in[], int pl, int pr, int il, int ir) { if(pl > pr) return NULL; node* root = new node; int k; for(k = il; k < ir; k++) { if(in[k] == post[pr]) break; } root->data = post[pr]; int numright = ir - k; root->rchild = creatTree(post, in, pr- numright, pr - 1, k+1, ir); root->lchild = creatTree(post, in, pl, pr - numright - 1, il, k - 1); return root; } int num = 0, n; int post[N], in[N]; void BFS(node* root) { queue<node*> qu; qu.push(root); while(! qu.empty()) { node* now = qu.front(); qu.pop(); printf("%d", now->data); num++; if(num < n) printf(" "); if(now->lchild != NULL) qu.push(now->lchild); if(now->rchild != NULL) qu.push(now->rchild); } } int main() { scanf("%d", &n); for(int i = 0; i < n; i++) scanf("%d", &post[i]); for(int i = 0; i < n; i++) scanf("%d", &in[i]); node* root = creatTree(post, in, 0, n-1, 0, n-1); BFS(root); return 0; }
本题考察用栈模拟中序和前序遍历,使得利用两个序列建树,最后再后序遍历的算法。由于中序前序遍历都可以用非递归函数模拟,所以我们要自己先将pop出的元素序列写出来,观察它是哪个序列,最后再用中+其他序列的方法建树。
本题还需要注意的是如何处理输入输出。可以用strcmp来比较第一个是push还是pop。如果是push再输入一个x,否则按pop来处理。
#include<cstdio> #include<stack> #include<cstring> using namespace std; const int N = 40; struct node { int data; node* lchild, *rchild; }; int pre[N], in[N], preindex = 0, inindex = 0; int n, num = 0; node* createTree(int pl, int pr, int il, int ir) { if(pl > pr) return NULL; node* root = new node; root->data = pre[pl]; int k; for(k = il; k <= ir; k++) if(in[k] == pre[pl]) break; int numleft = k - il; root->lchild = createTree(pl+1, pl+numleft, il, k - 1); root->rchild = createTree(pl+numleft+1, pr, k+1, ir); return root; } void postorder(node* root) { if(root == NULL) return ; postorder(root->lchild); postorder(root->rchild); printf("%d", root->data); num++; if(num < n) printf(" "); } int main() { scanf("%d", &n); char str[5]; int x; stack<int> st; for(int i = 0; i < 2*n; i++) { scanf("%s", str); if(strcmp(str, "Push") == 0) { scanf("%d", &x); pre[preindex++] = x; st.push(x); } else { in[inindex++] = st.top(); if( !st.empty()) st.pop(); } } node* root = createTree(0, n-1, 0, n-1); postorder(root); return 0; }
本题是转换二叉树。转换二叉树就是在后序遍历的基础上,当要访问根结点时,invite函数为交换左右子树即可。即:swap(lchild,rchild)。如果要访问反转二叉树的层序遍历那么可以用上述方法。还有种更简便的方法:如果题目要是用前中后序顺序来访问二叉树,可以直接在函数里先访问右子树再访问左子树,其他不变。
#include<cstdio> #include<cstring> #include<algorithm> #include<queue> using namespace std; const int N = 50; struct { int lchild, rchild; }node[N]; int n, num1 = 0; bool isroot[N]; int char2num(char c) { if(c == '-') return -1; else { isroot[c - '0'] = false; return c - '0'; } } int findroot() { for(int i = 0; i < n; i++) if(isroot[i]) return i; } void postinvert(int root) { if(root == -1 ) return; postinvert(node[root].lchild); postinvert(node[root].rchild); swap(node[root].lchild, node[root].rchild); } void level_Order(int root) { int num = 0; queue<int> q; q.push(root); while(! q.empty()) { int now = q.front(); q.pop(); printf("%d", now); num++; if(num < n) printf(" "); else printf("\n"); if(node[now].lchild != -1) q.push(node[now].lchild); if(node[now].rchild != -1) q.push(node[now].rchild); } } void inorder(int root) { if(root == -1) return; inorder(node[root].lchild); printf("%d", root); num1++; if(num1 < n) printf(" "); else printf("\n"); inorder(node[root].rchild); } int main() { memset(isroot, true, sizeof(isroot)); scanf("%d", &n); char lchild, rchild; for(int i = 0; i < n; i++) { scanf("%*c%c %c", &lchild, &rchild); node[i].lchild = char2num(lchild); node[i].rchild = char2num(rchild); } int root = findroot(); postinvert(root); level_Order(root); inorder(root); return 0; }
本题是一个关于一般树的遍历问题。首先要处理输入输出问题,由于本题需要考虑叶子结点的权值,所以可以用包含变量int和vector的结构体来表示。由于已经给定树的编号,所以可以用结构体数组的写法(静态链表)。在输入时根据k的大小判断后序是输入叶子结点还是销售额。又由于要考虑深度的问题,所以可以在preorder中加入一个depth变量用于记录深度。
#include<cstdio> #include<vector> #include<cmath> using namespace std; const int N = 100010; struct node { int data; vector<int> child; }Node[N]; int n; double p, r, ans = 0; void preorder(int root, int depth) { if(Node[root].child.size() == 0) { ans += pow(1+r, depth)* Node[root].data * p; } else { for(int i = 0; i < Node[root].child.size(); i++) { preorder(Node[root].child[i], depth+1); } } } int main() { int k, child; scanf("%d%lf%lf", &n, &p, &r); r /= 100; for(int i =0; i < n; i++) { scanf("%d", &k); if(k != 0) { for(int j = 0; j < k; j++) { scanf("%d", &child); Node[i].child.push_back(child); } } else { scanf("%d", &Node[i].data); } } preorder(0, 0); printf("%.1f", ans); return 0; }
本题和1079没有啥不同,都是关于树的遍历。由于只要找到最大的深度和最大深度的结点个数,不需要考虑权值问题,所以可以用vectornode[N]的写法来表示树。此时判断树是否为叶子结点的条件为node[i].size()==0。所以在递归中,如果是叶子结点则让其与最大深度进行比较如果大于则更新,相等则更新num否则退出。如果不是叶子结点则继续递归。
#include<cstdio> #include<vector> #include<cmath> using namespace std; const int N = 100010; vector<int> Node[N]; int n, maxdepth = -1, num = 0; double p, r; void preorder(int root, int depth) { if(Node[root].size() == 0) { if(maxdepth < depth) { maxdepth = depth; num = 1; } else if(maxdepth == depth) num++; return; } for(int i = 0; i < Node[root].size(); i++) { preorder(Node[root][i], depth+1); } } int main() { int parent, root; scanf("%d%lf%lf", &n, &p, &r); r /= 100; for(int i = 0; i < n; i++) { scanf("%d", &parent); if(parent == -1) root = i; else Node[parent].push_back(i); } preorder(root, 0); printf("%.2f %d", pow(1+r, maxdepth)*p, num); return 0; }
本题是找同一层最大结点数的题。可以用dfs或者bfs写。
注意点:
#include<cstdio> #include<vector> #include<cstring> #include<queue> using namespace std; const int N = 110; vector<int> Node[N]; int n, m; int levelNumber[N]; int h[N]; void DFS(int root) { levelNumber[h[root]]++; for(int i = 0; i < Node[root].size(); i++) { int child = Node[root][i]; h[child] = h[root]+1; DFS(child); } } void BFS() { queue<int> q; q.push(1); while(!q.empty()) { int now = q.front(); levelNumber[h[now]]++; q.pop(); for(int i = 0; i < Node[now].size(); i++) { int child = Node[now][i]; h[child] = h[now]+1; q.push(child); } } } int main() { int parent, k, child; memset(levelNumber, 0, sizeof(levelNumber)); memset(h, 0, sizeof(h)); scanf("%d%d", &n, &m); for(int i = 0; i < m; i++) { scanf("%d%d", &parent, &k); for(int j = 0; j < k; j++) { scanf("%d", &child); Node[parent].push_back(child); } } h[1] = 1; DFS(1); int level = 1, number = -1; for(int i = 1; i <= n; i++) { if(levelNumber[i] > number) { number = levelNumber[i]; level = i; } } printf("%d %d", number, level); return 0; }
本题是找深度最小的树,注意开始mindepth要设一个很大的值。
#include<cstdio> #include<vector> #include<cmath> using namespace std; const int N = 100010; vector<int> Node[N]; int h[N]; int n, mindepth = N, num = 0; double p, r; void DFS(int root) { if(Node[root].size() == 0) { if(h[root] < mindepth) { mindepth = h[root]; num = 1; } else if(h[root] == mindepth) num++; } for(int i = 0; i < Node[root].size(); i++) { int child = Node[root][i]; h[child] = h[root]+1; DFS(child); } } int main() { scanf("%d%lf%lf", &n, &p, &r); r /= 100; int child, k; for(int i = 0; i < n; i++) { scanf("%d", &k); for(int j = 0; j < k; j++) { scanf("%d", &child); Node[i].push_back(child); } } h[0] = 0; DFS(0); printf("%.4f %d", pow(1+r, mindepth)*p, num); return 0; }
本题和1094的思路差不多,都是找同一层次满足条件的个数,但是本题要求比1094难一点,因为1094只需要找到最值,所以可以不用找到最大层数,直接全部遍历即可,但是本题需要逐一输出,所以要找到最大层数。
#include<cstdio> #include<vector> #include<queue> using namespace std; const int N = 110; vector<int> Node[N]; int levelnumber[N]; int h[N]; int maxdepth = 1; void BFS() { queue<int> q; q.push(1); while(!q.empty()) { int now = q.front(); q.pop(); if(Node[now].size() == 0) levelnumber[h[now]]++; else { for(int i = 0; i < Node[now].size(); i++) { int child = Node[now][i]; h[child] = h[now]+1; if(h[child] > maxdepth) maxdepth = h[child]; q.push(child); } } } } int main() { int n, m; int parent, k, child; scanf("%d%d", &n, &m); for(int i = 0; i < m; i++) { scanf("%d%d", &parent, &k); for(int j = 0; j < k; j++) { scanf("%d", &child); Node[parent].push_back(child); } } h[1] = 1; BFS(); for(int i = 1; i <= maxdepth; i++) { printf("%d", levelnumber[i]); if(i != maxdepth) printf(" "); } return 0; }
本题是树的遍历,只不过需要用dfs搜索,需要注意的一个点是:因为可能会出现一个结点下面有两个子结点权值相等的情况,所以不能先排序,需要将所有的结果存储起来再进行排序。
#include<cstdio> #include<queue> #include<vector> #include<algorithm> using namespace std; const int N = 110; int n, m, s; vector<int> temp; vector<vector<int> > ans; struct node { int weight; vector<int>child; }Node[N]; bool cmp(vector<int>a, vector<int> b) { int size = min(a.size(), b.size()); for(int i = 0; i < size; i++) { if(a[i] != b[i]) return a[i] > b[i]; } } int path[N]; void DFS(int index, int now) { if(now == s && Node[index].child.size() == 0 ) { ans.push_back(temp); return ; } if(now > s || Node[index].child.size() == 0) return ; for(int i = 0; i < Node[index].child.size(); i++) { int child = Node[index].child[i]; temp.push_back(Node[child].weight); DFS(child, now + Node[child].weight); temp.pop_back(); } } int main() { scanf("%d%d%d", &n, &m, &s); for(int i = 0; i < n; i++) scanf("%d", &Node[i].weight); int parent, k, child; for(int i = 0; i < m; i++) { scanf("%d%d", &parent, &k); for(int j = 0; j < k; j++) { scanf("%d", &child); Node[parent].child.push_back(child); } } temp.push_back(Node[0].weight); DFS(0,Node[0].weight); sort(ans.begin(), ans.end(), cmp); for(int i = 0; i < ans.size(); i++) { for(int j = 0; j < ans[i].size(); j++) { printf("%d", ans[i][j]); if(j != ans[i].size() - 1) printf(" "); } printf("\n"); } return 0; }
先根据序列建树,由于给定的是先序序列所以可以用循环插入的方法建树。当建树以后写出preorder和premorder函数。再用temp和pre以及prem对比如果是的则输出post或postm否则输出no。
#include<cstdio> #include<vector> #include<algorithm> using namespace std; const int N = 1010; struct node { int data; node*lchild, *rchild; }; int n; void insert(node* & root, int x) { if(root == NULL) { root = new node; root->data = x; root->lchild = root->rchild = NULL; return; } else if(x < root->data) insert(root->lchild, x); else insert(root->rchild, x); } vector<int> temp, pre, preM, post, postM; void preorder(node* root, vector<int>& vi) { if(root == NULL) return; vi.push_back(root->data); preorder(root->lchild, vi); preorder(root->rchild, vi); } void postOrder(node* root, vector<int>& vi) { if(root == NULL) return; postOrder(root->lchild, vi); postOrder(root->rchild, vi); vi.push_back(root->data); } void preMorder(node* root, vector<int>& vi) { if(root == NULL) return ; vi.push_back(root->data); preMorder(root->rchild, vi); preMorder(root->lchild, vi); } void postMorder(node* root, vector<int>& vi) { if(root == NULL) return; postMorder(root->rchild, vi); postMorder(root->lchild, vi); vi.push_back(root->data); } int main() { int tmp; node* root = NULL; scanf("%d", &n); for(int i = 0; i < n; i++) { scanf("%d", &tmp); temp.push_back(tmp); insert(root, tmp); } preorder(root, pre); preMorder(root, preM); postOrder(root, post); postMorder(root, postM); if(temp == pre) { printf("YES\n"); for(int i = 0; i < post.size(); i++) { printf("%d", post[i]); if(i != post.size() - 1) printf(" "); else printf("\n"); } } else if(temp == preM) { printf("YES\n"); for(int i = 0; i < postM.size(); i++) { printf("%d", postM[i]); if(i != postM.size() - 1) printf(" "); else printf("\n"); } } else printf("NO\n"); return 0; }
本题考查给定一个序列如何用数组存储这个序列并建成CBT,用了两个知识点。
基于上述内容,先将给定的数组递增排序,**因为bst中序是递增,所以可以用中序遍历建树。在访问结点时就是将数值赋给这个结点。**再将数组顺序输出就是层序遍历了。
#include<cstdio> #include<algorithm> #include<queue> using namespace std; const int N = 1010; int n, index = 0; int tmp[N], CBT[N]; void inorder(int root) { if(root > n) return; if(root * 2 <= n) inorder(root*2); CBT[root] = tmp[index++]; if(root*2 + 1 <= n) inorder(root*2+1); } void BFS() { int num = 0; queue<int> q; q.push(1); while(!q.empty()) { int now = q.front(); printf("%d", CBT[now]); q.pop(); num++; if(num < n) printf(" "); if(now*2 <= n) q.push(now*2); if(now*2 +1 <= n) q.push(now*2+1); } } int main() { scanf("%d", &n); for(int i = 0; i < n; i++) scanf("%d", &tmp[i]); sort(tmp, tmp+n); inorder(1); BFS(); return 0; }
本题本质上和1064的思想一样,都是利用二叉搜索树中序遍历递增的性质在中序遍历时建树。只不过1064是完全二叉树可以用数组存储,而本题是普通二叉树所以需要用结构体实现。
```cpp #include<cstdio> #include<queue> #include<algorithm> using namespace std; const int N = 110; struct node { int data; int lchild, rchild; }Node[N]; int tmp[N], index = 0, n; void inorder(int root) { if(root == -1) return ; inorder(Node[root].lchild); Node[root].data = tmp[index++]; inorder(Node[root].rchild); } void BFS() { int num = 0; queue<int> q; q.push(0); while(!q.empty()) { int now = q.front(); q.pop(); printf("%d", Node[now].data); num++; if(num != n) printf(" "); else printf("\n"); if(Node[now].lchild != -1) q.push(Node[now].lchild); if(Node[now].rchild != -1) q.push(Node[now].rchild); } } int main() { int lchild, rchild; scanf("%d", &n); for(int i = 0; i < n; i++) { scanf("%d%d", &lchild, &rchild); Node[i].lchild = lchild; Node[i].rchild = rchild; } for(int i = 0; i < n; i++) scanf("%d", &tmp[i]); sort(tmp, tmp + n); inorder(0); BFS(); return 0; }
``
本题考察avl树的建立(循环插入),即如何在插入时平衡高度,属于模板题。
#include<cstdio> #include<algorithm> using namespace std; const int N = 30; struct node { int data; int height; node* lchild, *rchild; }; int getHeight(node* root) { if(root == NULL) return 0; else return root->height; } void updateHeight(node* root) { root->height = max(getHeight(root->lchild), getHeight(root->rchild)) + 1; } int getBalanceFactor(node* root) { return getHeight(root->lchild) - getHeight(root->rchild); } void L(node* & root) { node* temp = root->rchild; root->rchild = temp->lchild; temp->lchild = root; updateHeight(root); updateHeight(temp); root = temp; } void R(node* & root) { node* temp = root->lchild; root->lchild = temp->rchild; temp->rchild = root; updateHeight(root); updateHeight(temp); root = temp; } void insert(node* & root, int x) { if(root == NULL) { root = new node; root->data = x; root->height = 1; root->lchild = root->rchild = NULL; return ; } else if(x < root->data) { insert(root->lchild, x); updateHeight(root->lchild); if(getBalanceFactor(root) == 2) { if(getBalanceFactor(root->lchild) == 1) { R(root); } else if(getBalanceFactor(root->lchild) == -1) { L(root->lchild); R(root); } } } else { insert(root->rchild, x); updateHeight(root->rchild); if(getBalanceFactor(root) == -2) { if(getBalanceFactor(root->rchild) == 1) { R(root->rchild); L(root); } else if(getBalanceFactor(root->rchild) == -1) { L(root); } } } } int main() { int n; int a[N]; node* root = NULL; scanf("%d", &n); for(int i = 0; i < n; i++) { scanf("%d", &a[i]); insert(root, a[i]); } printf("%d", root->data); return 0; }
并查集是用数组来实现,其结构图画出来就和树一样。是树的一种应用。
本题是一道关于并查集 题目,由于每个人又不同的兴趣,所以第一个当兴趣的就用来当这个兴趣的负责人。再将当前这个人与同一个兴趣的负责人所在的集合合并。只要有一个兴趣相同就可以合并,所以我们要建立兴趣到负责人的映射,人与人之间的集合。当前这个人读入一个兴趣时,如果这个兴趣已经有人负责了,则加入这个负责人所在的集合。即:Union(i,findfather(course[k]));
#include<cstdio> #include<algorithm> #include<cstring> using namespace std; const int N = 1010; int father[N], course[N]; int isfather[N]; bool cmp(int a, int b) { return a > b; } void init(int n) { for(int i = 1; i <= n; i++) father[i] = i; memset(course, 0, sizeof(course)); } int findfather(int a) { int x = a; while(a != father[a]) a = father[a]; while(x != father[x]) { int z = x; x = father[x]; father[z] = a; } return a; } void Union(int a, int b) { int fa = findfather(a); int fb = findfather(b); if(fa != fb) { father[fa] = fb; } } int main() { int n, k, tmp; scanf("%d", &n); init(n); for(int i = 1; i <= n; i++) { scanf("%d:", &k); for(int j = 0; j < k; j++) { scanf("%d", &tmp); if(course[tmp] == 0) course[tmp] = i; Union(i, findfather(course[tmp])); } } for(int i = 1; i <= n; i++) { isfather[findfather(i)]++; } int ans = 0; for(int i = 1; i <= n; i++) { if(isfather[i] != 0) ans++; } printf("%d\n", ans); sort(isfather+1, isfather+n+1, cmp); for(int i = 1; i <= ans; i++) { printf("%d", isfather[i]); if(i != ans) printf(" "); } return 0; }
堆一般是用数组实现的,其定义为根结点大于(或小于)子结点的值,结构类似于二叉树,是二叉树的一种应用。
建堆: 将非叶节点倒着向下调整,即:从n/2~1进行downadjust(i,n)调整。
删除堆顶:用最后一个结点覆盖堆顶,再进行(1,n-1)向下调整
插入结点:将结点插入到数组的最后一个元素,再对最后一个结点进行向上调整,upadjust(1,n+1)。
堆排序:对建立好的堆进行操作,由于堆顶为最大元素,所以将堆顶的元素与最后一个元素呼唤,再进行downadjust(1,n-1)调整,以此类推,直到调整到堆中1~1的情况。
本题是基础模板题。只需要在排序中设计是否有数组相同的情况,若相同则输出。
本题还有个陷阱,初始序列不能算作相等,因为如果初始序列相等可能会出现两种方法都可以做从而多解的情况。
#include<cstdio> #include<algorithm> using namespace std; const int N = 110; int n; int a[N], b[N], heap[N]; bool flag = false; bool cmp(int a, int b) { return a < b; } bool isSame(int a[], int b[]) { for(int i = 1; i <= n; i++) if(a[i] != b[i]) return false; return true; } void disp(int a[]) { for(int i = 1; i <= n; i++) { printf("%d", a[i]); if(i != n) printf(" "); } } void InsertionSort() { bool print = false; for(int i = 2; i <= n; i++) { if(i != 2 &&isSame(a, b)) { printf("Insertion Sort\n"); print = true; } sort(a+1, a + i + 1, cmp); if(print) { disp(a); flag = true; return ; } } } void downadjust(int low, int high) { int i = low, j = i * 2; while(j <= high) { if(j+1 <= high && heap[j+1] > heap[j]) j = j + 1; if(heap[j] > heap[i]) { swap(heap[j], heap[i]); i = j; j = i * 2; } else break; } } void HeapSort() { bool print = false; for(int i = n/2; i >= 1; i--) downadjust(i, n); for(int i = n; i >= 2; i--) { if(i != n && isSame(b, heap)) { printf("Heap Sort\n"); print = true; } swap(heap[1], heap[i]); downadjust(1, i - 1); if(print) { disp(heap); return ; } } } int main() { scanf("%d", &n); for(int i = 1; i <= n; i++) { scanf("%d", &a[i]); heap[i] = a[i]; } for(int i = 1; i <= n; i++) { scanf("%d", &b[i]); } InsertionSort(); if(!flag) HeapSort(); return 0; }