《算法笔记》学习日记——9.3 树的遍历&9.4 二叉查找树(BST)

目录

  • 9.3 树的遍历
    • 问题 A: 树查找
    • 问题 B: 树的高度
    • 小结
  • 9.4 二叉查找树(BST)
    • 问题 A: 二叉排序树
    • 问题 B: 二叉搜索树
    • 小结

9.3 树的遍历

Codeup Contest ID:100000612

问题 A: 树查找

题目描述
有一棵树,输出某一深度的所有节点,有则输出这些节点,无则输出EMPTY。该树是完全二叉树。
输入
输入有多组数据。
每组输入一个n(1<=n<=1000),然后将树中的这n个节点依次输入,再输入一个d代表深度。
输出
输出该树中第d层得所有节点,节点间用空格隔开,最后一个节点后没有空格。
样例输入

5
1 2 3 4 5 
7
7
1 2 3 4 5 6 7 
2
0

样例输出

EMPTY
2 3

思路
这题刚一拿到我还想着用vector容器来存当前结点的孩子结点,然后一遍BFS得到所有结点的层次,再根据层次输出所有结点。

但是懒癌让我发现这一题其实根本用不着用数组存(但是又得接受输入的那n个数,所以设个vector吧,节省点内存空间,输入完清了就好了),之所以这么说是因为题目说了这是一棵完全二叉树,也就是说只有最后一层的结点是满的或者半满的,其他层的结点都是满的,所以很容易就能通过数学公式算出来。

稍加列举就不难发现,深度为d(也就是第d层)的那一层第一个结点的编号是2d-1,d的下一层(也就是d+1层)的第一个结点编号是2d,因此,如果所问的不是最后一层,那么直接输出[2d-1, 2d-1]这个区间里的所有数即可,如果所问的是最后一层,那么就输出[2d-1, n]这个区间里的所有数。至于如何判断所问的层数是不是最后一层,很简单,看一看2d-1是不是小于等于n就行了,因为2d-1是当前层的最后一个元素(如果该层是满的话),如果它比n还大,那么显然当前层就是最后一层,如果小于等于n,那就说明不是最后一层(小于n),或者说是最后一层,但是是满结点的情况,即:满二叉树(等于n)。
代码

#include
#include
#include
#include
#include
using namespace std;
vector<int> temp;
int main(){
	int n;
	while(scanf("%d", &n) != EOF){
		if(n==0) break;
		for(int i=1;i<=n;i++){
			int tmp;
			scanf("%d", &tmp);//其实这里的输入没什么用 
			temp.push_back(tmp);
		}
		temp.clear();//直接清空掉算了 
		int d;
		scanf("%d",&d);
		int first = pow(2,d-1);//计算d层的首结点编号
		int end = pow(2,d);//计算d+1层的首结点编号 
		if(first<=n&&end-1<=n){//如果d层的首元素小于等于n,说明该层结点完整 
			for(int i=first;i<=end-1;i++){//因为是完全二叉树,除了最后一层,其他肯定是满的 
				if(i==first) printf("%d", i);
				else printf(" %d", i);
			}
			printf("\n");
		}
		else if(first<=n&&end-1>n){//该层即是最后一层 
			for(int i=first;i<=n;i++){
				if(i==first) printf("%d", i);
				else printf(" %d", i);
			}
			printf("\n");
		}
		else printf("EMPTY\n");
	}
	return 0;
}

问题 B: 树的高度

题目描述
一棵树有n个节点,其中1号节点为根节点。
输入
第一行是整数n,表示节点数
后面若干行,每行两个整数a b,表示b是a的子节点。
输出
求这棵树的高度(根节点为第1层)
样例输入

5
1 2
1 3
3 4
3 5

样例输出

3

思路
这题挺简单的,但是卡了我很久,一直都是时间超限50,原因在于我输入的判定写了while(1),然后终止循环的条件写的是b==n,结果一直超时(一直超时一直爽哈),后来改用for循环写成i=0到i

