数据结构期末拯救计划:二叉树(重点)

二叉树的本质就是链表,只不过它的next节点换成了一左一右两个叶子节点,首先为了应付选择题,需要了解二叉树的性质:

1.二叉树中度为0,1,2,的结点分别有a,b,c个,则a=c+1

2.树的结点数为树的总度数+1

3.二叉树的第i层至多有2的i-1次方个结点(层数从1开始算)

4.m叉树第i层至多有m的i-1次方个结点(层数从1开始算)

5.高度为h的二叉树至多有2的h次方-1个结点(满二叉树)

6.高度为h的M叉树至多有m的h次方-1除m-1个结点(等比数列求和)

7.具有n个结点的完全二叉树的高度为⌈log2​(n+1)⌉或⌊log2​n⌋+1

8.高为h的满二叉树有2的h次方-1个结点

9.高为h的完全二叉树至多有2的h次方-1个节点(满二叉树),至少有2的h-1次方个节点(最后一层一个节点)

10.第 i 个结点所在层次为⌈log2​(n+1)⌉或⌊log2​n⌋+1

11.设完全二叉树度为0,1,2的节点有a,b,c个,则b等于0或1,a+c一定是奇数

12.完全二叉树有奇数个节点(2k-1),则a=k,b=0,c=k-1,如果有偶数个节点(2k)则a=k,b=1,c=k-1

再来看看二叉树的创建,第一种是根据前序序列创建(不唯一,但用到了),另一种是根据两种遍历序列创建,首先给出前序遍历的代码‘

#include<iostream>
using namespace std;
typedef struct tree
{
	char data;
	tree* left;
	tree* right;
}tree;
tree* fcreat(string s,int &i)
{
	if (s[i] == '#')
	{
		i++;
		return nullptr;
	}
	tree* point = new tree;
	point->data = s[i];
	point->left = point->right = nullptr;
	i++;
	point->left = fcreat(s, i);
	point->right = fcreat(s, i);
	return point;
}

接着是三种序列创建,注意形参以区分

#include<iostream>
#include<vector>
using namespace std;
typedef struct tree
{
	int data;
	tree* left;
	tree* right;
}tree;
tree* set1(vector<char>f, vector<char>i, int left1, int right1, int left2, int right2)
{
	if (left1 > right1||left2>right2)
	{
		return nullptr;
	}
	tree* root = new tree;
	root->data = f[left1];
	int mid = left2;
	while (i[mid] != f[left1])
	{
		mid++;
	}
	int leftsize = mid - left2 + 1;
	root->left = set1(f, i, left1 + 1, left1 + leftsize - 1, left2, mid - 1);
	root->right = set1(f, i, left1 + leftsize, right1, mid + 1, right2);
}
tree* set2(vector<char>f, vector<char>b, int left1, int right1, int left2, int right2)
{
	if (left1 > right1 || left2 > right2)
	{
		return nullptr;
	}
	tree* root = new tree;
	root->data = f[left1];
	int mid = left2;
	while (b[mid] != f[left1+1])
	{
		mid++;
	}
	int leftsize = mid - left2 + 1;
	root->left = set1(f, b, left1 + 1, left1 + leftsize, left2, mid);
	root->right = set1(f, b, left1 + leftsize+1, right1, mid + 1, right2-1);
}
tree* set3(vector<char>i, vector<char>b, int left1, int right1, int left2, int right2)
{
	if (left1 > right1 || left2 > right2)
	{
		return nullptr;
	}
	tree* root = new tree;
	root->data = b[right2];
	int mid = left1;
	while (b[mid] != root->data)
	{
		mid++;
	}
	int leftsize = mid - left2 + 1;
	root->left = set1(i, b, left1, mid-1, left2,left2+leftsize-1);
	root->right = set1(i, b, mid+1, right1, left2+leftsize, right2 - 1);
}
}

遍历分为七种,前序中序后序的递归,非递归以及层序遍历

以下为递归状态下的前序,中序以及后序遍历

#include<iostream>
#include<vector>
using namespace std;
typedef struct tree
{
	int data;
	tree* left;
	tree* right;
}tree;
void set(tree* root,vector<int>&str)
{
	if (root == nullptr)
	{
		return;
	}
	str.push_back(root->data);
	set(root->left, str);
	set(root->right, str);

}
void set1(tree* root, vector<int>& str)
{
	if (root == nullptr)
	{
		return;
	}
	set1(root->left, str);
	str.push_back(root->data);
	
	set1(root->right, str);

}
void set2(tree* root, vector<int>& str)
{
	if (root == nullptr)
	{
		return;
	}
	
	set2(root->left, str);
	set2(root->right, str);
	str.push_back(root->data);
}

以下为非递归的中序遍历,前序遍历与后序遍历

中序遍历思路:中序遍历的基本逻辑是左中右,所以先将左树节点入栈,再出栈并加入vector(此时代表中间节点),然后将节点移动到右侧

前序遍历思路,前序遍历的基本逻辑是中左右,所以先将根节点入栈并入vector,再将其移向左侧,再出栈并将元素移动到右侧

后序遍历思路:后序遍历的基本逻辑是左右中,实现比较复杂,需要一前驱节点来记录之前访问的路径,先将左侧节点入栈,再出栈并判断是否右子树为空或者已经访问,如果是正确的需要出栈加入vector并更新前驱节点,否则向右侧移动

