树的专题(持续更新)

二叉树

注:
完全二叉树的性质:

  1. 1号位存放的必须是根节点。
  2. 完全二叉树当中的任何一个结点(编号为x),其孩子结点的编号一定是2x,右孩子的编号一定是2x+1
  3. 判断某个结点为叶节点的标志:该结点(下标为root)的左孩子结点(root*2)大于结点总个数n。
  4. 完全二叉树与层次遍历有关系。

1.存储结构

struct node
{
	int data; // 数据域
	node* lchild; // 指向左子树根节点指针
	node* rchild; // 指向右子树根节点指针
};
node* root = NULL; // 建树前根节点指向NULL

2.二叉树的建立

node* Create(int data[], int n) // 二叉树建立
{
	node* root = NULL;
	for(int i=0; i<n; i++)
	{
		insert(root, data[i]); // 结点插入函数
	}
	return root;
}

void insert(node* &root, int x) // 根结点指针一定要使用引用&,否则插入不会成功
{
	if(root == NULL)
	{
		root = newNode(x);
		return ;
	}
	if(...)
	{
		insert(root->lchild, x);
	}
	else 
	{
		insert(root->rchild, x);
	}
}

node* newNode(int v) // 新建结点
{
	node* Node = new node; // 申请空间
	Node->data = v;
	Node->lchild = Node->rchild = NULL;
	return Node;
}

3.二叉树的遍历

1)先序遍历

void preorder(node* root)
{
	if(root == NULL)
		return ;
	printf("%d\n", root->data);
	preorder(root->lchild);
	preorder(root->rchild);
}

2)中序遍历

void inorder(node* root)
{
	if(root == NULL)
		return ;
	inorder(root->lchild);
	printf("%d\n", root->data);
	inorder(root->rchild);
}

3)后序遍历

void postorder(node* root)
{
	if(root == NULL)
		return ;
	postorder(root->lchild);
	postorder(root->rchild);
	printf("%d\n", root->data);
}

4)层次遍历

void layerorder(node* root)
{
	queue<node*> q; // 队列中存结点的地址
	q.push(root); 
	while(!q.empty())
	{
		node* now = q.front();
		q.pop();
		printf("%d\n", now->data);
		if(now->lchild != NULL)
			q.push(now->lchild);
		if(now->rchild != NULL)
			q.push(now->rchild);
	}
}

eg1. PAT A1020 Tree Traversals
在这里插入图片描述

题目大意:

给出二叉树的后序遍历、中序遍历序列,求其层次遍历的序列。

思路分析:

根据后序遍历、中序遍历,递归重建二叉树,再对其进行层次遍历。重建过程中,数值域的值为根节点的值,左右孩子分别递归求得。详细解析请移步另一篇blog: https://blog.csdn.net/qq_41995258/article/details/89006449

代码(AC)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <queue>
#include <algorithm>
using namespace std;

const int maxn = 35;
int level[maxn], in[maxn], post[maxn];

struct node
{
	int data;
	node* lchild;
	node* rchild;
};

node* Create(int post_L, int post_R, int in_L, int in_R)
{
	if(post_L > post_R) // 后序遍历序列长度为0,结束 
	{
		return NULL;
	}
	node* root = new node;  // 新建结点 
	root->data = post[post_R]; // 新结点的值为根节点的值 
	
	int k;
	for(k=in_L; k<=in_R; k++)
	{
		if(in[k] == post[post_R])
		{
			break;
		}
	}
	int num_left = k - in_L; // 左子树结点数量
	
	root->lchild = Create(post_L, post_L+num_left-1, in_L, k-1); // 左子树
	root->rchild = Create(post_L+num_left, post_R-1, k+1, in_R); // 右子树
	
	return root; 
}

void layerorder(node* root) // 层次遍历 
{
	queue<node*> q;
	q.push(root);
	int cnt = 0;
	while(!q.empty())
	{
		node* now = q.front();
		q.pop();
		level[cnt++] = now->data;
		if(now->lchild != NULL)
		{
			q.push(now->lchild);
		}
		if(now->rchild != NULL)
		{
			q.push(now->rchild);
		}
	}
}

void print(int n) // 显示结果 
{
	for(int i=0; i<n; i++)
	{
		printf("%d", level[i]);
		if(i != n)
		{
			printf(" ");
		}
	} 
}

int main()
{
	freopen("input.txt", "r", stdin);
	int n;
	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);
	print(n);
	
	fclose(stdin);
	return 0;
}

eg2. PAT A1102 Invert a Binary Tree
在这里插入图片描述
在这里插入图片描述

题目大意:

建立二叉树,将二叉树对称翻转,求其层次遍历、中序遍历序列。

思路分析:

本题适于使用静态二叉树方法。

根节点: 不是任何结点子结点的结点。

二叉树翻转: 后序遍历过程中,交换结点,实现二叉树反转。

