目录
1、T654:最大二叉树
T654:给定一个不重复的整数数组 nums 。 最大二叉树 可以用下面的算法从 nums 递归地构建:
-
创建一个根节点,其值为 nums 中的最大值。
-
递归地在最大值 左边 的 子数组前缀上 构建左子树。
-
递归地在最大值 右边 的 子数组后缀上 构建右子树。
返回 nums 构建的 最大二叉树 。
提示:
-
1 <= nums.length <= 1000
-
0 <= nums[i] <= 1000
-
nums 中的所有整数 互不相同
S: 构造树一般采用的是前序遍历,因为先构造中间节点,然后递归构造左子树和右子树。
1.1 暴力法
一上来直接暴力解法:
TreeNode* constructMaximumBinaryTree(vector<int>& nums) {
if (nums.size() == 0) return nullptr;//🚩题目中是保证了>=1,但如果不在下面设置几个if就不能没有这行
int maxVal = INT_MIN, maxValIndex;
for (int i = 0; i < nums.size(); ++i) {
if (maxVal < nums[i]) {
maxVal = nums[i];
maxValIndex = i;
}
}
TreeNode* root = new TreeNode(maxVal);
vector<int> leftNums(nums.begin(), nums.begin() + maxValIndex);
vector<int> rightNums(nums.begin() + maxValIndex + 1, nums.end());
root->left = constructMaximumBinaryTree(leftNums);
root->right = constructMaximumBinaryTree(rightNums);
// root->right = constructMaximumBinaryTree(nums.assign(nums.begin() + maxValIndex + 1, nums.end()));
//error(还不怎么会分割vector)
return root;
}
其实已经差不多了,就是还有几处要补充一下,补充之后得到版本一:
TreeNode* constructMaximumBinaryTree(vector<int>& nums) {
TreeNode* root = new TreeNode(0);
if (nums.size() == 1) {
root->val = nums[0];
return root;
}
int maxVal = INT_MIN, maxValIndex;
for (int i = 0; i < nums.size(); ++i) {
if (maxVal < nums[i]) {
maxVal = nums[i];
maxValIndex = i;
}
}
root->val = maxVal;
// 最大值所在的下标左区间 构造左子树
if (maxValIndex > 0) {//🚩保证左区间至少有一个数值(为了不让空节点进入递归,没有就无法通过)
vector<int> leftNums(nums.begin(), nums.begin() + maxValIndex);
root->left = constructMaximumBinaryTree(leftNums);
}
if (maxValIndex < nums.size() - 1) {//保证右区间至少有一个数值
vector<int> rightNums(nums.begin() + maxValIndex + 1, nums.end());
root->right = constructMaximumBinaryTree(rightNums);
}
return root;
}
上面两种写法,🚩标记的地方必须至少有一个,否则递归会导致栈溢出(无终止或者不允许空值进栈)
1.2 优化版
优化的思路和前面构造二叉树一样,在原有数组的基础上,通过索引递归更新分割数组
TreeNode* constructMaximumBinaryTree(vector<int>& nums) {
return constructMaximumBinaryTree(nums, 0, nums.size());
}
TreeNode* constructMaximumBinaryTree(vector<int>& nums, int left, int right) {
if (left == right) return nullptr;//没有这行就会栈溢出
TreeNode* root = new TreeNode(0);
// if (nums.size() == 1) {//这一行也可以
if (right - left == 1) {
root->val = nums[left];
return root;
}
int maxVal = INT_MIN, maxValIndex;
for (int i = left; i < right; ++i) {
if (maxVal < nums[i]) {
maxVal = nums[i];
maxValIndex = i;
}
}
root->val = maxVal;
root->left = constructMaximumBinaryTree(nums, left, maxValIndex);
root->right = constructMaximumBinaryTree(nums, maxValIndex + 1, right);
return root;
}
1.3 简.优化版
还可以再简化,把 right - left == 1 的专用逻辑省略,省去maxVal这一变量
C++:
TreeNode* constructMaximumBinaryTree(vector<int>& nums) {
return constructMaximumBinaryTree(nums, 0, nums.size());
}
TreeNode* constructMaximumBinaryTree(vector<int>& nums, int left, int right) {
if (left >= right) return nullptr;//没有这行就会栈溢出
// TreeNode* root = new TreeNode(0);
// if (right - left == 1) {//并入了下面的for循环
// root->val = nums[left];
// return root;
// }
int maxValIndex = left;
for (int i = left + 1; i < right; ++i) {
if (nums[maxValIndex] < nums[i]) {
maxValIndex = i;
}
}
TreeNode* root = new TreeNode(nums[maxValIndex]);
root->left = constructMaximumBinaryTree(nums, left, maxValIndex);
root->right = constructMaximumBinaryTree(nums, maxValIndex + 1, right);
return root;
}
Java:
public TreeNode constructMaximumBinaryTree(int[] nums) {
return constructMaximumBinaryTree(nums, 0, nums.length);
}
private TreeNode constructMaximumBinaryTree(int[] nums, int left, int right) {
if (left >= right) return null;
int maxValIndex = left;
for (int i = left + 1; i < right; ++i) {
if (nums[maxValIndex] < nums[i]) {
maxValIndex = i;
}
}
TreeNode root = new TreeNode(nums[maxValIndex]);
root.left = constructMaximumBinaryTree(nums, left, maxValIndex);
root.right = constructMaximumBinaryTree(nums, maxValIndex + 1, right);
return root;
}
可以发现上面的代码要简洁一些,主要是因为优化版允许空节点进入递归,所以不用在递归的时候加判断节点是否为空
2、T617:合并二叉树
T617:给你两棵二叉树: root1 和 root2 。
想象一下,当你将其中一棵覆盖到另一棵之上时,两棵树上的一些节点将会重叠(而另一些不会)。你需要将这两棵树合并成一棵新二叉树。合并的规则是:如果两个节点重叠,那么将这两个节点的值相加作为合并后节点的新值;否则,不为 null 的节点将直接作为新二叉树的节点。
返回合并后的二叉树。
注意: 合并过程必须从两个树的根节点开始。
提示:
-
两棵树中的节点数目在范围 [0, 2000] 内
-
-104 <= Node.val <= 104
S:
2.1 法1、递归
2.1.1 前序
C++:
TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) {
if (!root1) return root2;//都为空也没关系
if (!root2) return root1;
TreeNode* root = new TreeNode(root1->val + root2->val);
root->left = mergeTrees(root1->left, root2->left);
root->right = mergeTrees(root1->right, root2->right);
return root;
}
->免于new一个新TreeNode:
TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) {
if (!root1) return root2;//都为空也没关系
if (!root2) return root1;
root1->val += root2->val;
root1->left = mergeTrees(root1->left, root2->left);
root1->right = mergeTrees(root1->right, root2->right);
return root1;
}
Java:
public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
if (root1 == null) return root2;
if (root2 == null) return root1;
root1.val += root2.val;
root1.left = mergeTrees(root1.left, root2.left);
root1.right = mergeTrees(root1.right, root2.right);
return root1;
}
2.1.2 中序
TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) {
if (!root1) return root2;//都为空也没关系
if (!root2) return root1;
root1->left = mergeTrees(root1->left, root2->left);
root1->val += root2->val;
root1->right = mergeTrees(root1->right, root2->right);
return root1;
}
2.1.3 后序
TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) {
if (!root1) return root2;//都为空也没关系
if (!root2) return root1;
root1->left = mergeTrees(root1->left, root2->left);
root1->right = mergeTrees(root1->right, root2->right);
root1->val += root2->val;
return root1;
}
2.2 法2、迭代
C++:
TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) {
if (!root1) return root2;
if (!root2) 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 && node2->left) {
que.push(node1->left);
que.push(node2->left);
}
if (node1->right && node2->right) {
que.push(node1->right);
que.push(node2->right);
}
if (!node1->left && node2->left) {
node1->left = node2->left;
}
if (!node1->right && node2->right) {
node1->right = node2->right;
}
// if (node1->right && !node2->right) {
// node2->right = node1->right;
// }
}
return root1;
}
Java:
public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
if (root1 == null) return root2;
if (root2 == null) return root1;
Deque<TreeNode> queue = new LinkedList<>();
queue.addLast(root1);
queue.addLast(root2);
while (!queue.isEmpty()) {
TreeNode node1 = queue.poll();
TreeNode node2 = queue.poll();
node1.val += node2.val;
if (node1.left != null && node2.left != null) {
queue.add(node1.left);
queue.add(node2.left);
}
if (node1.right != null && node2.right != null) {
queue.add(node1.right);
queue.add(node2.right);
}
if (node1.left == null && node2.left != null) {
node1.left = node2.left;
}
if (node1.right == null && node2.right != null) {
node1.right = node2.right;
}
}
return root1;
}
3、T700:二叉搜索树中的搜索
T700:给定二叉搜索树(BST)的根节点 root 和一个整数值 val。
你需要在 BST 中找到节点值等于 val 的节点。 返回以该节点为根的子树。 如果节点不存在,则返回 null 。
提示:
-
数中节点数在 [1, 5000] 范围内
-
1 <= Node.val <= 107
-
root 是二叉搜索树
-
1 <= val <= 107
S:
3.1 法1、递归
C++:
TreeNode* searchBST(TreeNode* root, int val) {
if (!root || root->val == val) return root;
if (root->val < val) return searchBST(root->right, val);
if (root->val > val) return searchBST(root->left, val);
return nullptr;
}
Java:
public TreeNode searchBST(TreeNode root, int val) {
if (root == null || root.val == val) return root;
if (root.val < val) return searchBST(root.right, val);
if (root.val > val) return searchBST(root.left, val);
return null;
}
3.2 法2、迭代法
C++:
TreeNode* searchBST(TreeNode* root, int val) {
while (root) {
if (root->val < val) root = root->right;
else if (root->val > val) root = root->left;
else return root;
}
return nullptr;
}
Java:
public TreeNode searchBST(TreeNode root, int val) {
while (root != null) {
if (root.val > val) root = root.left;
else if (root.val < val) root = root.right;//没有else->空指针异常
else return root;
}
return null;
}
4、T98:验证二叉搜索树
-
节点的左子树只包含 小于 当前节点的数。
-
节点的右子树只包含 大于 当前节点的数。
-
所有左子树和右子树自身必须也是二叉搜索树。
4.1 法1、递归
4.1.1 版本Ⅰ、讲解版
一句话,递归中序遍历将二叉搜索树转变成一个数组再逐个比大小
C++:
vector<int> vec;
bool isValidBST(TreeNode* root) {
if (!root) return true;
traversal(root);
// for (int i = 0; i < vec.size() - 1; ++i) {//二选一
// if (vec[i] >= vec[i + 1]) return false;
// }
for (int i = 1; i < vec.size(); ++i) {
if (vec[i] <= vec[i - 1]) return false;
}
return true;
}
void traversal(TreeNode* node) {
if (!node) return;
traversal(node->left);
vec.push_back(node->val);
traversal(node->right);
}
Java:
List<Integer> nums;
public boolean isValidBST(TreeNode root) {
if (root == null) return true;
nums = new ArrayList<>();
traversal(root);
for (int i = 1; i < nums.size(); ++i) {
if (nums.get(i) <= nums.get(i - 1)) {
return false;
}
}
return true;
}
void traversal(TreeNode node) {
if (node == null) return;
traversal(node.left);
nums.add(node.val);
traversal(node.right);
}
4.1.2 版本Ⅱ、过渡版
在版本Ⅰ中,把二叉树转变为数组来判断,是最直观的,但其实不用转变成数组,可以在递归遍历的过程中直接判断是否有序。
C++:
// int maxVal = INT_MIN;//错误
long long maxVal = LONG_MIN;// 因为后台测试数据中有int最小值,所以都把maxVal改成了longlong最小值。
bool isValidBST(TreeNode* root) {
if (!root) return true;
bool left = isValidBST(root->left);
if (maxVal < root->val) {
maxVal = root->val;
} else return false;
bool right = isValidBST(root->right);
return left && right;
}
// Integer maxVal = Integer.MIN_VALUE;//error
Long maxVal = Long.MIN_VALUE;
public boolean isValidBST(TreeNode root) {
if (root == null) return true;
boolean left = isValidBST(root.left);
if (maxVal < root.val) {
// maxVal = root.val;
maxVal = Long.valueOf(root.val);
} else return false;
// if (maxVal >= root.val) {//if二选一
// return false;
// } else maxVal = Long.valueOf(root.val);
boolean right = isValidBST(root.right);
return left && right;
}
如果测试数据中有 longlong的最小值,怎么办?
不可能在初始化一个更小的值了吧。 建议避免 初始化最小值,如下方法取到最左面节点的数值来比较。
4.1.3 最终版
C++:
TreeNode* pre = nullptr;
bool isValidBST(TreeNode* root) {
if (!root) return true;
bool left = isValidBST(root->left);
if (pre && pre->val >= root->val) return false;//if的条件不能反过来,否则会被初始的空值忽悠
pre = root;
bool right = isValidBST(root->right);
return left && right;
}
Java:
TreeNode pre;
public boolean isValidBST(TreeNode root) {
if (root == null) return true;
boolean left = isValidBST(root.left);
if (pre != null && pre.val >= root.val) {//不好设置反向条件
return false;
}
pre = root;
boolean right = isValidBST(root.right);
return left && right;
}
4.2 法2、迭代
迭代法中序遍历稍加改动就可以了
4.2.1 普通迭代
C++:
bool isValidBST(TreeNode* root) {
if (!root) return true;
stack<TreeNode*> st;
TreeNode* cur = root;
TreeNode* pre = nullptr;
while (cur || !st.empty()) {
if (cur) {
st.push(cur);
cur = cur->left;
} else {
cur = st.top();
st.pop();
if (pre && pre->val >= cur->val) {//条件要考虑周全!
return false;
}
pre = cur;
cur = cur->right;
}
}
return true;
}
Java:
TreeNode pre = null;
public boolean isValidBST(TreeNode root) {
if (root == null) return true;
Stack<TreeNode> stack = new Stack<>();
TreeNode cur = root;
while (cur != null || !stack.isEmpty()) {
if (cur != null) {
stack.push(cur);
cur = cur.left;
} else {
cur = stack.pop();
if (pre != null && pre.val >= cur.val) {
return false;
}
pre = cur;
cur = cur.right;
}
}
return true;
}
4.2.2 统一迭代
TreeNode pre = null;
public boolean isValidBST(TreeNode root) {
if (root == null) return true;
Deque<TreeNode> stack = new LinkedList<>();
stack.offerFirst(root);
while (!stack.isEmpty()) {
TreeNode cur = stack.pop();
if (cur != null) {
if (cur.right != null) stack.push(cur.right);
stack.addFirst(cur);
stack.push(null);
if (cur.left != null) stack.push(cur.left);
} else {
cur = stack.removeFirst();
if (pre != null && pre.val >= cur.val) {
return false;
}
pre = cur;
}
}
return true;
}