二叉树练习记录

本博客探讨了多种二叉树的操作,包括如何根据先序和中序遍历构建二叉树、如何交换二叉树节点的左右孩子、找出叶节点、重构二叉树、寻找祖先节点、构建哈夫曼树以及处理哈夫曼编码问题。同时,介绍了如何利用顺序存储构建特定类型的二叉树,并解决相关问题,如判断是否为完全二叉搜索树以及统计二叉搜索树的二层节点数。文章覆盖了二叉树的构造、遍历、性质判断等多个方面。
摘要由CSDN通过智能技术生成

 

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。

binary_tree.jpg


假定二叉树的每个节点都用大写的字母标识,且对于同一棵二叉树,同一个字母不会用两次。现在请你根据给出的先序遍历和中序遍历,重构这棵二叉树。

输入格式:

输入包含一个或多个测试用例。每个测试用例一行,给出两个字符串,表示对二叉树进行先序遍历和中序遍历的结果。这两个字符串都是由大写字母组成(因此它们的长度不超过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。

输出格式:

对于每组数据,

输出二叉树的中序遍历的序列,每个字符后面都有一个空格。

每组输出一行,对应输入的一行字符串。

输入样例:(及其对应的二叉树)

snap858.jpg

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块木头,每块木头长度为整数L​i​​个长度单位,于是他购买了一条很长的、能锯成N块的木头,即该木头的长度是L​i​​的总和。

但是农夫自己没有锯子,请人锯木的酬金跟这段木头的长度成正比。为简单起见,不妨就设酬金等于所锯木头的长度。例如,要将长度为20的木头锯成长度为8、7和5的三段,第一次锯木头花费20,将木头锯成12和8;第二次锯木头花费12,将长度为12的木头锯成7和5,总花费为32。如果第一次将木头锯成15和5,则第二次锯木头花费15,总花费为35(大于32)。

请编写程序帮助农夫计算将木头锯成N块的最少花费。

输入格式:

输入首先给出正整数N(≤10​4​​),表示要将木头锯成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(≤10​4​​),表示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;
}
	
	
	
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

重剑DS

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值