void postorder(int root) // 后序遍历过程中,交换结点,实现二叉树反转 
{
	if(root == -1)
		return ;
	postorder(Node[root].lchild);
	postorder(Node[root].rchild);
	swap(Node[root].lchild, Node[root].rchild); // 交换左右孩子结点 
}

格式化输出: 利用计数器num,记录已输出结点个数,要记得清零!

int num = 0;
void print(int id)
{
	printf("%d", id);
	num++;
	if(num < n)
		printf(" ");
	else 
		printf("\n");
}
代码(AC)
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
using namespace std;

const int maxn = 15;
int hashtable[maxn] = {0};
int num = 0; // 当前已输出结点的个数 
int n;

struct node
{
	int lchild, rchild;
}Node[maxn];

void print(int id)
{
	printf("%d", id);
	num++;
	if(num < n)
		printf(" ");
	else 
		printf("\n");
}

void postorder(int root) // 后序遍历过程中,交换结点,实现二叉树反转 
{
	if(root == -1)
		return ;
	postorder(Node[root].lchild);
	postorder(Node[root].rchild);
	swap(Node[root].lchild, Node[root].rchild); // 交换左右孩子结点 
}

void inorder(int root)
{
	if(root == -1)
		return ;
	inorder(Node[root].lchild);
	print(root);
	inorder(Node[root].rchild);
}

void levelorder(int root)
{
	queue<int> q;
	q.push(root);	
	while(!q.empty()) 
	{
		int now = q.front();
		q.pop();
		print(now);
		if(Node[now].lchild != -1)
			q.push(Node[now].lchild);
		if(Node[now].rchild != -1)
			q.push(Node[now].rchild);
	}
} 

int main()
{
	freopen("input.txt", "r", stdin);
	//int n;
	scanf("%d", &n);
	int lc, rc;
	for(int i=0; i<n; i++)
	{
		getchar();
		scanf("%c %c", &lc, &rc);
		if(lc == '-')
		{
			Node[i].lchild = -1;
		}
		else 
		{
			Node[i].lchild = lc - '0';
			hashtable[lc-'0']++;
		}
		if(rc == '-')
		{
			Node[i].rchild = -1;
		}
		else
		{
			Node[i].rchild = rc - '0';
			hashtable[rc-'0']++;
		}
	}
	int root; // 根节点 
	for(root=0; root<n; root++) // 不是任何结点孩子结点的是根节点 
	{
		if(hashtable[root] == 0)
		{
			break;
		}
	}
	postorder(root); 
	levelorder(root);
	num = 0;
	inorder(root);
	
	fclose(stdin);
	return 0;
}

二叉排序树(BST)

二叉排序树,又称为二叉搜索树二叉排序树二叉查找树

要么是空树,要么左子树数据域 < 根节点数据域 < 右子树数据域。

1.BST建立

node* Create(int data[], int n) // BST建立
{
	node* root = NULL;
	for(int i=0; i<n; i++)
	{
		insert(root, data[i]);
	}
	return root;
}

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* newNode(int v)
{
	node* Node = new node;
	Node->data = v;
	Node->lchild = Node->rchild = NULL;
	return Node;
}

2.BST的性质

对于同一组数值,二叉查找树不唯一,但对二叉查找树进行中序遍历,遍历结果是有序的且唯一。

eg1. 《王道》P55——二叉排序树
在这里插入图片描述
在这里插入图片描述

代码
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;

const int maxn = 105;
int data[maxn], index;
int hashtable[maxn];
int n;

struct node
{
	int data;
	node* lchild;
	node* rchild;
};

void init()
{
	fill(data, data+maxn, -1);
	fill(hashtable, hashtable+maxn, 0); 
	index = 0; // 存储数组的下标 
}

node* newNode(int v)
{
	node* Node = new node;
	Node->data = v;
	Node->lchild = NULL;
	Node->rchild = NULL;
	return Node;
}

void insert(node* &root, int v)
{
	if(root == NULL)
	{
		root = newNode(v);
		return ;
	}
	if(v == root->data)
		return ;
	else if(v < root->data)
		insert(root->lchild, v);
	else 
		insert(root->rchild, v);
}

node* Create(int data[], int m)
{
	node* root = NULL;
	for(int i=0; i<m; 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->lchild != NULL)
		postorder(root->lchild);
	if(root->rchild != NULL)
		postorder(root->rchild);
	printf("%d ", root->data);
}*/

void postorder(node* root) // 不知道为什么,最开始用这个后序遍历总是卡死在程序里,之后换成上边那种写法便可以正常运行。
{						   // 再换回到这种写法又可以正常运行,我佛了 
	if(root == NULL)
		return ;
	postorder(root->lchild);
	postorder(root->rchild);
	printf("%d ", root->data);
}

int main()
{
	freopen("input.txt", "r", stdin);
	while(scanf("%d", &n) != EOF)
	{
		init(); // 初始化 
		int temp;
		for(int i=0; i<n; i++)
		{
			scanf("%d", &temp);
			if(hashtable[temp] == 0) // 未出现过的数字 
			{
				data[index++] = temp;
				hashtable[temp]++;	
			}
		}
		node* root = Create(data, index);
		preorder(root);
		printf("\n");
		inorder(root);
		printf("\n");
		postorder(root);
		printf("\n");
		
		root = NULL; // BST清空 
	}
	fclose(stdin);
	
	return 0;	
} 

