树与二叉树

本文详细介绍了树的数据结构,重点讨论了二叉树的特性,包括完全二叉树的定义和遍历方法(先序、中序、后序)。此外,文章还涉及了树的直径计算以及查找最近公共祖先和路径的问题,通过示例代码展示了如何在实际编程中解决这些问题。
摘要由CSDN通过智能技术生成

概述

1.树是一种非线性的数据结构,他表现的是一对多,是由n(n>=0)个节点组成的有限集,n=0的树成为空树。

2.有且仅有一个根节点,且根节点无前驱节点。

3.每个节点至多仅有一个父节点和若干个儿子节点,每个子树不相交

4.若一个树有n个节点,那么他有n-1个边(即节点之间的连接部分)

二叉树

正如它的名字名字一样,他的每个节点至多有两个分支(即两个儿子),所以就有了以一般二叉树、完全二叉树和满二叉树的概念。

完全二叉树:除去最后一层节点为满二叉树,且最后一层的结点依次从左到右分布

遍历完全二叉树

由于有了二叉树备辈分的关系和至多有两个分叉的特点,我们一般将两个分叉节点成为左右儿子

树的遍历分为三类:前序、中序、后序、层级遍历

先序遍历

中序遍历

后序遍历

层级遍历:这个相比就不用多说了,就是从树的最顶层依次向下从左向右依次遍历。

例题

给你一棵 n 个节点的完全二叉树,节点的编号为 1 到 n,二叉树的根为1号节点。编号为 i (1≤i≤n) 的节点的左儿子如果存在的话,编号为 i+i;编号为 i (1≤i≤n) 的节点的右儿子如果存在的话,编号为 i+i+1。

现在请你求出这棵完全二叉树的先序、中序和后序遍历的结果。

#include<iostream>
using namespace std;
int n;
void preorder(int t)//先序
{
	cout << t << " ";
	if (t + t <= n)
		preorder(t + t);
	if (t + t + 1 <= n)
		preorder(t + t + 1);
}
void inorder(int t)//中序
{
	if (t + t <= n)
		inorder(t + t);
	cout << t << " ";
	if (t + t + 1 <= n)
		inorder(t + t + 1);
}
void postorder(int t)//后序
{
	if (t + t <= n)
		postorder(t + t);
	if (t + t + 1 <= n)
		postorder(t + t + 1);
	cout << t << " ";
}
int main()
{
	cin >> n;
	preorder(1);
	cout << endl;
	inorder(1);
	cout << endl;
	postorder(1);
	return 0;
}

树的直径

树的直径是这棵二叉树上最长且中间不重复不相交的一条路径

例题

给你一棵树,让你求出这棵树的直径长度(直径经过的边的数量)。 树以下列方式给出:

  • 输入第一行给出一个数 n,表示一共有 n 个节点;
  • 接下来 n−1 行,每行给出两个数 x,y(x!=y),表示x,y 之间有一条边。
#include<iostream>
#include<cstring>
#include<vector>
using namespace std;
int n, pre[100001], c[100001], l, q[100001], frong = 1, rear = 0, dist[100001];
vector<int>edges[100001];
void dfs(int x)//深度优先搜索
{
	for (auto y : edges[x]) {//从当前节点向四周探索
		if (y != pre[x]) {
			pre[y] = x;
			dist[y] = dist[x] + 1;//多走一条路+1
			dfs(y);
		}
	}
}
int main()
{
	int n;
	cin >> n;
	for (int i = 1; i < n; i++) {
		int x, y;
		cin >> x >> y;
		edges[x].push_back(y);//与x相邻的数y
		edges[y].push_back(x);//与y相邻的数x
	}
	memset(dist, 0, (sizeof(dist)));//重置数组
	memset(pre, 0, sizeof(pre));
	pre[1] = -2;//这个等于几都行反正特殊点
	dfs(1);//从根节点开始,直径肯定经过了这个节点
	int idx = 0, v = 0;
	for (int i = 1; i <= n; i++)
		if (dist[i] > v)
			v = dist[i], idx = i;
	memset(dist, 0, (sizeof(dist)));//重置数组
	memset(pre, 0, sizeof(pre));
	pre[idx] = -2;
	dfs(idx);//距离根节点最远那个节点开始探索
	v = 0;
	for (int i = 1; i <= n; i++)//挨个节点找
		v = max(v, dist[i]);
	cout << v;
	return 0;
}

