C++二叉树的一些基础

C++二叉树的一些基础

参考

树节点的结构体

struct TreeNode {
    int val;
    TreeNode *left;
    TreeNode *right;
    TreeNode() : val(0), left(nullptr), right(nullptr) {}//无参构造
    TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}// 一个带有一个参数 x 的构造函数。它将 val 初始化为提供的值 x,并将 left 和 right 指针都设置为 nullptr
    TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
};

单独创建树节

#include <iostream>

struct TreeNode {
    int val;
    TreeNode* left;
    TreeNode* right;
    TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};

int main() {
	
    TreeNode* root = new TreeNode(1);
    root->left = new TreeNode(2);
    root->right = new TreeNode(3);
    root->right->left = new TreeNode(4);
    root->right->right = new TreeNode(5);

    return 0;
}

使用数组层次遍历建立二叉树

对于树的空节点,数组应该设置为NULL

vector<int>v = { 3,9,20,NULL,NULL,15,7 };// 在 C++中,NULL 被定义为整形常量 0
TreeNode* buildTree(vector<int>& nums)//层次遍历建立二叉树
{
 if (nums.empty()) return nullptr;
 TreeNode* root = new TreeNode(nums.front());
 vector<TreeNode*> q = { root };
 int i = 1;
 while (!q.empty() && i < nums.size())
 {
	 TreeNode* cur = q.front();
	 //q.assign(q.begin() + 1, q.end());
	 q.erase(q.begin());
	 if (i < nums.size())
	 {
		 cur->left = new TreeNode(nums[i++]);//层次遍历下,一个根节点的子节点按数组读取的顺序,左在前,右在后
		 q.push_back(cur->left);//将cur->left作为根节点添加到q中下次使用
	 }
	 if (i < nums.size())
	 {
		 cur->right = new TreeNode(nums[i++]);
		 q.push_back(cur->right);
	 }
 }
 return root;
}

层次遍历打印输出二叉树

广度优先

void levelOrderPrint(TreeNode* root)//层次遍历打印输出树,广度优先搜索
{
 if (!root) return;//空树
 vector<TreeNode*> q = { root };
 while (!q.empty())
 {
	 int size = q.size();
	 for (int i = 0; i < size; i++)
	 {
		 TreeNode* cur = q.front();
		 //q.assign(q.begin() + 1, q.end());
		 //q.front()与q.begin()的区别,q.front()返回头部的元素,q.begin()返回迭代器
		 q.erase(q.begin());
		 cout << cur->val << " ";
		 if (cur->left)
			 q.push_back(cur->left);
		 if (cur->right)
			 q.push_back(cur->right);
	 }
	 cout << endl;
 }
}

二叉树的深度优先遍历(Depth-First Search, DFS)包括三种基本的方式:先序遍历(Pre-order Traversal)、中序遍历(In-order Traversal)和后序遍历(Post-order Traversal)。

先序,中序,后序遍历输出二叉树

void preOrderTraversal(TreeNode* root) {
    if (root == nullptr) {
	        return;
    }
    cout << root->val << " ";
    preOrderTraversal(root->left);
    preOrderTraversal(root->right);
}

void inOrderTraversal(TreeNode* root) {
    if (root == nullptr) {
	        return;
    }
    inOrderTraversal(root->left);
    cout << root->val << " ";
    inOrderTraversal(root->right);
}

void postOrderTraversal(TreeNode* root) {
    if (root == nullptr) {
	        return;
    }
    postOrderTraversal(root->left);
    postOrderTraversal(root->right);
    cout << root->val << " ";
}

计算二叉树最大深度

递归,深度优先

int maxDepth(TreeNode* root) 
{
    if (root == nullptr) return 0;
	int leftDepth = maxDepth(root->left);
	int rightDepth = maxDepth(root->right);
	return max(leftDepth, rightDepth) + 1;
}

计算二叉树中指定层的节点数量

使用递归,深度优先

int countNodesAtLevel(TreeNode* root, int level)
{
    if(root == nullptr) return 0;
    if(level == 0) return 1;
    return countNodesAtLevel(root->left, level - 1) + countNodesAtLevel(root->right, level - 1);
}

判断两棵树 的叶子节点是否相似(值 和 顺序)

872.叶子相似的树
递归,深度优先

void dfs(vector<int> &v, TreeNode* root)
{
 if (root->left == nullptr && root->right == nullptr)
 {
	 v.push_back(root->val);
 }
 else
 {
	 if (root->left != nullptr)
	 {
		 dfs(v, root->left);
	 }
	 if (root->right != nullptr)
	 {
		 dfs(v, root->right);
	 }
 }
}
//什么时候递归结束呢,当节点左右指针都是空,即遇到叶子节点,递归结束
bool leafSimilar(TreeNode* root1, TreeNode* root2)//872.叶子相似的树
{
 vector<int> v1;
 dfs(v1, root1);
 vector<int> v2;
 dfs(v2, root2);
 return v1 == v2;//vector容器支持比较操作
}

