二叉树基础专题——洛谷二叉树题单

【数据结构1-2】二叉树

一、P4715 【深基16.例1】淘汰赛

题目大意:能力值高的国家和能力值低的国家踢比赛时高者获胜。1 号国家和 2 号国家踢一场比赛,胜者晋级。3 号国家和 4 号国家也踢一场,胜者晋级……晋级后的国家用相同的方法继续完成赛程,直到决出冠军。给出各个国家的能力值,请问亚军是哪个国家?
思路:如果不用二叉树将非常简单,但还是要用这个知识点。首先能力值可以看做二叉树的最下面的节点,倒数第二层每个节点的值都是子节点值最大的那个。倒数第三层的节点也是两个子节点能力值最大的值,以此类推,倒着用层次建立起二叉树。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
int a[10]; //2的几次方
int tree[500]; //数组形式的二叉树
void build(int level) //按照层次建立
{
	if (level == 0)
		return;
	for (int i = a[level - 1]; i < a[level]; i++)
	{
		tree[i] = max(tree[2 * i], tree[2 * i + 1]);
	}
	build(level - 1);
}
int main()
{
	 a[0] = 1;
	 for (int i = 1; i <= 8; i++)
		 a[i] = a[i - 1] * 2;
	 int n;
	 cin >> n;
	 for (int i = a[n]; i < a[n + 1]; i++)
		 cin >> tree[i];
	 build(n);
	 int s=min(tree[2], tree[3]); //找到亚军的能力值
	 for (int i = a[n]; i < a[n + 1]; i++) //扫描得到序号
	 {
		 if (tree[i] == s)
		 {
			 cout << i - (a[n] - 1) << endl;
			 return 0;
		 }
	 }
}

二、P4913 【深基16.例3】二叉树深度

题目大意:给出每个节点的两个儿子节点,建立一棵二叉树(根节点为 1),如果是叶子节点,则输入0 0。建好树后希望知道这棵二叉树的深度。二叉树的深度是指从根节点到叶子结点时,最多经过了几层。最多有 10^6个结点。
思路:简单的建树,dfs即可

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
struct node {
	int l, r;
}a[1000010];
int step_max = 0;
void dfs(int root, int step)//从哪个节点开始深度搜索
{
	if (root ==0)
		return ;
	step_max = max(step_max, step);
	dfs(a[root].l, step + 1);
	dfs(a[root].r, step + 1);
}
int main()
{
	int n;
	cin >> n;
	for (int i = 1; i <= n; i++)
		cin >> a[i].l >> a[i].r;
	dfs(1, 1);
	cout << step_max << endl;
}

三、P1827 [USACO3.4]美国血统

题目大意:第一行: 树的中序遍历,第二行: 同样的树的前序遍历
输出树的后序遍历
思路 : 递归切割字符串即可

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
//前序遍历:根左右
//中序遍历:左根右
//后序遍历:左右根
string pre, inor;
void work(string pre, string inor)//前序,中序
{
	if (pre.empty())return;
	char root = pre[0];
	int k = inor.find(root);
	pre.erase(pre.begin());
	string left_pre = pre.substr(0, k);
	string right_pre = pre.substr(k);
	string left_inor = inor.substr(0, k);
	string right_inor = inor.substr(k + 1);
	work(left_pre, left_inor);
	work(right_pre, right_inor);
	cout << root;
}
int main()
{
	cin >> inor >> pre;
	work(pre, inor);
	return 0;
}

四、P5076 【深基16.例7】普通二叉树

题意:一系列的增添改查询操作,个人建议用vector维护一个队列,保证顺序为从大到小。

lower_bound(start,last,n)  :返回第一个大于等于n的地址
upper_bound(start, last, n) :返回第一个大于n的地址
#include<iostream>
#include<cstdio>
#include<algorithm>
#include <vector>
using namespace std;
int main()
{
	int n, option, operand;
	cin >> n;
	vector< int> v;
	for (int i = 1; i <= n; i++)
	{
		cin >> option >> operand;//选项,操作数
		if (option == 1)
		{
			auto it= lower_bound(v.begin(), v.end(), operand);
			cout << it - v.begin() + 1 << endl;
		}
		if (option == 2)
		{
			cout << v[operand - 1] << endl;
		}
		if (option == 3)
		{
			auto it = lower_bound(v.begin(), v.end(), operand);
			cout << *(it - 1) << endl;
		}
		if (option == 4)
		{
			auto it = upper_bound(v.begin(), v.end(), operand);
			if (it != v.end()) cout << *it << endl;
			else cout << "2147483647" << endl;
		}
		if (option == 5)
		{
			if (v.empty())v.push_back(operand);
			else 
			{
				auto it = upper_bound(v.begin(), v.end(), operand);
				v.insert(it, operand);
			}
		}
	}
}

五、P1364 医院设置

题目大意,第一行一个整数 n,表示树的结点数。接下来的 n行每行描述了一个结点的状况,包含三个整数 w,u,v,其中 w为居民人口数,u为左链接(为 0表示无链接),v为右链接(为 0 表示无链接)。现在要求在某个结点上建立一个医院,使所有居民所走的路程之和为最小,输出最小距离和。
思路:用Floyed算法跑一遍两个节点的最小距离然后暴力就可以了。