树的共同祖先(LAC)

字面意思,无需多言

例题:

describe:

给你一棵 n 个节点的二叉树,节点的编号为 1到 n,二叉树的根为 1号节点。

读入 u,v,请求出 u 号节点和 v 号节点的最近公共祖先(Lowest Common Ancestor)。

如果 x 号节点既是 u 号节点的祖先也是 v 号节点的祖先,则称 x 号节点是 u 号节点和 v 号节点的公共祖先。

如果 x 号节点是 u 号节点和 v 号节点的所有公共祖先中深度最深的,则称 x 号节点是 u 号节点和 v 号节点的最近公共祖先

input:

第一行一个整数 n 表示节点数。

接下来 n 行,每行两个整数,第一个整数表示 i 号节点的左儿子的编号,第二个整数表示 i 号节点的右儿子的编号,如果某个数字为 0 表示没有对应的子节点。

输入保证是一棵二叉树。

最后一行两个整数 u,v 表示要求最近公共祖先的两个节点的编号。

对于所有数据,保证2≤n≤1000,1≤u,v≤n。

#include<iostream>
using namespace std;
struct node {
	int l, r, fa;//l左儿子、r右儿子、fa表示父亲节点
	int depth;//深度
}arr[10001];
int queue[10000];//队列
int main()
{
	int n, x, y;
	cin >> n;
	for (int i = 1; i <= n; i++)
	{
		cin >> x >> y;
		if (x)
			arr[i].l = x, arr[x].fa = i;
		if (y)
			arr[i].r = y, arr[y].fa = i;
	}
	int front = 1, rear = 1;
	queue[1] = 1; 
	arr[1].depth = 1;
	while (front <= rear) {
		int p = queue[front];
		++front;
		if (arr[p].l) {//若左儿子存在
			queue[++rear] = arr[p].l;
			arr[arr[p].l].depth = arr[p].depth + 1;
		}
		if (arr[p].r) {
			queue[++rear] = arr[p].r;
			arr[arr[p].r].depth = arr[p].depth + 1;
		}
	}
	int z,c;
	cin >> z >> c;
	if (arr[z].depth < arr[c].depth)
		swap(z, c);//达成同一层
	int cut = arr[z].depth - arr[c].depth;
	for (int i = 1; i <= cut; i++) {
		z = arr[z].fa;
	}
	while (z != c)
	{
		z = arr[z].fa;//向上找祖先,直到找到一样的
		c = arr[c].fa;
	}
	cout << z;
	return 0;
}

树上路径

例题

给你一棵树和树上的两个节点 u,v(u!=v), 需要你求出从 u 到v 的路径经过的所有节点。

树以下列方式给出:

  • 输入第一行给出一个数n,表示一共有 n 个节点;
  • 接下来n−1 行,每行给出两个数x,y(x!=y),表示x,y 之间有一条边。

输入最后一行两个数u,v 表示询问。

#include<iostream>
using namespace std;
#include<vector>
int n, pre[100001], c[100001], l, q[100001], front = 1, rear = 0;
vector<int>edges[1000001];
void dfs(int x)//深度优先搜索
 {
	for (auto y : edges[x]) {
		if (y != pre[x]) {
			pre[y] = x;
			dfs(y);
		}
	}
}
void bfs(int u)//宽度优先搜索
{
	q[++rear] = u;
	while (front <= rear) {
		int x = q[front];
		front++;
		for (auto y : edges[x]) {//遍历所有与x相邻的点
			if (y != pre[x]) {
				pre[y] = x;//y从x过来,从y中找到它的上级
				q[++rear] = y;
			}
		}
	}
}
int main()
{
	cin >> n;
	for (int i = 1; i < n; i++) {
		int x, y;
		cin >> x >> y;
		edges[x].push_back(y);//相连的数放在对应数组中
		edges[y].push_back(x);
	}
	int u, v;
	cin >> u >> v;
	pre[u] = -1;//第一个数的位置
	bfs(u);
	l = 0;
	for (int i = v; i != u; i = pre[i])//反着找
		c[++l] = i;
	c[++l] = u;
	for (int i = l; i; --i)
		printf("%d ", c[i]);
	return 0;
}

  • 18
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值