改进

void dfs(vector<int> &v, TreeNode* root)
{
 if(root == nullptr) return;//如果返回类型是void,直接return;
 if (root->left == nullptr && root->right == nullptr)
 {
	 v.push_back(root->val);
 }
 dfs(v, root->left);
 dfs(v, root->right);
}

1448.统计二叉树中好节点的数目

给你一棵根为 root 的二叉树,请你返回二叉树中好节点的数目。
「好节点」X 定义为:从根到该节点 X 所经过的节点中,没有任何节点的值大于 X 的值。

示例 1:
输入:root = [3,1,4,3,null,1,5]
输出:4
解释:图中蓝色节点为好节点。
根节点 (3) 永远是个好节点。
节点 4 -> (3,4) 是路径中的最大值。
节点 5 -> (3,4,5) 是路径中的最大值。
节点 3 -> (3,1,3) 是路径中的最大值。

示例 2:
输入:root = [3,3,null,4,2]
输出:3
解释:节点 2 -> (3, 3, 2) 不是好节点,因为 “3” 比它大。

示例 3:
输入:root = [1]
输出:1
解释:根节点是好节点。

int dfs_1448(TreeNode* root, int path_max)
{
 if (root == nullptr) return 0;
 int res = 0;
 if (root->val >= path_max)
 {
	 res++;
	 path_max = root->val;
 }
 res += dfs_1448(root->left, path_max) + dfs_1448(root->right, path_max);
 return res;
}

int goodNodes(TreeNode* root)//1448.统计二叉树中好节点的数目
{
 return dfs_1448(root, INT_MIN);//INT_MIN是最小int
}

void test1448()
{
 vector<int>v1 = { 3,1,4,3,NULL,1,5 };
 TreeNode* tree1 = buildTree(v1);
 int num = goodNodes(tree1);
 cout << "num " << num << endl;
 cout << INT_MIN << endl;
}

错误版本

int dfs_1448(TreeNode* root, int path_max)
{
 if (root == nullptr) return 0;
 int res = 0;
 if (root->val >= path_max)
 {
	 res++;
	 path_max = root->val;
 }
 if(root->left != nullptr )
 {
 	int res1 = dfs_1448(root->left,max);
 }
 if(root->right != nullptr)
 {
 	int res2 = dfs_1448(root->right,max);
 }
 res = res + res1 + res2;
 return res;
}
//错误原因:res1 和 res2 在递归调用 dfs 之后才会返回,你不能在当前函数的这一层直接使用它们

所以以后先递归调用,然后判断比较好
那么改进上一道题872.叶子相似的树

437.路径总和III

给定一个二叉树的根节点 root ,和一个整数 targetSum ,求该二叉树里节点值之和等于 targetSum 的 路径 的数目。
可以从任意节点开始计数,只要向下就行

这题用两次循环
一次从任意结点开始
一次向下遍历求和

int dfs_437(TreeNode* root, int targetSum)
{
 if (root == nullptr) return 0;
 int num = 0;

 if (root->val == targetSum)
 {
	 num++;
	 //path = 0;
 }
 num += dfs_437(root->left, targetSum - root->val);
 num += dfs_437(root->right, targetSum - root->val);
 
 //递归内部的数不会被记录,所以每次递归都要想办法把变化的数传下去
 //这里需要传递的变量有两个,num,targetSum - root->val
 //如果计算节点值累加,累加的数无法传递,只能用目标值递减的办法targetSum - root->val
 
 return num;
}
int pathSum(TreeNode* root, int targetSum)//437.路径总和III
{
 if (root == nullptr) return 0;
 int num = dfs_437(root, targetSum);
 num += pathSum(root->left, targetSum);//相当于第一层循环
 num += pathSum(root->right, targetSum);
 return num;
}
void test437()
{
 vector<int>v1 = { 10,5,-3,3,2,NULL ,11,3,-2,NULL,1 };
 TreeNode* tree1 = buildTree(v1);
 int num = 8;
 int path_num = pathSum(tree1, num);
 cout << "path_num " << path_num << endl;
}

100.相同的树

判断两棵树是否相同

bool isSameTree(TreeNode* p, TreeNode* q)//100.相同的树
{
	
	if (p == nullptr && q == nullptr) return true;
	else if (p == nullptr || q == nullptr)
	{
		return false;
	}
	else if (p->val != q->val)
	{
		return false;
	}
	else 
	{
		return isSameTree(p->left, q->left) && isSameTree(p->right, q->right);
	}

}

226.翻转二叉树

TreeNode* invertTree(TreeNode* root) //226.翻转二叉树
{
	if (root == nullptr) return root;
	TreeNode* temp;
	temp = root->left;
	root->left = root->right;
	root->right = temp;
	invertTree(root->left);
	invertTree(root->right);
	return root;
}