eg2.《王道》P58
在这里插入图片描述
在这里插入图片描述

思路分析:

分别建立各串的BST,前序遍历中序遍历都相同的代表是相同的树(一定要用中序遍历,另一个任选)。

吐槽:

这题简直把我给整懵逼了。循环存储对比串的前、中序遍历结果时,总是存不进数。最后没办法只能还存在pre_a[],in_a[]中,把原串的结果再转存到pre_b[],pre_a中。我真的不知道这是为什么????看代码中的调试过程就知道我有多崩溃55555

代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int maxn = 15;
const int maxm = 25;
char a[maxn], b[maxn];
char pre_a[maxn], in_a[maxn];
char pre_b[maxn], in_b[maxn];
int num_in = 0, num_pre = 0; // 已输出的结点个数 
int pre = 0, in = 0;

struct node 
{
	char data;
	node* lchild;
	node* rchild;
};

void init()
{
	fill(a, a+maxn, 0);
	fill(b, b+maxn, 0);
	fill(pre_a, pre_a+maxn, 0);
	fill(in_a, in_a+maxn, 0);
	fill(pre_b, pre_b+maxn, 0);
	fill(in_b, in_b+maxn, 0);
	num_in = 0;
	num_pre = 0;
	in = 0;
	pre = 0;
}

node* newNode(char v)
{
	node* Node = new node;
	Node->data = v;
	Node->lchild = NULL;
	Node->rchild = NULL;
	return Node;
}

void insert(node* &root, char v)
{
	if(root == NULL)
	{
		root = newNode(v); // wtmd!这里写错了(现在是对的) 
		return ;
	}
	if(v == root->data)
	{
		return ;
	}
	else if(v < root->data)
	{
		insert(root->lchild, v);
	}
	else 
	{
		insert(root->rchild, v);
	}
}

node* Create(char 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("%c", root->data);	
	pre_a[num_pre++] = root->data;	
	preorder(root->lchild);
	preorder(root->rchild);
}

void inorder(node* root)
{
	if(root == NULL)
		return ;
	inorder(root->lchild);
	//printf("%c", root->data);
	in_a[num_in++] = root->data;
	inorder(root->rchild);
}

int main()
{
	freopen("input.txt", "r", stdin);
	int n;
	while(scanf("%d", &n)!=EOF && n!=0)
	{
		init(); // 初始化 
		scanf("%s", a);
		node* root = Create(a, strlen(a));
		inorder(root); // 中序遍历
		strcpy(in_b, in_a);
		//cout << endl;
		preorder(root); // 前序遍历 
		strcpy(pre_b, pre_a);
		//cout << endl;
		//cout << "#" << (in_b) << endl;
		//cout << "#" << (pre_b) << endl;
		
		for(int i=0; i<n; i++)
		{
			num_in = 0;
			num_pre = 0;
			scanf("%s", b);
			node* root_new = Create(b, strlen(b));
			inorder(root_new);
			//cout << endl;
			preorder(root_new);
			//cout << endl;
			//cout << "#" << (in_a) << endl;
			//cout << "#" << (pre_a) << endl;
			if(strcmp(in_a, in_b)==0 && strcmp(pre_a, pre_b)==0)
			{
				printf("YES\n");
			}
			else 
			{
				printf("NO\n");
			}
			
			root_new = NULL;
		}	
		root = NULL;
	}
	
	
	fclose(stdin);
	return 0;
}

哈夫曼树

每个n个结点和它们的权值,以它们为叶子结点,从根结点到该结点的长度*权值之和,即二叉树的带权路径长度最小。

哈夫曼树的实现,可以使用“小顶堆”的优先队列。每次将最小的两个结点求和,再加入到优先队列中,直至队列中只剩下一个结点,该结点就是根节点。

带权路径长度 = 除根节点外其余结点的权值之和

eg1.《王道》——P49
在这里插入图片描述

代码
#include <iostream>
#include <cstdio>
#include <queue>
#include <vector>
#include <algorithm>
using namespace std;

priority_queue< int, vector<int>, greater<int> > q;

void pq_clear()
{
	while(!q.empty())
	{
		q.pop();
	}
}

int main()
{
	int n;
	while(scanf("%d", &n) != EOF)
	{
		pq_clear(); // 清空优先队列 
		for(int i=0; i<n; i++)
		{
			int temp;
			scanf("%d", &temp);
			q.push(temp);	
		}
		int ans = 0; 		
		while(q.size() > 1)
		{
			int x = q.top();
			q.pop();
			int y = q.top();
			q.pop();
			q.push(x+y);
			ans += (x+y);	
		}
		printf("%d\n", ans);
	}	
	
	return 0;
} 
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值