7-1 交换二叉树中每个结点的左孩子和右孩子 (20分)
以二叉链表作为二叉树的存储结构,交换二叉树中每个结点的左孩子和右孩子。
输入格式:
输入二叉树的先序序列。
提示:一棵二叉树的先序序列是一个字符串,若字符是‘#’,表示该二叉树是空树,否则该字符是相应结点的数据元素。
输出格式:
输出有两行:
第一行是原二叉树的中序遍历序列;
第二行是交换后的二叉树的中序遍历序列。
输入样例:
ABC##DE#G##F###
输出样例:
CBEGDFA
AFDGEBC
解题代码
#include <iostream>
#include <string>
#include <queue>
using namespace std;
struct TreeNode {
TreeNode* left;
TreeNode* right;
char val;
TreeNode(int data) {
val = data;
}
};
string str;
int pos;
//由带有空指针标记的先序序列构造二叉树
TreeNode* pre_dfs() {
char cur_val = str[pos]; pos++; //提取当前数据
if (cur_val == '#') //若是特殊数据,返回空指针
return NULL;
TreeNode* root = new TreeNode(cur_val); //创建新结点
root->left = pre_dfs();
root->right = pre_dfs();
return root;
}
//中序遍历
void in_dfs(TreeNode* root) {
if (root == NULL) //若二叉树为空,则遍历结束
return ;
in_dfs(root->left);
cout << root->val;
in_dfs(root->right);
}
void bfs(TreeNode* root) {
if (root == NULL) //若二叉树为空,则遍历结束
return ;
queue<TreeNode*> que;
que.push(root);
while (!que.empty()) {
TreeNode* cur = que.front();
que.pop();
TreeNode* temp = cur->left;
cur->left = cur->right;
cur->right = temp;
if (cur->left) que.push(cur->left);
if (cur->right) que.push(cur->right);
}
}
int main()
{
cin >> str;
TreeNode* root = pre_dfs(); //线序遍历构造二叉树
in_dfs(root); //中序遍历
cout << endl;
bfs(root); //层次遍历
in_dfs(root);
return 0;
}
7-2 列出叶结点 (25分)
对于给定的二叉树,本题要求你按从上到下、从左到右的顺序输出其所有叶节点。
输入格式:
首先第一行给出一个正整数 N(≤10),为树中结点总数。树中的结点从 0 到 N−1 编号。随后 N 行,每行给出一个对应结点左右孩子的编号。如果某个孩子不存在,则在对应位置给出 "-"。编号间以 1 个空格分隔。
输出格式:
在一行中按规定顺序输出叶节点的编号。编号间以 1 个空格分隔,行首尾不得有多余空格。
输入样例:
8
1 -
- -
0 -
2 7
- -
- -
5 -
4 6
输出样例:
4 1 5
解题代码
#include <iostream>
#include <cstring>
#include <vector>
using namespace std;
int fa[100]; //fa[i] = x 表示编号为i的结点的父节点是x
vector< vector<int> > ans; //ans[i][j] = x 表示第i层 下标为j的叶节点的值
int n;
struct TreeNode {
char left;
char right;
TreeNode() {
left = right = '-';
}
}node[100]; //node[i]直接就可以表示编号为i的结点
//前序遍历
void pre_dfs(int root, int depth) {
if (node[root].left == '-' && node[root].right == '-') {
ans[depth].push_back(root);
return;
}
if (node[root].left != '-') pre_dfs(node[root].left - '0', depth + 1);
if (node[root].right != '-') pre_dfs(node[root].right - '0', depth + 1);
}
int main()
{
int root;
memset(fa, -1, sizeof(fa));
cin >> n;
ans.resize(n + 1);
for (int i = 0; i < n; i++) {
cin >> node[i].left >> node[i].right;
if (node[i].left != '-') fa[node[i].left - '0'] = i;
if (node[i].right != '-') fa[node[i].right - '0'] = i;
}
//找根节点
for (int i = 0; i < n; i++) {
if (fa[i] == -1) {
root = i;
break;
}
}
pre_dfs(root, 1);
int first = 1;
for (int i = 1; i <= n; i++) {
for (int num : ans[i]) {
if (first) {
cout << num;
first = 0;
}
else {
cout << " " << num;
}
}
}
return 0;
}
7-3 重构二叉树 (25分) —— 由先序遍历 + 中序遍历 构建二叉树
给出两个字符串,分别表示二叉树的先序遍历(根、左子树、右子树)和中序遍历(左子树、根、右子树)的结果。
例如,对于下面的二叉树,先序遍历结果是DBACEGF,中序遍历结果是ABCDEFG。
假定二叉树的每个节点都用大写的字母标识,且对于同一棵二叉树,同一个字母不会用两次。现在请你根据给出的先序遍历和中序遍历,重构这棵二叉树。
输入格式:
输入包含一个或多个测试用例。每个测试用例一行,给出两个字符串,表示对二叉树进行先序遍历和中序遍历的结果。这两个字符串都是由大写字母组成(因此它们的长度不超过26)。
输出格式:
将每个测试用例转化为一棵二叉树,并在一行中输出树的后序遍历(左子树、右子树、根)的结果。
输入样例:
DBACEGF ABCDEFG
BCAD CBAD
输出样例:
ACBFGED
CDAB
解题代码
#include <iostream>
using namespace std;
struct TreeNode {
TreeNode* left;
TreeNode* right;
char val;
TreeNode(char x) {
left = right = NULL;
val = x;
}
};
//ipre表示先序序列在pre中的起始位置
//imid表示先序序列在mid中的起始位置
//n表示先序遍历和中序遍历中元素的个数
TreeNode* createByPreMid(const string& pre, const string& mid, int ipre, int imid, int n) {
if (n == 0) return NULL; //没有元素自然返回一个空指针
TreeNode* p = new TreeNode(pre[ipre]); //创建新结点
//在中序序列中定位根结点,以此可以划分属于当前根结点的左子树 和 右子树的范围
int i;
for(i = 0; i < n; i++) {
if (pre[ipre] == mid[imid + i])
break;
}
//如果当前结点存在左子树,那么pre[ipre+1]一定是左子树根结点的值
//而mid[imid]一定是属于左子树的最左下端的值
p->left = createByPreMid(pre, mid, ipre + 1, imid, i);
//如果当前结点存在右子树,因为前面构建左子树已经消耗了先序序列的i个元素,因为还构建了当前的根结点,所以总共消耗了i+1个元素
//同理中序遍历在此过程中也消耗了i+1个元素
//所以用于构建右子树的元素个数 = n(当前结点元素 + 左子树元素个数 + 右子树元素个数) - i(左子树元素个数) - 1(当前结点元素)
p->right = createByPreMid(pre, mid, ipre+i+1, imid+i+1, n-i-1);
return p;
}
void post_dfs(TreeNode *root) {
if (root == NULL) return ;
post_dfs(root->left);
post_dfs(root->right);
cout << root->val;
}
int main()
{
string pre, mid;
while (cin >> pre >> mid) {
int n = pre.size(); //元素的总个数
TreeNode* root = createByPreMid(pre, mid, 0, 0, n);
post_dfs(root);
cout << endl;
}
return 0;
}
练习4.1 根据后序和中序遍历输出先序遍历 (25分)
本题要求根据给定的一棵二叉树的后序遍历和中序遍历结果,输出该树的先序遍历结果。
输入格式:
第一行给出正整数N(≤30),是树中结点的个数。随后两行,每行给出N个整数,分别对应后序遍历和中序遍历结果,数字间以空格分隔。题目保证输入正确对应一棵二叉树。
输出格式:
在一行中输出Preorder:
以及该树的先序遍历结果。数字间有1个空格,行末不得有多余空格。
输入样例:
7
2 3 1 5 7 6 4
1 2 3 4 5 6 7
输出样例:
Preorder: 4 1 3 2 6 5 7
解题代码
#include <iostream>
#include <vector>
using namespace std;
struct TreeNode {
int val;
TreeNode* left;
TreeNode* right;
TreeNode(int x):val(x), left(NULL), right(NULL)
{ }
};
//由后序遍历和中序遍历序列构造二叉树
//ipost表明后序序列在post中的起始位置
//imid表明中序序列在mid中的起始位置
//n表明后序序列和中序序列中元素的个数
TreeNode* createByPostMid(vector<int>& post, vector<int>& mid, int ipost, int imid, int n) {
if (n == 0) return NULL;
TreeNode* p = new TreeNode(post[ipost+n-1]); //post[n-1]必是此树(子树)的根结点
int i;
for(i = 0; i < n; i++) { //在中序序列中定位根结点
if (mid[imid+i] == post[ipost+n-1]) break;
}
p->left = createByPostMid(post, mid, ipost, imid, i); //构建左子树(有i个结点)
p->right = createByPostMid(post, mid, ipost+i, imid+i+1, n-i-1); //构建右子树(右子树结点数=总结点数-左子树结点数-刚构建完的根结点=n-i-1)
return p;
}
void PreOrder_dfs(TreeNode* root) {
if (root == NULL) return ;
cout << " " << root->val;
PreOrder_dfs(root->left);
PreOrder_dfs(root->right);
}
int main()
{
vector<int> post;
vector<int> mid;
int n;
cin >> n;
post.resize(n);
mid.resize(n);
for(int i = 0; i < n; i++) {
cin >> post[i];
}
for(int i = 0; i < n; i++) {
cin >> mid[i];
}
TreeNode* root = createByPostMid(post, mid, 0, 0, n);
cout << "Preorder:";
PreOrder_dfs(root);
return 0;
}
7-4 列出所有祖先结点 (5分)
对于给定的二叉树,本题要求你按从上到下顺序输出指定结点的所有祖先结点。
输入格式:
首先第一行给出一个正整数 N(≤10),为树中结点总数。树中的结点从 0 到 N−1 编号。
随后 N 行,每行给出一个对应结点左右孩子的编号。如果某个孩子不存在,则在对应位置给出 "-"。编号间以 1 个空格分隔。
最后一行给出一个结点的编号i(0≤i≤N-1)。
输出格式:
在一行中按规定顺序输出i的所有祖先结点的编号。编号间以 1 个空格分隔,行首尾不得有多余空格。
输入样例:
7
2 -
- 6
- -
0 5
- -
4 1
- -
4
输出样例:
3 5
解题代码
#include <iostream>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
int fa[100]; //fa[i] = x 表示编号为i的结点的父节点是x
int n;
vector<int> ans;
int main()
{
int root, num;
memset(fa, -1, sizeof(fa));
cin >> n;
for(int i = 0; i < n; i++) {
char t1, t2;
cin >> t1 >> t2;
if (t1 != '-') fa[t1-'0'] = i;
if (t2 != '-') fa[t2-'0'] = i;
}
cin >> num;
int temp_fa = fa[num];
while (temp_fa != -1) {
ans.push_back(temp_fa);
temp_fa = fa[temp_fa];
}
reverse(ans.begin(), ans.end());
int first = 1;
for(int t : ans) {
if (first) {
cout << t;
first = 0;
} else {
cout << " " << t;
}
}
return 0;
}
7-5 前序序列创建二叉树 (25分)
编一个程序,读入用户输入的一串先序遍历字符串,根据此字符串建立一个二叉树(以二叉链表存储)。 例如如下的先序遍历字符串: ABC##DE#G##F### 其中“#”表示的是空格,代表一棵空树。然后再对二叉树进行中序遍历,输出遍历结果。
输入格式:
多组测试数据,每组测试数据一行,该行只有一个字符串,长度不超过100。
输出格式:
对于每组数据,
输出二叉树的中序遍历的序列,每个字符后面都有一个空格。
每组输出一行,对应输入的一行字符串。
输入样例:(及其对应的二叉树)
abc##de#g##f###
输出样例:
c b e g d f a
解题代码
#include <iostream>
using namespace std;
struct TreeNode {
TreeNode* left;
TreeNode* right;
char val;
TreeNode(char x):val(x),left(NULL),right(NULL)
{ }
};
TreeNode* createByPre(const string& pre, int& i) {
char ch = pre[i]; //提取当前数据
i++;
if (ch == '#') return NULL; //若是'#',返回空指针
TreeNode* root = new TreeNode(ch); //创建新结点
root->left = createByPre(pre, i); //创建左子树
root->right = createByPre(pre, i); //创建右子树
return root;
}
void in_dfs(TreeNode* root) {
if (root == NULL) return ;
in_dfs(root->left);
cout << root->val << " ";
in_dfs(root->right);
}
int main()
{
string str;
while (cin >> str) {
int i = 0;
TreeNode* root = createByPre(str, i);
in_dfs(root);
cout << endl;
}
return 0;
}
7-6 修理牧场 (25分) —— 哈夫曼树
农夫要修理牧场的一段栅栏,他测量了栅栏,发现需要N块木头,每块木头长度为整数Li个长度单位,于是他购买了一条很长的、能锯成N块的木头,即该木头的长度是Li的总和。
但是农夫自己没有锯子,请人锯木的酬金跟这段木头的长度成正比。为简单起见,不妨就设酬金等于所锯木头的长度。例如,要将长度为20的木头锯成长度为8、7和5的三段,第一次锯木头花费20,将木头锯成12和8;第二次锯木头花费12,将长度为12的木头锯成7和5,总花费为32。如果第一次将木头锯成15和5,则第二次锯木头花费15,总花费为35(大于32)。
请编写程序帮助农夫计算将木头锯成N块的最少花费。
输入格式:
输入首先给出正整数N(≤104),表示要将木头锯成N块。第二行给出N个正整数(≤50),表示每段木块的长度。
输出格式:
输出一个整数,即将木头锯成N块的最少花费。
输入样例:
8
4 5 1 2 1 3 1 1
输出样例:
49
解题代码
#include <iostream>
#include <queue>
using namespace std;
int main()
{
priority_queue<int, vector<int>, greater<int>> que;
int n, temp, sum = 0;
cin >> n;
while (n--) {
cin >> temp;
que.push(temp);
}
while (que.size() != 1) {
int x = que.top();
que.pop();
int y = que.top();
que.pop();
sum += x + y;
que.push(x+y);
}
cout << sum << endl;
return 0;
}
7-7 哈夫曼编码 (30分) —— 哈夫曼树
给定一段文字,如果我们统计出字母出现的频率,是可以根据哈夫曼算法给出一套编码,使得用此编码压缩原文可以得到最短的编码总长。然而哈夫曼编码并不是唯一的。例如对字符串"aaaxuaxz",容易得到字母 'a'、'x'、'u'、'z' 的出现频率对应为 4、2、1、1。我们可以设计编码 {'a'=0, 'x'=10, 'u'=110, 'z'=111},也可以用另一套 {'a'=1, 'x'=01, 'u'=001, 'z'=000},还可以用 {'a'=0, 'x'=11, 'u'=100, 'z'=101},三套编码都可以把原文压缩到 14 个字节。但是 {'a'=0, 'x'=01, 'u'=011, 'z'=001} 就不是哈夫曼编码,因为用这套编码压缩得到 00001011001001 后,解码的结果不唯一,"aaaxuaxz" 和 "aazuaxax" 都可以对应解码的结果。本题就请你判断任一套编码是否哈夫曼编码。
输入格式:
首先第一行给出一个正整数 N(2≤N≤63),随后第二行给出 N 个不重复的字符及其出现频率,格式如下:
c[1] f[1] c[2] f[2] ... c[N] f[N]
其中c[i]
是集合{'0' - '9', 'a' - 'z', 'A' - 'Z', '_'}中的字符;f[i]
是c[i]
的出现频率,为不超过 1000 的整数。再下一行给出一个正整数 M(≤1000),随后是 M 套待检的编码。每套编码占 N 行,格式为:
c[i] code[i]
其中c[i]
是第i
个字符;code[i]
是不超过63个'0'和'1'的非空字符串。
输出格式:
对每套待检编码,如果是正确的哈夫曼编码,就在一行中输出"Yes",否则输出"No"。
注意:最优编码并不一定通过哈夫曼算法得到。任何能压缩到最优长度的前缀编码都应被判为正确。
输入样例:
7
A 1 B 1 C 1 D 3 E 3 F 6 G 6
4
A 00000
B 00001
C 0001
D 001
E 01
F 10
G 11
A 01010
B 01011
C 0100
D 011
E 10
F 11
G 00
A 000
B 001
C 010
D 011
E 100
F 101
G 110
A 00000
B 00001
C 0001
D 001
E 00
F 10
G 11
输出样例:
Yes
Yes
No
No
解题代码
#include <iostream>
#include <queue>
#include <vector>
#include <string>
using namespace std;
struct Node {
char data;
int weight;
int parent, left, right;
int pos;
Node() {
weight = 0;
parent = left = right = -1;
}
bool operator <(const Node &t) const {
return weight > t.weight;
}
};
vector<Node> hufftree; //树中所有结点的存储空间
int n; //叶子节点数
int minn;
void Init() {
priority_queue<Node> que;
hufftree.resize(2 * n - 1); //为树中所有结点预留向量空间
for(int i = 0; i < n; i++) { //初始化hufftree,注意只有一个根结点
cin >> hufftree[i].data >> hufftree[i].weight;
hufftree[i].pos = i;
que.push(hufftree[i]);
}
for(int i = n; i < 2*n-1; i++) {
int least = que.top().pos;
que.pop();
int less = que.top().pos;
que.pop();
hufftree[least].parent = hufftree[less].parent = i;
hufftree[i].parent = -1;
hufftree[i].left = least;
hufftree[i].right = less;
hufftree[i].pos = i;
hufftree[i].weight = hufftree[least].weight + hufftree[less].weight;
que.push(hufftree[i]);
}
}
void getMinn() {
for(int i = 0; i < n; i++) {
int weight = hufftree[i].weight;
int parent = hufftree[i].parent;
int sum = 0;
while (parent != -1) {
sum++;
parent = hufftree[parent].parent;
}
sum = sum * weight;
minn += sum;
}
}
bool panduan(vector<string>& save) {
for(int i = 0; i < save.size(); i++) {
for(int j = 0; j < save.size(); j++) {
if (i == j) continue;
if (save[i].size() > save[j].size()) continue;
int k;
for(k = 0; k < save[i].size(); k++) {
if (save[j][k] != save[i][k]) break;
}
if (k >= save[i].size()) return 1;
}
}
return 0;
}
int main()
{
cin >> n;
Init();
getMinn();
int k;
cin >> k;
while (k--) {
vector<string> save;
int sum = 0;
for(int i = 0; i < n; i++) {
char t;
string s;
cin >> t >> s;
sum += s.size() * hufftree[i].weight;
save.push_back(s);
if (i == n - 1) {
//cout << sum << " ";
if (sum != minn) cout << "No" << endl;
else if (panduan(save)) {
cout << "No" << endl;
}
else cout << "Yes" << endl;
}
}
}
return 0;
}
习题4.5 顺序存储的二叉树的最近的公共祖先问题 (25分)
设顺序存储的二叉树中有编号为i和j的两个结点,请设计算法求出它们最近的公共祖先结点的编号和值。
输入格式:
输入第1行给出正整数n(≤1000),即顺序存储的最大容量;第2行给出n个非负整数,其间以空格分隔。其中0代表二叉树中的空结点(如果第1个结点为0,则代表一棵空树);第3行给出一对结点编号i和j。
题目保证输入正确对应一棵二叉树,且1≤i,j≤n。
输出格式:
如果i或j对应的是空结点,则输出ERROR: T[x] is NULL
,其中x
是i或j中先发现错误的那个编号;否则在一行中输出编号为i和j的两个结点最近的公共祖先结点的编号和值,其间以1个空格分隔。
输入样例1:
15
4 3 5 1 10 0 7 0 2 0 9 0 0 6 8
11 4
输出样例1:
2 3
输入样例2:
15
4 3 5 1 0 0 7 0 2 0 9 0 0 6 8
12 8
输出样例2:
ERROR: T[12] is NULL
对有n个结点的完全二叉树编号后,第i个结点(1≤i≤n)的编号,其双亲结点的编号是i/2(相当于去掉小数部分)
解题代码
#include <iostream>
using namespace std;
int data[1010];
int main()
{
int n, i, j;
cin >> n;
for(int i = 1; i <= n; i++)
cin >> data[i];
cin >> i >> j;
if (data[i] == 0) {
cout << "ERROR: T[" << i << "] is NULL" << endl;
}
else if (data[j] == 0) {
cout << "ERROR: T[" << j << "] is NULL" << endl;
}
else {
int fai = i, faj = j;
while (fai != faj) {
if (fai > faj) fai = fai / 2;
else if (fai < faj) faj = faj / 2;
}
cout << fai << " " << data[fai] << endl;
}
return 0;
}
目录树---用孩子兄弟表示法存储
在ZIP归档文件中,保留着所有压缩文件和目录的相对路径和名称。当使用WinZIP等GUI软件打开ZIP归档文件时,可以从这些信息中重建目录的树状结构。请编写程序实现目录的树状结构的重建工作。
输入格式:
输入首先给出正整数N(≤104),表示ZIP归档文件中的文件和目录的数量。随后N行,每行有如下格式的文件或目录的相对路径和名称(每行不超过260个字符):
- 路径和名称中的字符仅包括英文字母(区分大小写);
- 符号“\”仅作为路径分隔符出现;
- 目录以符号“\”结束;
- 不存在重复的输入项目;
- 整个输入大小不超过2MB。
输出格式:
假设所有的路径都相对于root目录。从root目录开始,在输出时每个目录首先输出自己的名字,然后以字典序输出所有子目录,然后以字典序输出所有文件。注意,在输出时,应根据目录的相对关系使用空格进行缩进,每级目录或文件比上一级多缩进2个空格。
输入样例:
7
b
c\
ab\cd
a\bc
ab\d
a\d\a
a\d\z\
输出样例:
root
a
d
z
a
bc
ab
cd
d
c
解题代码
#include <iostream>
#include <string>
using namespace std;
struct Node {
string name; //存储的数据
bool isMulu; //为1表示是目录,即会有对应的子目录(mulu)和文件(file)和brother;否则为文件,只会有brother
Node* mulu; //表示本目录的子目录
Node* file; //表示本目录的子文件(即文件直接在该目录下)
Node* brother; //如果结点表示目录,brother即表示与本目录平行的目录;如果该结点是文件,brother表示为与文件平行的文件(即文件之间都直接在同一文件夹里)
Node() {
name = "";
isMulu = 1; //默认是目录
mulu = NULL;
file = NULL;
brother = NULL;
}
}root;
void init() {
root.name = "root";
root.isMulu = 1;
}
Node* InsertMulu(Node* node, const string& s) {
if (node == NULL || node->name > s) { //node为空 或者 在字典序下node->name > s
Node* temp = new Node();
temp->name = s;
temp->brother = node;
return temp;
}
if (node->name == s) return node; //之前已经插入过了
node->brother = InsertMulu(node->brother, s);
return node;
}
Node* InsertFile(Node* node, const string& s) {
if (node == NULL || node->name > s) { //node为空 或者 在字典序下node->name > s
Node* temp = new Node();
temp->name = s;
temp->isMulu = 0;
temp->brother = node;
return temp;
}
node->brother = InsertFile(node->brother, s);
return node;
}
void Print(Node* node, int space) {
if (node != NULL) {
for(int i = 0; i < space; i++) cout << " ";
cout << node->name << endl;
if (node->isMulu == 1)
Print(node->mulu, space+2);
Print(node->file, space+2);
Print(node->brother, space);
}
}
int main()
{
init();
int n;
string s;
cin >> n;
getchar(); //吃掉输入n后带的那个'\n'
while (n--) {
Node* temp = &root;
int L = 0;
getline(cin, s);
string word;
for(int i = 0; i < s.size(); i++) {
if (s[i] == '\\') {
word = s.substr(L, i-L); //从下标为L开始截取长度为i-L位
temp->mulu = InsertMulu(temp->mulu, word); //InsertMulu返回的是该结点第一个孩子结点(子目录)的地址
temp = temp->mulu;
while (temp->name != word) temp = temp->brother; //找到name为word的兄弟结点
L = i + 1; //为了跳过'\\'
}
}
if (L < s.size()) { //没有遍历完s,说明最后一个是文件名
word = s.substr(L, s.size()-L+1);
temp->file = InsertFile(temp->file, word); //InsertFile返回的是该结点第一个孩子结点(子文件)的地址
}
}
Print(&root, 0);
return 0;
}
7-57 是否完全二叉搜索树 (30 分)---二叉树的顺序存储(利用性质构建题目要求的"二叉搜索树")
将一系列给定数字顺序插入一个初始为空的二叉搜索树(定义为左子树键值大,右子树键值小),你需要判断最后的树是否一棵完全二叉树,并且给出其层序遍历的结果。
输入格式:
输入第一行给出一个不超过20的正整数N
;第二行给出N
个互不相同的正整数,其间以空格分隔。
输出格式:
将输入的N
个正整数顺序插入一个初始为空的二叉搜索树。在第一行中输出结果树的层序遍历结果,数字间以1个空格分隔,行的首尾不得有多余空格。第二行输出YES
,如果该树是完全二叉树;否则输出NO
。
输入样例1:
9
38 45 42 24 58 30 67 12 51
输出样例1:
38 45 24 58 42 30 12 67 51
YES
输入样例2:
8
38 24 12 45 58 67 42 51
输出样例2:
38 45 24 58 42 12 67 51
NO
解题代码
#include <iostream>
using namespace std;
int a[1048579];
void insert(int root, int val) {
if (a[root] == 0) {
a[root] = val;
return;
}
if (val > a[root]) insert(root*2, val); //如果键值比目前根结点的键值大,插入到其左子树
else insert(root*2+1, val); //如果键值比目前根结点的键值小,插入到其右子树
}
int main()
{
int n, temp;
cin >> n;
for(int i=1; i <= n; i++) {
cin >> temp;
insert(1, temp);
}
int cnt = 0, i = 1, first = 1;
/*开始进行层次遍历*/
while (cnt < n) {
while (a[i] == 0) i++;
cnt++;
if (first) {
cout << a[i];
first = 0;
} else {
cout << " " << a[i];
}
i++;
}
cout << endl;
/*判别该树是否为题目描述的"完全二叉搜索树*/
if (i == n + 1) cout << "YES" << endl;
else cout << "NO" << endl;
return 0;
}
7-46 二叉搜索树的2层结点统计 (25 分)---建二叉搜索树+dfs
7-46 二叉搜索树的2层结点统计 (25 分)
二叉搜索树或者是一棵空树,或者是具有下列性质的二叉树:若它的左子树不空,则左子树上所有结点的值均小于或等于它的根结点的值;若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;它的左、右子树也分别为二叉搜索树。
将一系列数字按给定顺序插入一棵初始为空的二叉搜索树,你的任务是统计结果树中最下面 2 层的结点数。
输入格式:输入在第一行给出一个正整数 N (≤1000),为插入数字的个数。第二行给出 N 个 [−1000,1000] 区间内的整数。数字间以空格分隔。
输出格式:在一行中输出最下面 2 层的结点总数。
输入样例:9 25 30 42 16 20 20 35 -5 28 输出样例:6
解题代码
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 1010;
int root, size, l[maxn], r[maxn], vals[maxn]; //一开始root为0,但当插入结点后root会变为1
int depth_cnt[maxn], max_depth;
void Insert(int &k, int val) { //注意是传引用
if (k == 0) { //树为空(一开始)的情况 或者 插入叶结点的情况
size++;
k = size;
vals[k] = val;
return;
}
if (val > vals[k]) Insert(r[k], val); //如果键值比目前根结点的键值大,插入到其右子树
else Insert(l[k], val); //如果键值比目前根结点的键值小 或者 相等,插入到其左子树
}
void dfs(int k, int depth) {
if (k == 0) return; //如果是root==0,表示树为空 或者 某个结点为空
depth_cnt[depth]++; //深度为depth的结点数加一
max_depth = max(max_depth, depth);
dfs(l[k], depth+1);
dfs(r[k],depth+1);
}
int main()
{
int n, temp;
cin >> n;
for(int i = 1; i <= n; i++) {
cin >> temp;
Insert(root, temp);
}
dfs(root, 1);
cout << depth_cnt[max_depth] + depth_cnt[max_depth-1] << endl;
return 0;
}