101.对称二叉树

bool dfs_101(TreeNode* node1, TreeNode* node2)
{
	if (node1 == nullptr && node2 == nullptr) return true;
	else if (node1 == nullptr || node2 == nullptr) return false;
	else
	{
		return node1->val == node2->val&& dfs_101(node1->left, node2->right)&& dfs_101(node1->right, node2->left);
	}

}
bool isSymmetric(TreeNode* root)//101.对称二叉树
{
	
	TreeNode* left = root->left;
	TreeNode* right = root->right;
	return dfs_101(left, right);
}

112.路经总和

bool hasPathSum(TreeNode* root, int targetSum)//112.路经总和
{
	if (root == nullptr) return false;
	else if (root->left == nullptr && root->right == nullptr)//找到叶子节点
	{
		return root->val == targetSum;
	}
	else
	{
		return hasPathSum(root->left, targetSum - root->val) || hasPathSum(root->right, targetSum - root->val);
	}
}

129.求根节点到叶节点数字之和

给你一个二叉树的根节点 root ,树中每个节点都存放有一个 0 到 9 之间的数字。
每条从根节点到叶节点的路径都代表一个数字:

例如,从根节点到叶节点的路径 1 -> 2 -> 3 表示数字 123 。
计算从根节点到叶节点生成的 所有数字之和 。
叶节点 是指没有子节点的节点。

示例 1:

输入:root = [1,2,3]
输出:25
解释:
从根到叶子节点路径 1->2 代表数字 12
从根到叶子节点路径 1->3 代表数字 13
因此,数字总和 = 12 + 13 = 25

int dfs_129(TreeNode* root,int prevSum)
{
	 
	if (root == nullptr) return 0;
	int sum = root->val + prevSum * 10;
	if (root->left == nullptr && root->right == nullptr)
	{
		return sum;
	}
	else
	{
		return dfs_129(root->left,sum) + dfs_129(root->right,sum);
	}
}
int sumNumbers(TreeNode* root) //129.求根节点到叶节点数字之和
{
	return dfs_129(root, 0);
}

有一个思考,在dfs中的return sum;之后是否就结束了整个递归
答案是没有,只是结束了这层递归,把结果返回给了上层递归,整个递归在遍历完整个树才结束

/////////////////////////////////////层次遍历///////////////////////////////////

199.二叉树的右视图

vector<int> rightSideView(TreeNode* root) //199.二叉树的右视图
{
	vector<int> v;
	if (root == nullptr) return {};	
	vector<TreeNode*> q = { root };
	while (!q.empty())
	{
		int size = q.size();
		for (int i = 0; i < size; i++)
		{
			TreeNode* cur = q.front();
			cout << cur->val << " ";
			q.erase(q.begin());

			if (i == size - 1)
				v.push_back(cur->val);//这一层末尾节点值就是需要的
			
			if (cur->left)
				q.push_back(cur->left);//将下一层的节点加入队列
				
			if (cur->right)
				q.push_back(cur->right);
		}
		cout << endl;
	}
	return v;
}

637.二叉树的层平均值

vector<double> averageOfLevels(TreeNode* root) //637.二叉树的层平均值
{
	vector<double> v;
	if (root == nullptr) return {};
	vector<TreeNode*> q = { root };
	while (!q.empty())
	{
		int size = q.size();
		double sum = 0;
		for (int i = 0; i < size; i++)
		{
			TreeNode* cur = q.front();
			cout << cur->val << " ";
			q.erase(q.begin());
			sum += cur->val;

			if (cur->left)
				q.push_back(cur->left);//将下一层的节点加入队列

			if (cur->right)
				q.push_back(cur->right);
		}
		cout << endl;
		v.push_back(sum / size);
	}
	return v;
}

102.二叉树的层次遍历

vector<vector<int>> levelOrder(TreeNode* root) //102.二叉树的层次遍历
{
	
	vector<vector<int>> v;
	if (root == nullptr) return {};
	vector<TreeNode*> q = { root };
	while (!q.empty())
	{	
		vector<int> temp;
		
		cout <<"size " << size << endl;
		cout <<"q.size() " << q.size() << endl;
		int size = q.size();
		for (int i = 0; i < size; i++)
		//for (int i = 0; i < q.size(); i++)
		{

			TreeNode* cur = q.front();
			cout << cur->val << " ";
			q.erase(q.begin());	
			temp.push_back(cur->val);

			if (cur->left)
				q.push_back(cur->left);

			if (cur->right)
				q.push_back(cur->right);
		}
		cout << endl;
		v.push_back(temp);
	}
	return v;
}

找了半天的一个BUG
int size = q.size();
for (int i = 0; i < size; i++) {}

for (int i = 0; i < q.size(); i++){}有什么区别
第二种是错误的,因为如果循环体内q改变,那么每次循环,条件都会变
第一种size不会改变

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值