两棵二叉搜索树中的所有元素 图解题解
这道题到底在问什么
- 输入
- root1=[50,30,70,20,40], root2=[60,45,80]
- 输出
- [20,30,40,45,50,60,70,80]
最优解:一步一步想明白
- 3记住「中序拿有序数组,再归并」,下面逐帧套它。
- 4中序遍历 root1:它的左子树都走完了,现在轮到节点 20。
- 5把 20 记进 A(变绿),接着去它的右子树。中序的顺序保证 A 一路都是升序。
- 6中序遍历 root1:它的左子树都走完了,现在轮到节点 30。
- 7把 30 记进 A(变绿),接着去它的右子树。中序的顺序保证 A 一路都是升序。
- 8中序遍历 root1:它的左子树都走完了,现在轮到节点 40。
- 9把 40 记进 A(变绿),接着去它的右子树。中序的顺序保证 A 一路都是升序。
- 10中序遍历 root1:它的左子树都走完了,现在轮到节点 50。
- 11把 50 记进 A(变绿),接着去它的右子树。中序的顺序保证 A 一路都是升序。
- 12中序遍历 root1:它的左子树都走完了,现在轮到节点 70。
- 13把 70 记进 A(变绿),接着去它的右子树。中序的顺序保证 A 一路都是升序。
- 14root1 中序遍历完成,A = [20, 30, 40, 50, 70],已经是升序数组。
- 15中序遍历 root2:它的左子树都走完了,现在轮到节点 45。
- 16把 45 记进 B(变绿),接着去它的右子树。中序的顺序保证 B 一路都是升序。
- 17中序遍历 root2:它的左子树都走完了,现在轮到节点 60。
- 18把 60 记进 B(变绿),接着去它的右子树。中序的顺序保证 B 一路都是升序。
- 19中序遍历 root2:它的左子树都走完了,现在轮到节点 80。
- 20把 80 记进 B(变绿),接着去它的右子树。中序的顺序保证 B 一路都是升序。
- 21root2 中序遍历完成,B = [45, 60, 80],已经是升序数组。
- 22两个有序数组都拿到了。用双指针 i 扫 A、j 扫 B,每次比一比 A[i] 和 B[j],把较小的放进结果。
- 23A[i]=20 ≤ B[j]=45,取 A 的 20(绿色),放进结果的第 1 位。
- 24A[i]=30 ≤ B[j]=45,取 A 的 30(绿色),放进结果的第 2 位。
- 25A[i]=40 ≤ B[j]=45,取 A 的 40(绿色),放进结果的第 3 位。
- 26B[j]=45 < A[i]=50,取 B 的 45(绿色),放进结果的第 4 位。
- 27A[i]=50 ≤ B[j]=60,取 A 的 50(绿色),放进结果的第 5 位。
- 28B[j]=60 < A[i]=70,取 B 的 60(绿色),放进结果的第 6 位。
- 29A[i]=70 ≤ B[j]=80,取 A 的 70(绿色),放进结果的第 7 位。
- 30A 已取完,直接取 B 的 80(绿色),放进结果的第 8 位。
- 31归并完成:最终升序结果 [20, 30, 40, 45, 50, 60, 70, 80]。回头看,靠 BST 中序天然有序,把树的问题化成了两个有序数组归并,时间 O(m+n)。
⚠️ 容易写错的地方
✗ 错:用前序或后序遍历取值
✓ 对:必须用中序
只有 BST 的中序遍历结果才是升序,前序、后序都不有序,后面没法直接归并
✗ 错:归并时漏处理一方先取完
✓ 对:一方到头就把另一方剩余整段接上
只在两边都没到头时比较,某一方空了还去取它就会越界
✗ 错:把重复值去掉
✓ 对:相等的值都要保留
题目要的是全部元素,两树有相同值时结果里要出现多次,不是集合去重
完整代码(Python / C++ / Java)
Python
from typing import List, Optional
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
class Solution:
def getAllElements(self, root1: Optional[TreeNode], root2: Optional[TreeNode]) -> List[int]:
def inorder(node, out):
if not node:
return
inorder(node.left, out)
out.append(node.val)
inorder(node.right, out)
a, b = [], []
inorder(root1, a)
inorder(root2, b)
ans = []
i = j = 0
while i < len(a) or j < len(b):
if j == len(b) or (i < len(a) and a[i] <= b[j]):
ans.append(a[i]); i += 1
else:
ans.append(b[j]); j += 1
return ansC++
#include <algorithm>
#include <climits>
#include <functional>
#include <iterator>
#include <numeric>
#include <vector>
using namespace std;
struct TreeNode{int val;TreeNode* left;TreeNode* right;TreeNode():val(0),left(nullptr),right(nullptr){} TreeNode(int x):val(x),left(nullptr),right(nullptr){} TreeNode(int x,TreeNode*l,TreeNode*r):val(x),left(l),right(r){}};
class Solution{public: vector<int> getAllElements(TreeNode* root1,TreeNode* root2){ vector<int>a,b,ans; function<void(TreeNode*,vector<int>&)> in=[&](TreeNode*n,vector<int>&v){ if(!n)return; in(n->left,v); v.push_back(n->val); in(n->right,v);}; in(root1,a); in(root2,b); merge(a.begin(),a.end(),b.begin(),b.end(),back_inserter(ans)); return ans; }};Java
import java.util.*;
class TreeNode{int val;TreeNode left,right;TreeNode(){} TreeNode(int val){this.val=val;} TreeNode(int val,TreeNode left,TreeNode right){this.val=val;this.left=left;this.right=right;}}
class Solution{ public List<Integer> getAllElements(TreeNode root1,TreeNode root2){ ArrayList<Integer>a=new ArrayList<>(),b=new ArrayList<>(),ans=new ArrayList<>(); in(root1,a); in(root2,b); int i=0,j=0; while(i<a.size()||j<b.size()){ if(j==b.size()||(i<a.size()&&a.get(i)<=b.get(j)))ans.add(a.get(i++)); else ans.add(b.get(j++)); } return ans;} void in(TreeNode n,List<Integer>v){ if(n==null)return; in(n.left,v); v.add(n.val); in(n.right,v);} }复杂度
时间
O(m + n)
两棵树各中序遍历一次,共 O(m+n);归并两个有序数组也是 O(m+n),m、n 是两树节点数
空间
O(m + n)
两个中序数组 + 结果数组共 O(m+n);递归中序的栈是 O(h1+h2),不超过这个量级
看不够?换成动画再走一遍
上面的推演每一步都对应一帧动画。点开交互动画版,能一步步看着 两棵二叉搜索树中的所有元素 的数据怎么变、指针怎么走,还能切 Python / Java / C++ 跟着练。
面试官可能追问
能不能不先存两个完整数组,边遍历边归并?+
可以。用两个栈分别模拟两棵树的中序迭代器,每次比较两个迭代器的当前最小值、弹出较小的那个推进,这样不用一次性展开两个数组,额外空间从 O(m+n) 降到 O(h1+h2)。代价是代码更复杂,面试里能说出这个优化方向就够了。
如果输入不是 BST、而是两个普通二叉树呢?+
那中序就不一定有序了,得改成:各自遍历(任意序)收集所有值,合起来排序,O((m+n) log(m+n))。BST 这道题正是利用了「中序有序」才省掉排序、做到线性。
想听吴师兄把这道题讲给你听?
文字版和动画都随便看。开通图解算法年卡,可以听吴师兄声线把 两棵二叉搜索树中的所有元素 一步步讲透(全站 0 节语音精讲,持续扩充), 卡住的地方还有 AI 私教小欧就着动画帮你拆到懂。
本平台为独立第三方培训机构,与华为技术有限公司无任何关联;课程的服务内容与权益以购买协议为准,学习效果因个人情况而异。「华为 OD」「华为可信」等仅为对岗位与考试方向的客观描述,相关商标归各自权利人所有。