LeetCode:二叉树的修改与构造(5道经典题目)

LeetCode:二叉树的修改与构造(5道经典题目)




本文带来与二叉树的修改与构造有关的经典题目,主要实现是C++。




226. 翻转二叉树

给你一棵二叉树的根节点 root ,翻转这棵二叉树,并返回其根节点。

遍历的过程中去翻转每一个节点的左右孩子就可以达到整体翻转的效果。只要把每一个节点的左右孩子翻转一下,就可以达到整体翻转的效果。这道题目使用前序遍历和后序遍历都可以,唯独中序遍历不方便,因为中序遍历会把某些节点的左右孩子翻转了两次。层序遍历也可以,只要把每一个节点的左右孩子翻转一下的遍历方式都是可以的!

class Solution {
public:
	TreeNode* invertTree(TreeNode* root) { // 前序遍历 递归
		if (root == nullptr) return root;
		swap(root->left, root->right);
		invertTree(root->left);
		invertTree(root->right);
		return root;
	}
};
class Solution {
public:
	TreeNode* invertTree(TreeNode* root) { // 统一迭代法 前序遍历 
		stack<TreeNode*> st;
		if (root != nullptr) st.push(root);
		while (!st.empty()) {
			TreeNode* node = st.top();
			if (node != nullptr) {
				st.pop();
				if (node->right) st.push(node->right);
				if (node->left) st.push(node->left);
				st.push(node);
				st.push(nullptr);
			}
			else {
				st.pop();
				node = st.top();
				st.pop();
				swap(node->left, node->right);
			}
		}
		return root;
	}
};
class Solution {
public:
	TreeNode* invertTree(TreeNode* root) { // 层序遍历 迭代
		queue<TreeNode*> que;
		if (root != nullptr) que.push(root);
		while (!que.empty()) {
			int size = que.size();
			for (int i = 0; i < size; i++) {
				TreeNode* node = que.front();
				que.pop();
				swap(node->left, node->right);
				if (node->left) que.push(node->left);
				if (node->right) que.push(node->right);
			}
		}
		return root;
	}
};

前面提到中序遍历会使得某些节点的左右孩子被翻转两次,如下代码可实现中序遍历正常翻转:

class Solution {
public:
	TreeNode* invertTree(TreeNode* root) { // 递归 中序遍历
		if (root == nullptr) return root;
		invertTree(root->left);
		swap(root->left, root->right);
		invertTree(root->left); // 遍历左孩子,因为中间节点已经翻转了
		return root;
	}
};

当然,统一迭代法的中序遍历是可以解决这个问题的,因为这是用栈来遍历,而不是靠指针来遍历,避免了递归法中翻转了两次的情况。

class Solution {
public:
	TreeNode* invertTree(TreeNode* root) { // 统一迭代法 中序遍历 
		stack<TreeNode*> st;
		if (root != nullptr) st.push(root);
		while (!st.empty()) {
			TreeNode* node = st.top();
			if (node != nullptr) {
				st.pop();
				if (node->right != nullptr) st.push(node->right);
				st.push(node);
				st.push(nullptr);
				if (node->left != nullptr) st.push(node->left);
			}
			else {
				st.pop();
				node = st.top();
				st.pop();
				swap(node->left, node->right);
			}
		}
		return root;
	}
};

106. 从中序与后序遍历序列构造二叉树(🧡🧡🧡🧡)

给定两个整数数组 inorder 和 postorder ,其中 inorder 是二叉树的中序遍历, postorder 是同一棵树的后序遍历,请你构造并返回这颗 二叉树。

步骤:

  • 第一步:如果数组大小为零的话,说明是空节点了。
  • 第二步:如果不为空,那么取后序数组最后一个元素作为节点元素。
  • 第三步:找到后序数组最后一个元素在中序数组的位置,作为切割点
  • 第四步:切割中序数组,切成中序左数组和中序右数组 (顺序别搞反了,一定是先切中序数组)
  • 第五步:切割后序数组,切成后序左数组和后序右数组
  • 第六步:递归处理左区间和右区间
class Solution {
public:
	TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
		if (inorder.size() == 0 || postorder.size() == 0) return nullptr;
		return traversal(inorder, postorder);
	}

	TreeNode* traversal(vector<int>& inorder, vector<int>& postorder) {
		if (postorder.size() == 0) return nullptr;

		// 第二步:后序遍历数组最后一个元素,就是当前的中间节点
		int rootValue = postorder[postorder.size() - 1];
		TreeNode* root = new TreeNode(rootValue);

		if (postorder.size() == 1) return root; // 叶子节点

		// 第三步:找到中序遍历的切割点
		int delimiterIndex;
		for (delimiterIndex = 0; delimiterIndex < inorder.size(); delimiterIndex++) {
			if (inorder[delimiterIndex] == rootValue) break;
		}

		// 第四步:切割中序数组,左闭右开
		// [0, delimiterIndex)
		vector<int> leftInorder(inorder.begin(), inorder.begin() + delimiterIndex);
		// [delimiterIndex + 1, end)
		vector<int> rightInorder(inorder.begin() + delimiterIndex + 1, inorder.end());

		postorder.resize(postorder.size() - 1); // 舍弃末尾元素,已经用过了

		// 第五步:切割后序数组,左闭右开,注意这里使用了左中序数组大小作为切割点
		// [0, leftInorder.size())
		vector<int> leftPostorder(postorder.begin(), postorder.begin() + leftInorder.size());
		// [leftInorder.size(), end)
		vector<int> rightPostorder(postorder.begin() + leftInorder.size(), postorder.end());

		// 第六步:递归
		root->left = traversal(leftInorder, leftPostorder);
		root->right = traversal(rightInorder, rightPostorder);

        return root;
	}
};