中间改了不止一次的代码哈,一开始我确实是用静态树的方法来做的,但是因为时间超限的缘故,把能删的都删了,包括结构体里用来装孩子的vector容器,包括dfs函数,包括sort函数等等(因为我最初懒得在所有结点中寻找那个层次最大的结点,于是处理完所有结点的层次之后直接用sort函数给结构体数组来个从大到小的排序,最后直接输出第一个结点的层次就是我们要的答案了,但是后来想想看好像能直接int一个变量在处理的过程中实时更新高度的最大值就行了_(:з」∠)_)。

在经过一系列的删来删去之后,就得到了这串异常短小的代码……
代码

#include
#include
#include
#include
#include
using namespace std;
const int maxn = 100010;
struct node{
	int layer;//层数
}Node[maxn];
int main(){
	int n;
	while(scanf("%d", &n) != EOF){
		int deep = 1;
		Node[1].layer = 1;
		for(int i=0;i<n-1;i++){
			int a, b;
			scanf("%d%d", &a, &b);
			Node[b].layer = Node[a].layer + 1;//b的层次加1
			if(Node[b].layer>deep) deep = Node[b].layer;//更新deep的值 
		}
		printf("%d\n", deep);
	}
	return 0;
}

小结

感觉树的遍历和之前学的DFS、BFS本质上是一样的,因为之前“岔路口”的概念,在一棵树上也同样能得到体现,不过非二叉树的话更多的用的是静态数组实现,而不是二叉树的动态链表实现(感觉用二叉树的链表来做先序中序后序遍历好方便的说),在这里只要记住先序中序后序遍历用DFS,层次用BFS遍历就好啦。

9.4 二叉查找树(BST)

Codeup Contest ID:100000613

问题 A: 二叉排序树

题目描述
输入一系列整数,建立二叉排序数,并进行前序,中序,后序遍历。
输入
输入第一行包括一个整数n(1<=n<=100)。接下来的一行包括n个整数。
输出
可能有多组测试数据,对于每组数据,将题目所给数据建立一个二叉排序树,并对二叉排序树进行前序、中序和后序遍历。每种遍历结果输出一行。每行最后一个数据之后有一个空格。
样例输入

1
2 
2
8 15 
4
21 10 5 39 

样例输出

2 
2 
2 
8 15 
8 15 
15 8 
21 10 5 39 
5 10 21 39 
5 10 39 21 

思路
这题很简单,就当是对BST的基本操作练习吧,主要流程就是把读入的数据建一棵BST,然后分别对这棵BST进行前序、中序、后序遍历即可。
代码

#include
#include
#include
#include
#include
using namespace std;
const int maxn = 101;
struct node{
	int data;
	node* lchild;
	node* rchild;
};
int Data[maxn]={0};//存放输入的数
node* newNode(int x){
	node* Node = new node;
	Node->data = x;
	Node->lchild = Node->rchild = NULL;
	return Node;
}
void Insert(node* &root, int x){
	if(root==NULL){
		root = newNode(x);
		return;
	}
	if(x==root->data) return;
	else if(x<root->data) Insert(root->lchild, x);
	else Insert(root->rchild, x);
} 
node* Create(int data[], int n){
	node* root = NULL;
	for(int i=0;i<n;i++) Insert(root, data[i]);
	return root;
}
void preorder(node* root){
	if(root==NULL) return;
	printf("%d ", root->data);
	preorder(root->lchild);
	preorder(root->rchild);
}
void inorder(node* root){
	if(root==NULL) return;
	inorder(root->lchild);
	printf("%d ", root->data);
	inorder(root->rchild);
}
void postorder(node* root){
	if(root==NULL) return;
	postorder(root->lchild);
	postorder(root->rchild);
	printf("%d ", root->data);
}
int main(){
	int n;
	while(scanf("%d", &n) != EOF){
		for(int i=0;i<n;i++) scanf("%d", &Data[i]);
		node* root = Create(Data, n);
		preorder(root);
		printf("\n");
		inorder(root);
		printf("\n");
		postorder(root);
		printf("\n");
		memset(Data, 0, sizeof(Data));
	}
	return 0;
}

问题 B: 二叉搜索树