Floyed算法描述

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define MAX 100000007
using namespace std;
int people[101],dis[101][101];
int main()
{
	int n, l, r;
	cin >> n;
	memset(dis, MAX, sizeof(dis));
	for (int i = 1; i <= n; i++)
	{
		dis[i][i] = 0;
		cin >> people[i] >> l >> r;
		if (l)dis[i][l] = dis[l][i] = 1;
		if (r)dis[i][r] = dis[r][i] = 1;
	}
	//Floyed算法
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			for (int k = 1; k <= n; k++)
			{
				if (j != k && dis[j][k] > dis[j][i] + dis[i][k])
					dis[j][k] = dis[j][i] + dis[i][k];
			}
	int end_min = MAX;
	for (int i = 1; i <= n; i++)
	{
		int sum = 0;
		for (int j = 1; j <= n; j++)
		{
			if (i != j && dis[i][j] != -1) sum += people[j] * dis[i][j];
		}
		end_min = min(end_min, sum);
	}
	cout << end_min << endl;
}

六、P1229 遍历问题

题目大意:第一行表示该二叉树的前序遍历结果s1,第二行表示该二叉树的后序遍历结果s2。求可能的中序遍历序列的总数
思路:只有一个儿子的节点 ,才会在知道前序后序的情况下有不同的中序遍历,所以将题目转化成找只有一个儿子的节点个数。(前序中出现AB,后序中出现BA,则这个节点只有一个儿子)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string.h>
using namespace std;
int main()
{
	char str1[1000], str2[1000];
	//先序,后序
	cin >> str1 >> str2;
	int ans = 1;
	for (int i = 0; i < strlen(str1)-1; i++)
	{
		for (int j = 0; j < strlen(str2); j++)
		{
			if (str1[i] == str2[j] && str1[i + 1] == str2[j - 1])
				ans*=2;
		}
	}
	cout << ans << endl;
}

七、P1305 新二叉树

题目大意:后面 n 行,每一个字母为节点,后两个字母分别为其左右儿子。输出其前序序列。

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
struct node {
	char lc, lr;//左右儿子
}l[200];
void bfs(char x)
{
	if (x == '*')
		return;
	cout << x;
	bfs(l[x].lc);
	bfs(l[x].lr);
}
int main()
{
	int n;
	cin >> n;
	char root,h;
	cin >> root;
	cin >> l[root].lc;
	cin >> l[root].lr;
	for (int i = 2; i <= n; i++)
	{
		cin >> h;
		cin>>l[h].lc >> l[h].lr;
	}
	bfs(root);
}

八、P1030 求先序排列

给出一棵二叉树的中序与后序排列。求出它的先序排列。(约定树结点用不同的大写字母表示,长度≤8)。
此题和第三题一样

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
void work(string inor,string after)
{
	if (inor.empty())return;
	char root= after[after.size() - 1];
	cout << root;
	int k = inor.find(root);
	after.erase(after.end()-1);
	string left_after = after.substr(0, k);
	string right_after = after.substr(k);
	string left_inor = inor.substr(0, k);
	string right_inor = inor.substr(k + 1);
	work( left_inor, left_after);
	work(right_inor, right_after);
}
int main()
{
	string inor, after;
	cin >> inor >> after;
	work(inor, after);
}

九、P3884 [JLOI2009]二叉树问题

题目大意:给出一堆节点,求出树的深度,宽度,以及u,v两个节点之间的距离。
思路:第一个求深度可以在输入是自行更新值,而求宽度也简单,扫描一面求除最大值,求u,v两节点的距离重点在于求出他们最近的公共祖父,那就让两个节点一直向上,遍历即可。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
struct node
{
	int f;
	int l, r;
	int deep,flag;//深度,记录走过没
}a[10001];
int sum[101];
int found(int x, int y)
{
	a[x].flag = 1;
	while (a[x].f != 0)
	{
		x = a[x].f;
		a[x].flag = 1;
	}
	while (a[y].flag !=1 )
	{  //遍历至x节点已走过的节点,找到最近公共祖先
		y = a[y].f;
	}
	return y;
}
int main()
{
	int n,x,y,deep_max=0;
	cin >> n;
	a[1].f =a[1].deep= 0;
	for (int i = 1; i < n; i++)
	{
		cin >> x >> y;
		if (a[x].l == 0)
		{
			a[x].l = y;
		}
		else
			a[x].r = y;
		a[y].deep = a[x].deep+1;
		a[y].f = x;
		deep_max = max(deep_max, a[y].deep);
	}
	cin >> x >> y;
	int ancestor = found(x, y),num=0;
	while (x != ancestor)
	{
		x = a[x].f;
		num++;
	}
	num *= 2;
	while (y != ancestor)
	{
		y= a[y].f;
		num++;
	}
	for (int i = 1; i <= n; i++)      //把每一个深度有多少个节点记录
		sum[a[i].deep]++;
	sort(sum + 1, sum + 1 + 100);
	cout << ++deep_max << endl << sum[100] << endl << num; //sum[100]是最大的宽度节点个数
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值