上述的代码性能并不好,因为每层递归定义了新的vector(就是数组),既耗时又耗空间,下面做了一些优化,以下标索引来写。

class Solution {
public:
	TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
		if (inorder.size() == 0 || postorder.size() == 0) return nullptr;
		// 左闭右开的原则
		return traversal(inorder, 0, inorder.size(), postorder, 0, postorder.size());
	}

	TreeNode* traversal(vector<int>& inorder, int inorderBegin, int inorderEnd, vector<int>& postorder, int postorderBegin, int postorderEnd) {
		if (postorderBegin == postorderEnd) return nullptr;
		int rootValue = postorder[postorderEnd - 1];
		TreeNode* root = new TreeNode(rootValue);
		if (postorder.size() == 1) return root;
		int delimiterIndex = 0;
		for (delimiterIndex = 0; delimiterIndex < inorder.size(); delimiterIndex++) {
			if (inorder[delimiterIndex] == rootValue) break;
		}

		// 切割中序数组
		// 左中序区间,左闭右开[leftInorderBegin, leftInorderEnd)
		int leftInorderBegin = inorderBegin;
		int leftInorderEnd = delimiterIndex;
		// 右中序区间,左闭右开[rightInorderBegin, rightInorderEnd)
		int rightInorderBegin = delimiterIndex + 1;
		int rightInorderEnd = inorderEnd;

		// 切割后序数组
		// 左后序区间,左闭右开[leftPostorderBegin, leftPostorderEnd)
		int leftPostorderBegin = postorderBegin;
		int leftPostorderEnd = postorderBegin + (delimiterIndex - inorderBegin);
		// 右后序区间,左闭右开[rightPostorderBegin, rightPostorderEnd)
		int rightPostorderBegin = leftPostorderEnd;
		int rightPostorderEnd = postorderEnd - 1; // 排除最后一个元素,已经作为节点了

		root->left = traversal(inorder, leftInorderBegin, leftInorderEnd, postorder, leftPostorderBegin, leftPostorderEnd);
		root->right = traversal(inorder, rightInorderBegin, rightInorderEnd, postorder, rightPostorderBegin, rightPostorderEnd);

		return root;
	}
};

105. 从前序与中序遍历序列构造二叉树

给定两个整数数组 preorder 和 inorder ,其中 preorder 是二叉树的先序遍历, inorder 是同一棵树的中序遍历,请构造二叉树并返回其根节点。

class Solution {
public:
	TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
		if (preorder.size() == 0 || inorder.size() == 0) return nullptr;
		return traversal(preorder, inorder);
	}

	TreeNode* traversal(vector<int>& preorder, vector<int>& inorder) {
		if (inorder.size() == 0) return nullptr;

		int rootValue = preorder[0];
		TreeNode* root = new TreeNode(rootValue);

		if (inorder.size() == 1) return root;

		// 找切割点
		int delimiterIndex;
		for (delimiterIndex = 0; delimiterIndex < inorder.size(); delimiterIndex++) {
			if (inorder[delimiterIndex] == rootValue) break;
		}

		vector<int> leftInorder(inorder.begin(), inorder.begin() + delimiterIndex);
		vector<int> rightInorder(inorder.begin() + delimiterIndex + 1, inorder.end());

		vector<int> leftPreorder(preorder.begin() + 1, preorder.begin() + 1 + leftInorder.size());
		vector<int> rightPreorder(preorder.begin() + 1 + leftInorder.size(), preorder.end());

		root->left = traversal(leftPreorder, leftInorder);
		root->right = traversal(rightPreorder, rightInorder);

		return root;
	}
};
class Solution {
public:
	TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
		if (preorder.size() == 0 || inorder.size() == 0) return nullptr;
		return traversal(preorder, 0, preorder.size(), inorder, 0, inorder.size());
	}

	TreeNode* traversal(vector<int>& preorder, int preorderBegin, int preorderEnd, vector<int>& inorder, int inorderBegin, int inorderEnd) {
		if (preorderBegin == preorderEnd) return nullptr;

		int rootValue = preorder[preorderBegin];
		TreeNode* root = new TreeNode(rootValue);

		if (inorderEnd - inorderBegin == 1) return root;

		int delimiterIndex;
		for (delimiterIndex = inorderBegin; delimiterIndex < inorderEnd; delimiterIndex++) {
			if (inorder[delimiterIndex] == rootValue) break;
		}

		// 分割
		int leftInorderBegin = inorderBegin;
		int leftInorderEnd = delimiterIndex;
		int rightInorderBegin = delimiterIndex + 1;
		int rightInorderEnd = inorderEnd;

		int leftPreorderBegin = preorderBegin + 1;
		int leftPreorderEnd = preorderBegin + 1 + (leftInorderEnd - leftInorderBegin);
		int rightPreorderBegin = leftPreorderEnd;
		int rightPreorderEnd = preorderEnd;

		root->left = traversal(preorder, leftPreorderBegin, leftPreorderEnd, inorder, leftInorderBegin, leftInorderEnd);
		root->right = traversal(preorder, rightPreorderBegin, rightPreorderEnd, inorder, rightInorderBegin, rightInorderEnd);
		return root;
	}
};