#include<iostream>
#include<vector>
#include<stack>
using namespace std;
typedef struct tree
{
	int data;
	tree* left;
	tree* right;
}tree;
void set(tree* root, vector<int> &str)
{
	stack<tree*>s;
	tree* cur = root;
	while (cur != nullptr || !s.empty())
	{
		while (cur != nullptr)
		{
			s.push(cur);
			cur = cur->left;
		}
		cur = s.top();
		s.pop();
		str.push_back(cur->data);
		cur = cur->right;
	}
}
void set1(tree* root, vector<int>& str)
{
	stack<tree*>s;
	tree* cur = root;
	while (cur || !s.empty())
	{

		while (cur)
		{
			str.push_back(cur->data);
			s.push(cur);
			cur = cur->left;
		}
		tree* point1 = s.top();
		s.pop();
		cur = point1->right;
	}
}
void set2(tree* root, vector<int>& str)
{
	stack<tree*>st;
	tree* prev = nullptr;
	tree* cur = root;
	while (cur || !st.empty())
	{
		while (cur)
		{
			st.push(cur);
			cur = cur->left;
		}
		tree* top = st.top();
		if (top->right == nullptr || top->right == prev)
		{
			st.pop();
			str.push_back(top->data);
			prev = top;
		}
		else
		{
			cur = top->right;
		}
}

以下为层序遍历代码,层序遍历使用队列作为载体,并先将节点入队列,在队列不为空的时候,首先记录队列的元素数并且为返回数组开新的一层,然后遍历剩余元素(每次队列都是一层),每次出队列并且更新返回数组,如果有左右节点,则入队列准备成为新的一层

#include<iostream>
#include<vector>
#include<stack>
#include<queue>
using namespace std;
typedef struct tree
{
	int data;
	tree* left;
	tree* right;
}tree;
void set(tree* root, vector<vector<int>>&str)
{
	if (root == nullptr)
	{
		return;
	}
	queue<tree*>cur;
	cur.push(root);
	while (!cur.empty())
	{
		int n = cur.size();
		str.push_back(vector<int>());
		for (int i = 0; i < n; i++)
		{
			tree* point = cur.front();
			cur.pop();
			str.back().push_back(point->data);
			if (point->left)
			{
				cur.push(point->left);
			}
			if (point->right)
			{
				cur.push(point->right);
			}
		}
	}
}

二叉树的共同祖先问题

【问题描述】假设二叉树采用二叉链表方式存储,root指向根结点,p所指结点和q所指结点为二叉树中的两个不同结点,且互不成为根到该结点的路径上的点,编程求解距离它们最近的共同祖先。

【输入形式】二叉树的前序和中序遍历序列,用以创建该二叉树的链式存储结构;以及二叉树的两个结点数据 x 和 y

【输出形式】结点数据值为 x 和结点数据值为 y 的最近的共同祖先,若没有共同祖先则输出NULL,请注意一个结点本身不能成为另一个结点的共同祖先。

【样例输入】

GABDCEF

BDAEFCG

DF

【样例输出】

A

关键就是寻找公共祖先的函数,这是一个递归函数,当root为两节点任意一节点或者为空时返回root,然后递归调用左右节点,此时,这两个节点有两种情况,都在左,都在右或者一左一右,第一种返回左,第二种返回右,第三种返回根节点

#include<iostream>
#include<vector>
#include<stack>
#include<queue>
using namespace std;
typedef struct tree
{
	int data;
	tree* left;
	tree* right;
}tree;
tree* find(tree* root, int data1, int data2)
{
	if (!root || root->data == data1 || root->data == data2)
	{
		return root;
	}
	tree* left1 = find(root->left, data1, data2);
	tree* right1 = find(root->right, data1, data2);
	if (!left1)
	{
		return right1;
	}
	if (!right1)
	{
		return left1;
	}
	return root;
}

计算叶子节点数目与交换左右子树

【问题描述】二叉树按照二叉链表的方式存储。编写程序,计算二叉树中叶子结点的数目并输出;编写程序,将二叉树的每个结点的左、右子树进行交换,请注意不是只交换结点的data值,而是左、右孩子指针指向的交换,最后输出交换后的二叉树的后序遍历序列。

【输入形式】二叉树的前序遍历序列,空指针的位置输入字符#

【输出形式】叶子结点的数目;左右子树交换后,后序遍历的序列,空子树的位置输出字符#

【样例输入】

ABE##F##CG###

【样例输出】

3

###GC##F##EBA

计算叶子节点的函数

int leave(tree* root)
{
	if (root == nullptr)
	{
		return 0;
	}
	if (root->left == nullptr && root->right == nullptr)
	{
		return 1;
	}
	return leave(root->left) + leave(root->right);
}

交换左右节点函数

#include<iostream>
#include<vector>
#include<stack>
#include<queue>
using namespace std;
typedef struct tree
{
	int data;
	tree* left;
	tree* right;
}tree;
void swap(tree* root)
{
	if (root == nullptr)
	{
		return;
	}
	tree* point = root->left;
	root->left = root->right;
	root->right = point;
	swap(root->left);
	swap(root->right);
}

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值