题目描述
判断两序列是否为同一二叉搜索树序列
输入
开始一个数n,(1<=n<=20) 表示有n个需要判断,n= 0 的时候输入结束。
接下去一行是一个序列,序列长度小于10,包含(0~9)的数字,没有重复数字,根据这个序列可以构造出一颗二叉搜索树。
接下去的n行有n个序列,每个序列格式跟第一个序列一样,请判断这两个序列是否能组成同一颗二叉搜索树。
输出
如果序列相同则输出YES,否则输出NO
样例输入

6
45021
12045
54120
45021
45012
21054
50412
0

样例输出

NO
NO
YES
NO
NO
NO

思路
思路很简单,对初始序列建立一棵BST,然后把它的先序序列(中序、后序也行)存放在一个叫origin的vector容器里。

然后再对每个序列都建立一棵BST,然后对它进行先序(中序、后序也行)遍历,把遍历得到的序列存在一个nowPre的vector容器里(这里之所以用vector容器是为了对两个序列比较是否相等时,可以直接用“==”进行判断,而数组的话需要用for循环逐一比较),存放好了以后直接用“==”判断origin和nowPre是否相等,如果相等,则说明确实是同一棵BST,输出“YES”,如果不等,则不是同一棵BST,输出“NO”。
代码

#include
#include
#include
#include
#include
#include
using namespace std;
const int maxn = 11;
vector<int> origin;//原始的先序序列 
vector<int> nowPre;//现在的先序序列
int Data[maxn]={0}; 
struct node{
	int data;
	node* lchild;
	node* rchild;
};
node* newNode(int x){
	node* Node = new node;
	Node->data = x;
	Node->lchild = Node->rchild = NULL;
	return Node;
}
void Insert(node* &root, int x){
	if(root==NULL){
		root = newNode(x);
		return;
	}
	if(x==root->data) return;
	else if(x<root->data) Insert(root->lchild, x);
	else Insert(root->rchild, x);
}
node* Create(int data[], int n){
	node* root = NULL;
	for(int i=0;i<n;i++) Insert(root, data[i]);
	return root;
}
void preorderO(node* root){//对原始的BST进行先序遍历 
	if(root==NULL) return;
	origin.push_back(root->data);
	preorderO(root->lchild);
	preorderO(root->rchild);
}
void preorderN(node* root){//对现在新的BST进行先序遍历 
	if(root==NULL) return;
	nowPre.push_back(root->data);
	preorderN(root->lchild);
	preorderN(root->rchild);
}
int main(){
	int n;
	while(scanf("%d", &n) != EOF){
		if(n==0) break;
		char strO[maxn]={0};
		scanf("%s", strO);//读入初始序列
		int len = strlen(strO);
		for(int i=0;i<len;i++) Data[i] = strO[i]-'0';//把每一位存入Data数组里
		node* root = Create(Data, len);
		preorderO(root);//对原始序列进行先序遍历,并存放于origin数组里
		memset(Data, 0, sizeof(Data));
		for(int i=1;i<=n;i++){
			char tmp[maxn]={0};
			scanf("%s", tmp);
			int tmplen = strlen(tmp);
			for(int i=0;i<tmplen;i++) Data[i] = tmp[i]-'0';
			node* tmproot = Create(Data, tmplen);
			preorderN(tmproot);
			if(origin==nowPre) printf("YES\n");//如果先序序列一致,说明是同一棵BST
			else printf("NO\n");//否则不是
			memset(Data, 0, sizeof(Data));//清空两个数组 
			nowPre.clear();
		}
		origin.clear();//最后清空origin数组 
	}
	return 0;
}

小结

二叉查找树这一节的题目还是比较简单的哈,我觉得比较难的地方是BST的删除操作(书上的代码蛮长的,需要好好理解消化),其他的操作都和普通二叉树大体类似,只是在插入操作的时候加入了BST特有的性质,虽然可能用到的函数比较多,但是每个函数还是很简单的,就当是回顾性地练习一下二叉树的链表写法叭~

你可能感兴趣的:(《算法笔记》学习日记,二叉树,链表,算法,数据结构,stl)