目录
A1043 Is it a Binary Search Tree
A1064 Complete Binary Search Tree
A1099 Build a Binary Search Tree
A1123 Is It a Complete AVL Tree
A1079 Total Sales of Supply Chain
A1090 Highest Price in Supply Chain
A1106 Lowest Price in Supply Chain
A1119 Pre- and Post-order Traversals
二叉树
二叉树的遍历
A1020 Tree Traversals
思路分析:
已知后序和中序,重建二叉树,求层序。
由于需要重建出二叉树,因此用指针实现二叉链表比较合适。
注意点:左子树和右子树序列的左右下标。
AC代码:
#include<cstdio>
#include<queue>
using namespace std;
const int maxn = 35;
int post[maxn], in[maxn], n;
struct node {
int data;
node* lchild;
node* rchild;
};
node* create(int postL, int postR, int inL, int inR) {
if(postL > postR) return NULL;
node* root = new node;
root->data = post[postR];
int k;
for(k = inL; k <= inR; k ++)
if(in[k] == post[postR])
break;
int numLeft = k - inL;
root->lchild = create(postL, postL + numLeft - 1, inL, k - 1);
root->rchild = create(postL + numLeft, postR - 1, k + 1, inR);
return root;
}
int cnt = 0;
void layerOrder(node* root) {
queue<node*> q;
q.push(root);
while(!q.empty()) {
node* now = q.front();
q.pop();
if(cnt ++ > 0) printf(" ");
printf("%d", now->data);
if(now->lchild != NULL) {
q.push(now->lchild);
}
if(now->rchild != NULL) {
q.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 = create(0, n - 1, 0, n - 1);
layerOrder(root);
return 0;
}
A1086 Tree Traversals Again
第二遍思路:
可以发现规律:Push的顺序是先序,Pop的顺序是中序。已知先序和中序,重建二叉树,求后序。
AC代码:
#include<cstdio>
#include<stack>
using namespace std;
const int maxn = 35;
int pre[maxn], in[maxn];
struct node {
int data;
node* lchild;
node* rchild;
};
node* create(int preL, int preR, int inL, int inR) {
if(preL > preR) return NULL; //空树
node* root = new node;
root->data = pre[preL];
int k;
for(k = inL; k <= inR; k ++) {
if(in[k] == pre[preL])
break;
}
int numLeft = k - inL;
root->lchild = create(preL + 1, preL + numLeft, inL, k - 1);
root->rchild = create(preL + numLeft + 1, preR, k + 1, inR);
return root;
}
int cnt = 0;
void postorder(node* root) {
if(root == NULL) return;
postorder(root->lchild);
postorder(root->rchild);
if(cnt ++ > 0) printf(" ");
printf("%d", root->data);
}
int main() {
int n;
scanf("%d", &n);
char op[5];
int x, numPre = 0, numIn = 0;
stack<int> s;
for(int i = 0; i < 2 * n; i ++) {
scanf("%s", op);
if(op[1] == 'u') {
scanf("%d", &x);
pre[numPre ++] = x;
s.push(x);
}
else {
int now = s.top();
s.pop();
in[numIn ++] = now;
}
}
postorder(create(0, n - 1, 0, n - 1));
return 0;
}
A1102 Invert a Binary Tree
反转一棵二叉树的方法:
法一:读入时左右节点互换即可。
法二:后序遍历交换左右子树。
思考反转子树需要用后序遍历的原因:设想一棵只有三个节点(树根,左子树,右子树)。我想要交换两棵子树,必须首先获得两棵子树的引用(即地址),因为要先访问左子树和右子树,然后才交换左右子树。(使用先序遍历也可以实现反转的效果,但从定义上来说是错误的。定义指:根->左->右)。
void postorder(int root) {
if(root == -1) return;
postorder(root->lchild);
postorder(root->rchild);
swap(node[root].lchild, node[root].rchild);
}
AC代码:
#include<cstdio>
#include<iostream>
#include<queue>
#include<string>
using namespace std;
const int maxn = 15;
struct Node {
int data;
int lchild;
int rchild;
} node[maxn];
bool isNotRoot[maxn] = {false};
int num = 0;
void levelOrder(int root) {
queue<int> q;
q.push(root);
while(!q.empty()) {
int now = q.front();
q.pop();
if(num ++ > 0) printf(" ");
printf("%d", node[now].data);
if(node[now].lchild != -1) q.push(node[now].lchild);
if(node[now].rchild != -1) q.push(node[now].rchild);
}
}
int num2 = 0;
void inorder(int root) {
if(root == -1) return;
inorder(node[root].lchild);
if(num2 ++ > 0) printf(" ");
printf("%d", node[root].data);
inorder(node[root].rchild);
}
int main() {
int n;
scanf("%d", &n);
for(int i = 0; i < n; i ++) {
string l, r;
node[i].data = i;
cin >> r >> l;
if(l != "-") {
node[i].lchild = stoi(l);
isNotRoot[stoi(l)] = true;
}
else node[i].lchild = -1;
if(r != "-") {
node[i].rchild = stoi(r);
isNotRoot[stoi(r)] = true;
}
else node[i].rchild = -1;
}
int root;
for(int i = 0; i < n; i ++)
if(isNotRoot[i] == false) {
root = i;
break;
}
levelOrder(root);
printf("\n");
inorder(root);
return 0;
}
A1127 ZigZagging on a Tree
分析:中序+后序构建二叉树,然后zigzaggingOrder遍历二叉树。zigzag序即层序的基础上隔层反转。
一开始想要直接将层序存起来,然后用两个vector分别获取奇数层和偶数层(根为第1层),奇数层倒着存放。最后合并按层号排序,输出结果。后来看到可以直接用排序解决整个问题。
关键点:DFS递归构建树时,传入参数depth和index,用于设置node的layer和index。按照数组表示树的思想(即堆)设置node的下标index,左子树为index * 2, 右子树为index * 2 + 1(数组下标从1开始)。设置好node后存入vector数组zigzag中。
然后排序规则即为:先按层号排序。然后奇数层按index从大到小排序,偶数层按index从小到大排序。
注:代码中的create函数可以不用返回node*,结构体node其实也可以不需要lchild和rchild成员,实际上就是DFS遍历的思想。这里保留node*是为了考虑代码一致性的问题。
感悟:结构体中有时可以设置一些变量,用于实现某种排序。这里的按index排序,其实相当于事先想象一个完全二叉树,对应位置已经标号序号。这种思想(index排序)我认为和链表排序中的order变量(order用于按链表遍历顺序认为设定的一种排序)类似。
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
const int maxn = 35;
struct node {
int data, layer, index;
node* lchild;
node* rchild;
};
int in[maxn], post[maxn];
vector<node*> zigzag;
bool cmp(node* &a, node* &b) {
if(a->layer != b->layer) return a->layer < b->layer;
else if(a->layer % 2 == 0) return a->index < b->index;
else return a->index > b->index;
}
node* create(int inL, int inR, int postL, int postR, int depth, int index) {
if(postL > postR) return NULL;
node* root = new node;
root->data = post[postR];
root->layer = depth;
root->index = index;
zigzag.push_back(root);
int k;
for(k = inL; k <= inR; k ++)
if(in[k] == post[postR])
break;
int numLeft = k - inL;
root->lchild = create(inL, k - 1, postL, postL + numLeft - 1, depth + 1, index * 2);
root->rchild = create(k + 1, inR, postL + numLeft, postR - 1, depth + 1, index * 2 + 1);
return root;
}
void zigzaggingOrder() {
sort(zigzag.begin(), zigzag.end(), cmp);
for(int i = 0; i < zigzag.size(); i ++) {
if(i > 0) printf(" ");
printf("%d", zigzag[i]->data);
}
printf("\n");
}
int main() {
int n;
node* root;
scanf("%d", &n);
for(int i = 1; i <= n; i ++)
scanf("%d", &in[i]);
for(int i = 1; i <= n; i ++)
scanf("%d", &post[i]);
root = create(1, n, 1, n, 1, 1);
zigzaggingOrder();
return 0;
}
A1130 Infix Expression
题意:中缀表达式。
解法:二叉树中序遍历。先构建二叉树,找到根节点,然后中序遍历二叉树。注意题目要求有括号输出,“(”其实是通过前序遍历实现,“)”通过后序遍历实现。
需要注意的点是最外层不能有括号,即深度为1时不带括号,因此在中序遍历传入一个depth参数,大于1则不输出括号。此外,只有出现有一个子节点是叶子节点的情况就需要加括号,而不是同时是叶子节点,因为题目中可能有负号(“-”)这种单目运算符也要加括号。
#include<cstdio>
#include<iostream>
#include<string>
using namespace std;
const int maxn = 25;
struct Node {
string data;
int left;
int right;
} node[maxn];
int isRoot[maxn] = {0};
void inorder(int root, int depth) {
if(root == -1) return;
if(depth > 1 && (node[root].left != -1 || node[root].right != -1))
printf("(");
inorder(node[root].left, depth + 1);
printf("%s", node[root].data.c_str());
inorder(node[root].right, depth + 1);
if(depth > 1 && (node[root].left != -1 || node[root].right != -1))
printf(")");
}
int main() {
int n;
scanf("%d", &n);
for(int i = 1; i <= n; i ++) {
cin >> node[i].data >> node[i].left >> node[i].right;
if(node[i].left != -1) isRoot[node[i].left] ++;
if(node[i].right != -1) isRoot[node[i].right] ++;
}
int root = 1;
while(isRoot[root] != 0) root ++;
inorder(root, 1);
return 0;
}
A1138 Postorder Traversal
分析:根据前序+中序,重建二叉树,输出后序第一个元素。 一段时间后自己重写这样的代码,发现没法完全记住。
主要有两个点:(1)node* root = new node写成node* root = NULL了。思考:首先这里是重建一棵二叉树,必定存在创建节点的操作,这里重建的思想是前序遍历的思想。所以递归前的代码是用于创建一个树的根节点的。
(2)递归的左子树边界为:[preL + 1, preL + numLeft] 和 [preL + numLeft + 1, preR]。这里最好画图分析一下。
#include<cstdio>
const int maxn = 50010;
int pre[maxn], in[maxn], post[maxn];
struct node {
int data;
node* left;
node* right;
};
node* create(int preL, int preR, int inL, int inR) {
if(preL > preR) return NULL;
node* root = new node;
root->data = pre[preL];
int k;
for(k = inL; k <= inR; k ++)
if(in[k] == pre[preL])
break;
int numLeft = k - inL;
root->left = create(preL + 1, preL + numLeft, inL, k - 1);
root->right = create(preL + numLeft + 1, preR, k + 1, inR);
return root;
}
int num = 0;
void postorder(node* root) {
if(root == NULL) return;
postorder(root->left);
postorder(root->right);
post[num ++] = root->data;
}
int main() {
int n;
scanf("%d", &n);
for(int i = 0; i < n; i ++)
scanf("%d", &pre[i]);
for(int i = 0; i < n; i ++)
scanf("%d", &in[i]);
node* root = create(0, n - 1, 0, n - 1);
postorder(root);
printf("%d", post[0]);
return 0;
}
A1151 LCA in a Binary Tree
MARK。一遍没思路。不用构建树,从数组中递归查找。中序数组便是二叉树的顺序。用一个map 类型的pos来记录这个顺序order。
#include<iostream>
#include<vector>
#include<map>
using namespace std;
map<int, int> pos;
vector<int> in, pre;
void LCA(int inL, int inR, int preRoot, int u, int v) {
if(inL > inR) return;
int inRoot = pos[pre[preRoot]], inU = pos[u], inV = pos[v];
if(inU < inRoot && inV < inRoot)
LCA(inL, inRoot - 1, preRoot + 1, u, v);
else if(inU < inRoot && inV > inRoot || inU > inRoot && inV < inRoot)
printf("LCA of %d and %d is %d.\n", u, v, in[inRoot]);
else if(inU > inRoot && inV > inRoot)
LCA(inRoot + 1, inR, preRoot + 1 + (inRoot - inL), u, v);
else if(inU == inRoot)
printf("%d is an ancestor of %d.\n", u, v);
else if(inV == inRoot)
printf("%d is an ancestor of %d.\n", v, u);
}
int main() {
int m, n, u, v;
scanf("%d %d", &m, &n);
in.resize(n + 1), pre.resize(n + 1);
for(int i = 1; i <= n; i ++) {
scanf("%d", &in[i]);
pos[in[i]] = i;
}
for(int i = 1; i <= n; i ++) scanf("%d", &pre[i]);
for(int i = 0; i < m; i ++) {
scanf("%d %d", &u, &v);
if(pos[u] == 0 && pos[v] == 0)
printf("ERROR: %d and %d are not found.\n", u, v);
else if(pos[u] == 0 || pos[v] == 0)
printf("ERROR: %d is not found.\n", pos[u] == 0 ? u : v);
else
LCA(1, n, 1, u, v);
}
return 0;
}
BST
BST是一棵数据域有序的二叉树。
A1043 Is it a Binary Search Tree
分析:判断是否是BST的前序遍历或者镜像BST的前序遍历,如果是,打印出相应的后序遍历。
思路:构建BST,将前序和镜像前序的序列保存在vector中。
关键点:镜像BST的前序遍历,只需要遍历时,先右子树后左子树即可。
#include<cstdio>
#include<vector>
using namespace std;
struct node {
int data;
node* left;
node* right;
};
void insert(node* &root, int x) {
if(root == NULL) { //空节点
root = new node;
root->data = x;
root->left = root->right = NULL;
return;
}
if(x < root->data) insert(root->left, x);
else insert(root->right, x);
}
void preorder(node* root, vector<int>&vi) {
if(root == NULL) return;
vi.push_back(root->data);
preorder(root->left, vi);
preorder(root->right, vi);
}
void preorderMirror(node* root, vector<int>&vi) {
if(root == NULL) return;
vi.push_back(root->data);
preorderMirror(root->right, vi);
preorderMirror(root->left, vi);
}
void postorder(node* root, vector<int>&vi) {
if(root == NULL) return;
postorder(root->left, vi);
postorder(root->right, vi);
vi.push_back(root->data);
}
void postorderMirror(node* root, vector<int>&vi) {
if(root == NULL) return;
postorderMirror(root->right, vi);
postorderMirror(root->left, vi);
vi.push_back(root->data);
}
vector<int> origin, pre, preM, post, postM;
int main() {
int n, data;
node* root = NULL;
scanf("%d", &n);
for(int i = 0; i < n; i ++) {
scanf("%d", &data);
origin.push_back(data);
insert(root, data);
}
preorder(root, pre);
preorderMirror(root, preM);
postorder(root, post);
postorderMirror(root, postM);
if(origin == pre) {
printf("YES\n");
for(int i = 0; i < post.size(); i ++) {
if(i > 0) printf(" ");
printf("%d", post[i]);
}
}
else if(origin == preM) {
printf("YES\n");
for(int i = 0; i < postM.size(); i ++) {
if(i > 0) printf(" ");
printf("%d", postM[i]);
}
}
else {
printf("NO\n");
}
return 0;
}
A1064 Complete Binary Search Tree
分析:输出一棵完全二叉搜索树的层序遍历序列。
关键点:
BST的中序遍历是递增序列。完全二叉树CBT的左孩子是2 * x,右孩子是2 * x + 1(下标从1开始)。
因此可以将输入序列从小到大排序,这样就得到了和BST的中序遍历相同的序列,所以可以对数组表示的CBT进行中序遍历,将数字填入数组。这样就能得到一棵CBST。
注意:完全二叉树CBT达到空节点的标志是当前节点编号root > 节点个数n。
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn = 1010;
int CBT[maxn], number[maxn], n;
int index = 0;
void inOrder(int root) {
if(root > n) return; //空节点
inOrder(root * 2);
CBT[root] = number[index ++];
inOrder(root * 2 + 1);
}
int main() {
scanf("%d", &n);
for(int i = 0; i < n; i ++) {
scanf("%d", &number[i]);
}
sort(number, number + n);
inOrder(1);
for(int i = 1; i <= n; i ++) {
if(i > 1) printf(" ");
printf("%d", CBT[i]);
}
return 0;
}
A1099 Build a Binary Search Tree
分析:构建一棵BST,并输出层序遍历序列。
关键点:BST的中序遍历是递增序列。因此读入序列,从小到大排序,读入给定二叉树结构,中序遍历这颗二叉树,将增序序列填入二叉树即可。然后输出层序遍历结果。
#include<cstdio>
#include<queue>
#include<algorithm>
using namespace std;
const int maxn = 110;
struct Node {
int data;
int lchild, rchild;
} node[maxn];
int in[maxn];
int index = 0;
void inorder(int root) {
if(root == -1) return;
inorder(node[root].lchild);
node[root].data = in[index ++];
inorder(node[root].rchild);
}
int num = 0;
void layerOrder(int root) {
queue<int> q;
q.push(root);
while(!q.empty()) {
int now = q.front();
q.pop();
if(num ++ > 0) printf(" ");
printf("%d", node[now].data);
if(node[now].lchild != -1) q.push(node[now].lchild);
if(node[now].rchild != -1) q.push(node[now].rchild);
}
}
int main() {
int n, l, r;
scanf("%d", &n);
for(int i = 0; i < n; i ++) {
scanf("%d %d", &l, &r);
node[i].lchild = l;
node[i].rchild = r;
}
for(int i = 0; i < n; i ++) {
scanf("%d", &in[i]);
}
sort(in, in + n);
inorder(0);
layerOrder(0);
return 0;
}
A1115 Counting Nodes in a BST
分析:遍历BST统计最后两层节点数。
思路:第一想法,可以通过将输入序列排序得到BST的中序遍历结果。然后可以想到,对BST中序遍历填入序列。那么就需要构建一棵BST。题意说按插入顺序,那么可以写插入函数构建这颗BST。当BST构建好并中序填入数据后,对BST进行DFS并用hashTable统计每层节点数,最后输出最后两层节点数。
关键点:
首先看题目定义,相等的节点插入左子树。
对BST进行DFS遍历,记录最大深度。将该深度的节点数+1,然后遍历左右子树,相当于前序遍历。(本来加入了root == NULL return的判断,后来发现去掉也可以,思考一下,当到达叶子节点后,DFS先后执行统计、左子树遍历、右子树遍历,就自动会退回到上一个DFS)。
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn = 1010;
struct node {
int data;
node* lchild;
node* rchild;
};
int in[maxn];
int hashTable[maxn] = {0};
void insert(node* &root, int x) {
if(root == NULL) {
root = new node;
root->data = x;
root->lchild = root->rchild = NULL;
return;
}
if(x <= root->data) {
insert(root->lchild, x);
}
else {
insert(root->rchild, x);
}
}
int maxDepth = -1;
void DFS(node* root, int depth) {
if(depth > maxDepth) {
maxDepth = depth;
}
hashTable[depth] ++;
if(root->lchild != NULL) DFS(root->lchild, depth + 1);
if(root->rchild != NULL) DFS(root->rchild, depth + 1);
}
int num = 0;
void inorder(node* root) {
if(root == NULL) return;
inorder(root->lchild);
root->data = in[num ++];
inorder(root->rchild);
}
int main() {
int n;
node* root = NULL;
scanf("%d", &n);
for(int i = 0; i < n; i ++) {
scanf("%d", &in[i]);
insert(root, in[i]);
}
sort(in, in + n);
inorder(root);
DFS(root, 0);
int n1 = hashTable[maxDepth];
int n2 = hashTable[maxDepth - 1];
printf("%d + %d = %d", n1, n2, n1 + n2);
return 0;
}
A1143 Lowest Common Ancestor
分析:求二叉树上两个节点的最近公共祖先。一开始的思路是通过先序和中序(排序)构建二叉树,然后递归寻找最近公共祖先,u和v同时小于当前节点,则递归进入左子树,同时大于等于当前节点,则递归进入右子树。如果一个小于一个大于等于则当前节点即为最近公共祖先。此外用search函数查询节点是否在BST中。最后得到20分,有几个测试的没过,暂时没找到原因。
代码先放上:
#include<cstdio>
#include<algorithm>
using namespace std;
struct node {
int data;
node* left;
node* right;
};
const int maxn = 10010;
int pre[maxn], in[maxn], post[maxn];
node* create(int preL, int preR, int inL, int inR) {
if(preL > preR) return NULL;
node* root = new node;
root->data = pre[preL];
int k;
for(k = inL; k <= inR; k ++)
if(in[k] == pre[preL])
break;
int numLeft = k - inL;
root->left = create(preL + 1, preL + numLeft, inL, k - 1);
root->right = create(preL + numLeft + 1, preR, k + 1, inR);
return root;
}
int num = 0;
void inorder(node* root) {
if(root == NULL) return;
inorder(root->left);
root->data = in[num ++];
inorder(root->right);
}
bool search(node* root, int data) {
if(root == NULL) return false;
if(data == root->data) return true;
else if(data < root->data) search(root->left, data);
else search(root->right, data);
}
node* LCA(node* root, int u, int v) {
if(root == NULL) return NULL;
if(u < root->data && v < root->data)
return LCA(root->left, u, v);
else if(u >= root->data && v >= root->data)
return LCA(root->right, u, v);
else
return root;
}
int main() {
int m, n;
scanf("%d %d", &m, &n);
for(int i = 0; i < n; i ++) {
scanf("%d", &pre[i]);
in[i] = pre[i];
}
sort(in, in + n);
node* root = create(0, n - 1, 0, n - 1);
int u, v;
for(int i = 0; i < m; i ++) {
scanf("%d %d", &u, &v);
if(search(root, u) == false && search(root, v) == false)
printf("ERROR: %d and %d are not found.\n", u, v);
else if(search(root, u) == false)
printf("ERROR: %d is not found.\n", u);
else if(search(root, v) == false)
printf("ERROR: %d is not found.\n", v);
else {
node* lca = LCA(root, u, v);
if(lca != NULL) {
if(lca->data == u) printf("%d is an ancestor of %d.\n", u, v);
else if(lca->data == v) printf("%d is an ancestor of %d.\n", v, u);
else printf("LCA of %d and %d is %d.\n", u, v, lca->data);
}
else {
if(u == v) printf("%d is an ancestor of %d.\n", u, v);
}
}
}
return 0;
}
看了柳神的代码,思路大致为直接在前序数组中判断。顺序遍历前序数组,当出现当前节点比一个小,比另一个大的情况,或者等于其中的一个时,此时的位置可能为最近公共祖先。
用一个map记录pre数组中出现的节点,可以判断u或v是否存在BST中,当存在时,输出最近公共祖先。
#include<iostream>
#include<vector>
#include<map>
using namespace std;
map<int, bool> mp;
int main() {
int m, n, u, v, a;
scanf("%d %d", &m, &n);
vector<int> pre(n);
for(int i = 0; i < n; i ++) {
scanf("%d", &pre[i]);
mp[pre[i]] = true;
}
for(int i = 0; i < m; i ++) {
scanf("%d %d", &u, &v);
for(int j = 0; j < n; j ++) {
a = pre[j];
if((a > u && a < v) || (a > v && a < u) || a == u || a == v)
break;
}
if(mp[u] == false && mp[v] == false)
printf("ERROR: %d and %d are not found.\n", u, v);
else if(mp[u] == false || mp[v] == false)
printf("ERROR: %d is not found.\n", mp[u] ? v : u);
else if(a == u || a == v)
printf("%d is an ancestor of %d.\n", a, a == u ? v : u);
else
printf("LCA of %d and %d is %d.\n", u, v, a);
}
return 0;
}
AVL树
AVL树是平衡BST。左右子树高度差称为平衡因子。
A1066 Root of AVL Tree
分析:构建AVL树,得到树根。
#include<cstdio>
#include<algorithm>
using namespace std;
struct node {
int v, height;
node* lchild;
node* rchild;
};
node* newNode(int v) {
node* Node = new node;
Node->v = v;
Node->height = 1;
Node->lchild = Node->rchild = NULL;
return Node;
}
int getHeight(node* root) {
if(root == NULL) return 0;
return root->height;
}
int getBF(node* root) {
return getHeight(root->lchild) - getHeight(root->rchild);
}
void updateHeight(node* root) {
root->height = max(getHeight(root->lchild), getHeight(root->rchild)) + 1;
}
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 v) {
if(root == NULL) {
root = newNode(v);
return;
}
if(v < root->v) {
insert(root->lchild, v);
updateHeight(root);
if(getBF(root) == 2) {
if(getBF(root->lchild) == 1) {
R(root);
}
else if(getBF(root->lchild) == -1) {
L(root->lchild);
R(root);
}
}
}
else {
insert(root->rchild, v);
updateHeight(root);
if(getBF(root) == -2) {
if(getBF(root->rchild) == -1) {
L(root);
}
else if(getBF(root->rchild) == 1) {
R(root->rchild);
L(root);
}
}
}
}
node* create(int data[], int n) {
node* root = NULL;
for(int i = 0; i < n; i ++) {
insert(root, data[i]);
}
return root;
}
int main() {
int n, data;
scanf("%d", &n);
node* root = NULL;
for(int i = 0; i < n; i ++) {
scanf("%d", &data);
insert(root, data);
}
printf("%d", root->v);
return 0;
}
A1123 Is It a Complete AVL Tree
分析:构建AVL树(平衡BST),输出层序遍历结果。然后判断AVL树是不是完全二叉树。
关键点:DFS遍历求最大index,若最大index == n,则是完全二叉树,否则不是。
#include<cstdio>
#include<queue>
using namespace std;
struct node {
int v, height;
node* lchild;
node* rchild;
};
node* newNode(int v) {
node* Node = new node;
Node->v = v;
Node->height = 1;
Node->lchild = Node->rchild = NULL;
return Node;
}
int getHeight(node* root) {
if(root == NULL) return 0;
return root->height;
}
void updateHeight(node* &root) {
root->height = max(getHeight(root->lchild), getHeight(root->rchild)) + 1;
}
int getBF(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 = newNode(x);
return;
}
if(x < root->v) {
insert(root->lchild, x);
updateHeight(root);
if(getBF(root) == 2) {
if(getBF(root->lchild) == 1) {
R(root);
}
else if(getBF(root->lchild) == -1) {
L(root->lchild);
R(root);
}
}
}
else {
insert(root->rchild, x);
updateHeight(root);
if(getBF(root) == -2) {
if(getBF(root->rchild) == -1) {
L(root);
}
else if(getBF(root->rchild) == 1) {
R(root->rchild);
L(root);
}
}
}
}
void levelOrder(node* root) {
queue<node*> q;
q.push(root);
int num = 0;
while(!q.empty()) {
node* now = q.front();
q.pop();
if(num ++ > 0) printf(" ");
printf("%d", now->v);
if(now->lchild != NULL) q.push(now->lchild);
if(now->rchild != NULL) q.push(now->rchild);
}
printf("\n");
}
int maxIndex = -1;
void DFS(node* root, int index) {
if(index > maxIndex) maxIndex = index;
if(root->lchild != NULL) DFS(root->lchild, index * 2);
if(root->rchild != NULL) DFS(root->rchild, index * 2 + 1);
}
int main() {
int n, data;
node* root = NULL;
scanf("%d", &n);
for(int i = 0; i < n; i ++) {
scanf("%d", &data);
insert(root, data);
}
levelOrder(root);
DFS(root, 1);
if(maxIndex == n) printf("YES\n");
else printf("NO\n");
return 0;
}
完全二叉树(判断)
A1110 Complete Binary Tree
分析:读取一棵树的结构,判断是否是完全二叉树。
对二叉树进行DFS遍历相当于进行前序遍历。index从1开始,进入左子树时,index * 2;进入右子树时,index * 2 + 1。如果这棵二叉树是完全二叉树,那么maxIndex将等于n。
关键点:完全二叉树的下标值 == 节点个数n
#include<cstdio>
#include<iostream>
#include<string>
using namespace std;
const int maxn = 25;
struct Node {
int left;
int right;
} node[maxn];
bool isNotRoot[maxn] = {0};
int maxIndex = -1, ans;
void DFS(int root, int index) {
if(index > maxIndex) {
maxIndex = index;
ans = root;
}
if(node[root].left != -1) DFS(node[root].left, index * 2);
if(node[root].right != -1) DFS(node[root].right, index * 2 + 1);
}
int main() {
int n;
scanf("%d", &n);
for(int i = 0; i < n; i ++) {
string l, r;
cin >> l >> r;
if(l != "-") {
node[i].left = stoi(l);
isNotRoot[stoi(l)] = true;
}
else node[i].left = -1;
if(r != "-") {
node[i].right = stoi(r);
isNotRoot[stoi(r)] = true;
}
else node[i].right = -1;
}
int root = 0;
while(isNotRoot[root] != false) root ++;
DFS(root, 1);
if(maxIndex == n)
printf("YES %d", ans);
else
printf("NO %d", root);
return 0;
}
红黑树(判断)
A1135 Is It A Red-Black Tree
分析:不要被红黑树和它的定义吓到。如果考虑让你判断这样一棵二叉树是否满足以下5个条件,而不告诉你是红黑树可能会简单点。主要用到二叉树的递归处理。如构建二叉树。
判断孩子是否是黑色,注意空节点是黑色。当父节点是红色时,如果有子节点,且子节点也为红色,则不是红黑树。
注意judgeChildren()的递归逻辑: 首先判断递归结束条件:空节点(黑色)则返回false;然后判断当前节点(即父节点)是否是红色,是红色的情况下,判断它的左右子节点,是黑色则不处理;最后递归判断左右子树是否满足条件。前序遍历的思想。
judgeBlackNum()判断当前节点的后继节点上的任意简单路径上的黑色节点个数是否相同,那么首先就需要一个函数统计某个节点到叶子的简单路径上的黑色节点个数。
统计黑色节点个数countBlack()的递归逻辑:递归结束条件,如果是空节点返回0;然后递归计算左右子树的黑色节点数,最后计算当前节点路径上的黑色节点数。如果当前节点是黑色,那么这颗树的黑色节点数等于左右黑色节点数的较大值 + 1;如果是红色,则等于左右黑色节点数较大值。后序遍历的思想。
judgeBlackNum()的递归逻辑:递归结束条件:空节点(黑色)返回true;然后统计左右子树的黑色节点数,如果左右子树简单路径上的黑色节点数不相等,那么则不符合红黑树的定义,返回false;最后递归判断左右子树是否满足条件。前序遍历的思想。
#include<cstdio>
#include<algorithm>
using namespace std;
struct node {
int data;
node* left;
node* right;
};
node* build(node* root, int data) {
if(root == NULL) {
root = new node;
root->data = data;
root->left = root->right = NULL;
}
else if(abs(data) <= abs(root->data)) {
root->left = build(root->left, data);
}
else {
root->right = build(root->right, data);
}
return root;
}
bool judgeChildren(node* root) {
if(root == NULL) return true;
if(root->data < 0) {
if(root->left != NULL && root->left->data < 0) return false;
if(root->right != NULL && root->right->data < 0) return false;
}
return judgeChildren(root->left) && judgeChildren(root->right);
}
int countBlack(node* root) {
if(root == NULL) return 0;
int l = countBlack(root->left);
int r = countBlack(root->right);
return root->data > 0 ? max(l, r) + 1 : max(l, r);
}
bool judgeBlackNum(node* root) {
if(root == NULL) return true;
int l = countBlack(root->left);
int r = countBlack(root->right);
if(l != r) return false;
return judgeBlackNum(root->left) && judgeBlackNum(root->right);
}
int main() {
int k, n, data[35];
scanf("%d", &k);
for(int i = 0; i < k; i ++) {
scanf("%d", &n);
node* root = NULL;
for(int j = 0; j < n; j ++) {
scanf("%d", &data[j]);
root = build(root, data[j]);
}
if(data[0] < 0 || judgeChildren(root) == false || judgeBlackNum(root) == false)
printf("No\n");
else
printf("Yes\n");
}
return 0;
}
树
树的遍历
树的遍历可以用DFS实现。
A1053 Path of Equal Weight
思路:DFS可以看成树的先根遍历。即把判断叶节点的计算放在DFS遍历子树前面。
numNode作为path数组的下标,便于随时将新节点加入路径或将旧节点覆盖。如果使用vector,则需在DFS前push_back()在DFS之和pop_back()。
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
const int maxn = 110;
struct Node {
int weight;
vector<int> child;
} node[maxn];
int n, m, s, path[maxn];
bool cmp(int a, int b) {
return node[a].weight > node[b].weight;
}
void DFS(int index, int numNode, int sum) {
if(sum > s) return;
if(sum == s) {
if(node[index].child.size() != 0) return;//还没到叶子节点
for(int i = 0; i < numNode; i ++) {
printf("%d", node[path[i]].weight);
if(i < numNode - 1) printf(" ");
else printf("\n");
}
return;
}
for(int i = 0; i < node[index].child.size(); i ++) {
int child = node[index].child[i];
path[numNode] = child;
DFS(child, numNode + 1, sum + node[child].weight);
}
}
int main() {
scanf("%d %d %d", &n, &m, &s);
for(int i = 0; i < n; i ++) {
scanf("%d", &node[i].weight);
}
int id, k, cid;
for(int i = 0; i < m; i ++) {
scanf("%d %d", &id, &k);
for(int j = 0; j < k; j ++) {
scanf("%d", &cid);
node[id].child.push_back(cid);
}
sort(node[id].child.begin(), node[id].child.end(), cmp);
}
path[0] = 0; //树根
DFS(0, 1, node[0].weight);
return 0;
}
A1079 Total Sales of Supply Chain
分析:DFS求树的最大高度和带权路径和。
对DFS的理解:先根遍历,即先判断叶节点,然后遍历子树。
#include<cstdio>
#include<cmath>
#include<vector>
using namespace std;
const int maxn = 100010;
struct Node {
int product;
vector<int> child;
} node[maxn];
int n;
double p, r, ans = 0;
void DFS(int index, int depth) {
if(node[index].child.size() == 0) {
ans += node[index].product * p * pow(1 + r, depth);
return;
}
for(int i = 0; i < node[index].child.size(); i ++) {
DFS(node[index].child[i], depth + 1);
}
}
int main() {
int k, cid;
scanf("%d %lf %lf", &n, &p, &r);
r /= 100;
for(int i = 0; i < n; i ++) {
scanf("%d", &k);
for(int j = 0; j < k; j ++) {
scanf("%d", &cid);
node[i].child.push_back(cid);
}
if(k == 0) {
scanf("%d", &node[i].product);
}
}
DFS(0, 0);
printf("%.1f\n", ans);
return 0;
}
A1090 Highest Price in Supply Chain
分析:DFS求树的最大高度和高度为最大高度的叶子数量。
由于树节点不存在数据域,可直接用一个vector<int> child[maxn]存储子节点。类似于图的邻接表。
#include<cstdio>
#include<cmath>
#include<vector>
using namespace std;
const int maxn = 100010;
vector<int> child[maxn];
double p, r;
int n, maxDepth = 0, numLeaf;
void DFS(int index, int depth) {
if(child[index].size() == 0) {//叶子节点
if(depth > maxDepth) {
maxDepth = depth;
numLeaf = 1;
}
else if(depth == maxDepth) {
numLeaf ++;
}
return;
}
for(int i = 0; i < child[index].size(); i ++) {
DFS(child[index][i], depth + 1);
}
}
int main() {
scanf("%d %lf %lf", &n, &p, &r);
r /= 100;
int father, root;
for(int i = 0; i < n; i ++) {
scanf("%d", &father);
if(father != -1)
child[father].push_back(i);
else
root = i;
}
DFS(root, 0);
printf("%.2f %d",p * pow(1 + r, maxDepth), numLeaf);
return 0;
}
A1094 The Largest Generation
分析:DFS求最大层宽(树宽)。
关键思想:用hashTable统计每层节点数。
#include<cstdio>
#include<vector>
using namespace std;
const int maxn = 110;
vector<int> child[maxn];
int hashTable[maxn] = {0}; //记录每层节点个数
void DFS(int index, int level) {
hashTable[level] ++;
for(int i = 0; i < child[index].size(); i ++)
DFS(child[index][i], level + 1);
}
int main() {
int n, m, parent, k, cid;
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", &cid);
child[parent].push_back(cid);
}
}
DFS(1, 1); //根节点为1,层号为1
int maxLevel = -1, maxValue = 0;
for(int i = 1; i < maxn; i ++) {
if(hashTable[i] > maxValue) {
maxValue = hashTable[i];
maxLevel = i;
}
}
printf("%d %d", maxValue, maxLevel);
return 0;
}
A1106 Lowest Price in Supply Chain
分析:DFS求叶子的最小高度及这一高度叶子数量。
#include<cstdio>
#include<cmath>
#include<vector>
using namespace std;
const int maxn = 100010;
vector<int> child[maxn];
int minDepth = maxn, numLeaf = 0;
void DFS(int index, int depth) {
if(child[index].size() == 0) {//叶子
if(depth < minDepth) {
minDepth = depth;
numLeaf = 1;
}
else if(depth == minDepth) {
numLeaf ++;
}
return;
}
for(int i = 0; i < child[index].size(); i ++)
DFS(child[index][i], depth + 1);
}
int main() {
int n, k, cid;
double p, r;
scanf("%d %lf %lf", &n, &p, &r);
r /= 100;
for(int i = 0; i < n; i ++) {
scanf("%d", &k);
for(int j = 0; j < k; j ++) {
scanf("%d", &cid);
child[i].push_back(cid);
}
}
DFS(0, 0);
printf("%.4f %d", p * pow(1 + r, minDepth), numLeaf);
return 0;
}
A1004 Counting Leaves
分析:DFS求每一层的叶子数(同时求最大层数)。
关键点:用hashTable统计每一层叶子数。
#include<cstdio>
#include<vector>
using namespace std;
const int maxn = 110;
vector<int> child[maxn];
int hashTable[maxn] = {0};
int maxDepth = 0;
void DFS(int index, int depth) {
if(child[index].size() == 0) {
hashTable[depth] ++;
if(depth > maxDepth)
maxDepth = depth;
return;
}
for(int i = 0; i < child[index].size(); i ++)
DFS(child[index][i], depth + 1);
}
int main() {
int n, m, pid, cid, k;
scanf("%d %d", &n, &m);
if(n == 0) return 0;
for(int i = 0; i < m; i ++) {
scanf("%d %d", &pid, &k);
for(int j = 0; j < k; j ++) {
scanf("%d", &cid);
child[pid].push_back(cid);
}
}
DFS(1, 1);
for(int i = 1; i <= maxDepth; i ++) {
if(i > 1) printf(" ");
printf("%d", hashTable[i]);
}
return 0;
}
A1119 Pre- and Post-order Traversals
MARK
分析:前序 + 后序 求 中序。当一个节点只有左子树(或只有右子树)时,将左子树变成右子树(或右子树变成左子树)前后,它的中序遍历就改变了。当节点同时有左子树和右子树时,它的中序遍历唯一。
关键点:因此判断某个根节点是否只有左子树或只有右子树则可以判断中序遍历(二叉树)是否唯一。
然后要得到中序遍历的结果,可以按照中序遍历的顺序递归构建序列。边界条件是:当某个节点只有一个节点,即没有左右子树,则它的中序遍历就是它本身。
左右子树的划分:当后序数组的最后一个节点是根的话,那么以倒数第二个节点为根,在前序数组中找到这个节点,这个节点将左右子树分开,当这个节点和前序数组第一个节点(即根节点)之间没有节点,则说明没有左子树,则二叉树不唯一。
#include<cstdio>
#include<vector>
using namespace std;
const int maxn = 35;
int pre[maxn], post[maxn];
vector<int> in;
bool isUnique = true;
void create(int preL, int preR, int postL, int postR) {
if(preL == preR) { //某个节点只有一个节点时,它的中序遍历就是它本身
in.push_back(pre[preL]);
return;
}
if(pre[preL] == post[postR]) {
int i = preL + 1;
while(i <= preR && pre[i] != post[postR - 1]) i ++;
if(i - preL > 1)
create(preL + 1, i - 1, postL, postL + (i - preL - 1) - 1);
else
isUnique = false;
in.push_back(post[postR]);
create(i, preR, postL + (i - preL - 1), postR - 1);
}
}
int main() {
int n;
scanf("%d", &n);
for(int i = 0; i < n; i ++)
scanf("%d", &pre[i]);
for(int i = 0; i < n; i ++)
scanf("%d", &post[i]);
create(0, n - 1, 0, n - 1);
if(isUnique == true) printf("Yes\n");
else printf("No\n");
for(int i = 0; i < in.size(); i ++) {
if(i > 0) printf(" ");
printf("%d", in[i]);
}
printf("\n");
return 0;
}