目录
1. 二叉树的前序遍历
给你二叉树的根节点
root
,返回它节点值的 前序 遍历。
示例 1:
输入:root = [1,null,2,3]
输出:[1,2,3]
示例 2:
输入:root = []
输出:[]
示例 3:
输入:root = [1]
输出:[1]
解题思路
1. 如果树为空,直接返回
2. 如果树非空:从根节点位置开始遍历,因为前序遍历规则:根节点、左子树、右子树
a. 沿着根节点一直往左走,将所经过路径中的节点依次入栈,并访问。
b. 取栈顶元素,该元素取到后,其左子树要么为空,要么已经遍历,可以直接遍历该节点,对于该节点,其左子树已经遍历,该节点也已经遍历,剩余其右子树没有遍历,将其左子树当成一棵新的树开始遍历,继续a步骤。
(前序遍历时每走到一个节点就往ret数组中添加该节点,即第一次访问该节点时添加)
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> ret = new ArrayList<>();
Stack<TreeNode> s = new Stack<>();
TreeNode cur = root;
while(cur != null || !s.empty())
{
// 每次循环表示要开始访问一颗树了,先将一颗树的左路节点都入栈并访问节点
// 剩余左路节点的右子树还没访问
while(cur != null)
{
ret.add(cur.val);
s.push(cur);
cur = cur.left;
}
// 取栈中的节点依次访问节点的右子树
cur = s.pop();
cur = cur.right;
}
return ret;
}
2. 二叉树的中序遍历
OJ:二叉树的中序遍历
给定一个二叉树的根节点
root
,返回 它的 中序 遍历 。
示例 1:
输入:root = [1,null,2,3]
输出:[1,3,2]
示例 2:
输入:root = []
输出:[]
示例 3:
输入:root = [1]
输出:[1]
解题思路
(1)空树,直接返回
(2)如果树非空:从根节点位置开始遍历,但此时根节点不能遍历,因为中序遍历规则:左子树、根节点、右子树
a. 沿着根节点一直往左走,将所经过路径中的节点依次入栈,直至当前根节点为空
b. 取栈顶元素,该元素取到后,其左子树要么为空,要么已经遍历,可以直接遍历该节点,对于该节点,其左子树已经遍历,该节点也已经遍历,剩余其右子树没有遍历,将其左子树当成一棵新的树开始遍历,继续a步骤
(简单来说就是将节点先入栈,出栈时再往ret数组中添加元素。第二次访问时添加该节点)
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> ret = new ArrayList<>();
// 空树直接返回
if(null == root){
return ret;
}
Stack<TreeNode> s = new Stack<>();
TreeNode cur = root;
while(cur != null || !s.empty()){
// 沿这cur一直往左侧走,找到该条路径中最左侧的节点,并保存其所经路径中的所有节点
while(cur != null){
s.push(cur);
cur = cur.left;
}
// 获取根节点,直接遍历,因为其左侧是空树
cur = s.peek();
s.pop();
ret.add(cur.val);
// cur的左子树已经遍历,cur已经遍历,剩余其右子树没有遍历,
// 将其右子树当成一棵新的树进行遍历
cur = cur.right;
}
return ret;
}
3. 二叉树的后序遍历
OJ:二叉树的后序遍历
给你一棵二叉树的根节点
root
,返回其节点值的 后序遍历 。
示例 1:
输入:root = [1,null,2,3]
输出:[3,2,1]
示例 2:
输入:root = []
输出:[]
示例 3:
输入:root = [1]
输出:[1]
解题思路
(1)空树,直接返回
(2)如果树非空:从根节点位置开始遍历,但此时根节点不能遍历,因为后序遍历规则:左子树、右子树、根节点
a. 沿着根节点一直往左走,将所经过路径中的节点依次入栈
b. 取栈顶元素,该元素取到后,其左子树要么为空,要么已经遍历,但是此时该节点不能遍历,除非其右子树不存在或者其右子树已经遍历,才可以遍历该节点, 如果该节点右子树没有遍历,将其右子树作为一棵新的二叉树遍历,继续a步骤
(当第三次访问到节点时,将节点的值添加到lsit数组中)
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> list = new ArrayList<>();
if(root == null){
return list;
}
Deque<TreeNode> stack = new ArrayDeque<>();
TreeNode cur = root;
TreeNode prev = null;
while(cur != null || !stack.isEmpty()){
while(cur != null){
stack.push(cur);
cur = cur.left;
}
cur = stack.pop();
if(cur.right == null || cur.right == prev){
list.add(cur.val);
prev = cur;
cur = null;
}else{
stack.push(cur);
cur = cur.right;
}
}
return list;
}
4. 根据二叉树创建字符串
OJ:根据二叉树创建字符串
给你二叉树的根节点
root
,请你采用前序遍历的方式,将二叉树转化为一个由括号和整数组成的字符串,返回构造出的字符串。空节点使用一对空括号对
"()"
表示,转化后需要省略所有不影响字符串与原始二叉树之间的一对一映射关系的空括号对。
示例1:
输入:root = [1,2,3,4]
输出:"1(2(4))(3)"
解释:初步转化后得到 "1(2(4)())(3()())" ,但省略所有不必要的空括号对后,字符串应该是"1(2(4))(3)" 。
示例 2:
输入:root = [1,2,3,null,4]
输出:"1(2()(4))(3)"
解释:和第一个示例类似,但是无法省略第一个空括号对,否则会破坏输入与输出一一映射的关系。
解题思路
1. 如果树空,转化结束
2. 如果树非空
a. 先转化根节点
b. 转化根节点的左子树
如果根的左子树非空或者左子树空但是右子树非空:( 递归转化左子树 ), 注意将转化结果内嵌到()中
c. 转化根节点的右子树
如果根的右子树非空:( 递归转化右子树 ), 注意将转化结果内嵌到()中
class Solution {
String str;
public String tree2str(TreeNode t) {
StringBuilder sb = new StringBuilder();
tree2str(t, sb);
return sb.toString();
}
public void tree2str(TreeNode t, StringBuilder str) {
if(null == t){
return;
}
// 先将根节点的数据放到str中
str.append(t.val);
// 处理根节点的左子树
if(null != t.left || null != t.right)
{
// 左子树非空,递归转化左子树
str.append("(");
tree2str(t.left, str);
str.append(")");
}
// 再检测t的右子树,如果右子树为空,不增加任何内容
if(null != t.right){
// 递归处理右子树
str.append("(");
tree2str(t.right, str);
str.append(")");
}
}
}
5. 从前序与中序遍历序列构造二叉树
给定两个整数数组
preorder
和inorder
,其中preorder
是二叉树的先序遍历,inorder
是同一棵树的中序遍历,请构造二叉树并返回其根节点。
示例 1:
输入: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]
输出: [3,9,20,null,null,15,7]
示例 2:
输入: preorder = [-1], inorder = [-1]
输出: [-1]
解题思路
从前序遍历中获取根节点,中序遍历中根节点前面的是左子树的部分,中序遍历中根节点后面的是右子树部分。使用index下标来遍历前序数组。
1. 从前序遍历结果中获取到树的根节点
2. 在中序遍历结果中确定根节点的位置,按照该位置将中序遍历结果分为两部分
左半部分是根节点的左子树,递归创建根节点的左子树
右半部分是根节点的右子树,递归创建根节点的右子树
class Solution {
public TreeNode buildTree(int[] preorder, int[] inorder) {
return buildTreeHelper(preorder,inorder,0,inorder.length - 1);
}
int index = 0;
private TreeNode buildTreeHelper(int[] preorder, int[] inorder, int left, int right) {
if(index == inorder.length ){
return null;
}
if (left > right){
return null;
}
int pos = find(inorder,preorder);
TreeNode root = new TreeNode(preorder[index]);
index ++;
root.left = buildTreeHelper(preorder,inorder,left,pos - 1);
root.right = buildTreeHelper(preorder,inorder,pos + 1,right);
return root;
}
private int find(int[] inorder,int[] preorder) {
for (int i = 0; i < inorder.length; i++) {
if (inorder[i] == preorder[index]){
return i;
}
}
return -1;
}
}
6. 从中序与后序遍历序列构造二叉树
给定两个整数数组
inorder
和postorder
,其中inorder
是二叉树的中序遍历,postorder
是同一棵树的后序遍历,请你构造并返回这颗 二叉树 。
示例 1:
输入:inorder = [9,3,15,20,7], postorder = [9,15,7,20,3]
输出:[3,9,20,null,null,15,7]
示例 2:
输入:inorder = [-1], postorder = [-1]
输出:[-1]
解题思路
先看看同一颗树的前序遍历结果preorder = [3,9,20,15,7]和后序遍历的结果postorder = [9,15,7,20,3],对比一下这两。发现将后序遍历的结果反转->[3,20,7,15,9],前序遍历是根左右,后序遍历反转后的是根右左,所以我们只需要将后序遍历的结果反转一下,然后向上一题那样构建二叉树,只不过是先构建右子树再构建左子树。
class Solution {
public TreeNode buildTree(int[] inorder, int[] postorder) {
reverse(postorder);
return buildTreeHelper(postorder,inorder,0,inorder.length - 1);
}
private void reverse(int[] postorder){
for(int i = 0;i < postorder.length/2;i ++){
int temp = postorder[i];
postorder[i] = postorder[postorder.length - i - 1];
postorder[postorder.length - i - 1] = temp;
}
}
int index = 0;
private TreeNode buildTreeHelper(int[] preorder, int[] inorder, int left, int right) {
if(index == preorder.length ){
return null;
}
if (left > right){
return null;
}
int val = preorder[index];
index ++;
int pos = find(inorder,val);
TreeNode root = new TreeNode(val);
root.right = buildTreeHelper(preorder,inorder,pos + 1,right);
root.left = buildTreeHelper(preorder,inorder,left,pos - 1);
return root;
}
private int find(int[] inorder,int val) {
for (int i = 0; i < inorder.length; i++) {
if (inorder[i] == val){
return i;
}
}
return -1;
}
}
7. 二叉树的最近公共祖先
OJ:二叉树的最近公共祖先
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
示例 1:
输入:root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
输出:3
解释:节点5和节点1的最近公共祖先是节点3
示例 2:
输入:root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4
输出:5
解释:节点5和节点1的最近公共祖先是节点3
解题思路
这题不太熟,暂时不写思路。
class Solution {
TreeNode lca;
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
find(root,p,q);
return lca;
}
private boolean find(TreeNode root, TreeNode p, TreeNode q){
if(root == null){
return false;
}
int left = find(root.left,p,q) ? 1 : 0;
int right = find(root.right,p,q) ? 1 : 0;
int mid = (root == p || root == q) ? 1 : 0;
if(left + right + mid == 2){
lca = root;
}
return left + right + mid > 0;
}
}