654. 最大二叉树

给定一个不重复的整数数组 nums 。 最大二叉树 可以用下面的算法从 nums 递归地构建:

  • 创建一个根节点,其值为 nums 中的最大值。
  • 递归地在最大值 左边 的 子数组前缀上 构建左子树。
  • 递归地在最大值 右边 的 子数组后缀上 构建右子树。

返回 nums 构建的 最大二叉树 。

构造树一般采用的是前序遍历,因为先构造中间节点,然后递归构造左子树和右子树。

class Solution {
public:
	TreeNode* constructMaximumBinaryTree(vector<int>& nums) {
		TreeNode* node = new TreeNode(0);
		if (nums.size() == 1) {
			node->val = nums[0];
			return node;
		}
		// 找到数组中最大的值和对应的下标
		int maxValue = 0;
		int maxValueIndex = 0;
		for (int i = 0; i < nums.size(); i++) {
			if (nums[i] > maxValue) {
				maxValue = nums[i];
				maxValueIndex = i;
			}
		}
		node->val = maxValue;
		if (maxValueIndex > 0) {
			vector<int> newVec(nums.begin(), nums.begin() + maxValueIndex);
			node->left = constructMaximumBinaryTree(newVec);
		}
		if (maxValueIndex < nums.size() - 1) {
			vector<int> newVec(nums.begin() + maxValueIndex + 1, nums.end());
			node->right = constructMaximumBinaryTree(newVec);
		}
		return node;
	}
};
class Solution {
public:
	TreeNode* constructMaximumBinaryTree(vector<int>& nums) {
		return traversal(nums, 0, nums.size());
	}

	TreeNode* traversal(vector<int>& nums, int left, int right) {
		if (left >= right) return nullptr;
		int maxValueIndex = left;
		for (int i = left + 1; i < right; i++) {
			if (nums[i] > nums[maxValueIndex]) maxValueIndex = i;
		}
		TreeNode* root = new TreeNode(nums[maxValueIndex]);
		root->left = traversal(nums, left, maxValueIndex);
		root->right = traversal(nums, maxValueIndex + 1, right);
		return root;
	}
};

617. 合并二叉树

给你两棵二叉树: root1 和 root2 。想象一下,当你将其中一棵覆盖到另一棵之上时,两棵树上的一些节点将会重叠(而另一些不会)。你需要将这两棵树合并成一棵新二叉树。合并的规则是:如果两个节点重叠,那么将这两个节点的值相加作为合并后节点的新值;否则,不为 null 的节点将直接作为新二叉树的节点。返回合并后的二叉树。注意: 合并过程必须从两个树的根节点开始。

class Solution {
public:
	TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) {
		if (root1 == nullptr) return root2;
		if (root2 == nullptr) return root1;
		root1->val += root2->val;
		root1->left = mergeTrees(root1->left, root2->left);
		root1->right = mergeTrees(root1->right, root2->right);
		return root1;
	}
};
class Solution {
public:
	TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) {
		if (root1 == nullptr) return root2;
		if (root2 == nullptr) return root1;
		queue<TreeNode*> que;
		que.push(root1);
		que.push(root2);
		while (!que.empty()) {
			TreeNode* node1 = que.front(); que.pop();
			TreeNode* node2 = que.front(); que.pop();
			node1->val += node2->val;
			if (node1->left != nullptr && node2->left != nullptr) {
				que.push(node1->left);
				que.push(node2->left);
			}
			if (node1->right != nullptr && node2->right != nullptr) {
				que.push(node1->right);
				que.push(node2->right);
			}
			if (node1->left == nullptr && node2->left != nullptr) {
				node1->left = node2->left;
			}
			if (node1->right == nullptr && node2->right != nullptr) {
				node1->right = node2->right;
			}
		}
		return root1;
	}
};

以上题解大多来自【代码随想录】(完整版看这个!!),在此基础上做了一定总结,并附带一些自己的理解。

后续题目,随缘更新,有错误请指出!

END

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

AlphaGuaGua

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值