二叉树的递归法和统一迭代法
题目1:144. 二叉树的前序遍历
思路:中左右
1)递归法
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> result =new ArrayList<>();
Stack<TreeNode> stack=new Stack<>();
if(root==null){
return result;
}
stack.push(root);
while(!stack.isEmpty()){
TreeNode node= stack.pop();
result.add(node.val);
//中
if (node.right != null){
//右
stack.add(node.right);
}
if(node.left != null){
//左
stack.add(node.left);
}
}
return result;
}
}
2)统一迭代法 前序:中左右 入栈的话就是右左中 辅助数据结构:栈
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> result = new LinkedList<>();
Stack<TreeNode> st = new Stack<>();
if (root != null) st.push(root);
while (!st.empty()) {
TreeNode node = st.peek();
if (node != null) {
st.pop(); // 先弹把中点弹出去,后面跟右中左再加回来
if (node.right!=null) st.push(node.right); // 添加右节点(空节点不入栈)
if (node.left!=null) st.push(node.left); // 添加左节点(空节点不入栈)
st.push(node); // 添加中节点
st.push(null); // 中节点访问过,但是还没有处理,加入空节点做为标记。
} else { // 只有遇到空节点的时候,才将下一个节点放进结果集
st.pop(); // 将空节点弹出
node = st.peek(); // 重新取出栈中元素
st.pop();
result.add(node.val); // 加入到结果集
}
}
return result;
}
}
题目2:94. 二叉树的中序遍历
思路:左中右
1)递归法
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> result=new ArrayList<>();
traveral(root,result);
return result;
}
void traveral(TreeNode root,List<Integer> result){
if(root==null){return;}
traveral(root.left,result);
result.add(root.val);
traveral(root.right,result);
}
}
2)统一迭代法 中序:左中右 入栈的话就是右中左 辅助数据结构:栈
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> result = new LinkedList<>();
Stack<TreeNode> st = new Stack<>();
if (root != null) st.push(root);
while (!st.empty()) {
TreeNode node = st.peek();
if (node != null) {
st.pop(); // 先弹把中点弹出去,后面跟右中左再加回来
if (node.right!=null) st.push(node.right); // 添加右节点(空节点不入栈)
st.push(node); // 添加中节点
st.push(null); // 中节点访问过,但是还没有处理,加入空节点做为标记。
if (node.left!=null) st.push(node.left); // 添加左节点(空节点不入栈)
} else { // 只有遇到空节点的时候,才将下一个节点放进结果集
st.pop(); // 将空节点弹出
node = st.peek(); // 重新取出栈中元素
st.pop();
result.add(node.val); // 加入到结果集
}
}
return result;
}
}
题目3:145. 二叉树的后序遍历
思路:左右中
1)递归法
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> result=new ArrayList<Integer>();
tranveral(root,result);
return result;
}
void tranveral(TreeNode root,List<Integer> result){
if(root==null){return;}
tranveral(root.left,result);
tranveral(root.right,result);
result.add(root.val);
}
}
2)统一迭代法 后序:左右中 入栈的话就是中右左 辅助数据结构:栈
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> result = new LinkedList<>();
Stack<TreeNode> st = new Stack<>();
if (root != null) st.push(root);
while (!st.empty()) {
TreeNode node = st.peek();
if (node != null) {
st.pop(); // 先弹把中点弹出去,后面跟右中左再加回来
st.push(node); // 添加中节点
st.push(null); // 中节点访问过,但是还没有处理,加入空节点做为标记。
if (node.right!=null) st.push(node.right); // 添加右节点(空节点不入栈)
if (node.left!=null) st.push(node.left); // 添加左节点(空节点不入栈)
} else { // 只有遇到空节点的时候,才将下一个节点放进结果集
st.pop(); // 将空节点弹出
node = st.peek(); // 重新取出栈中元素
st.pop();
result.add(node.val); // 加入到结果集
}
}
return result;
}
}
二叉树层序遍历
题目1:102. 二叉树的层序遍历
思路:用队列先进先出模拟层序遍历逻辑
1)递归法
2)迭代法
class Solution {
public List<List<Integer>> resList = new ArrayList<List<Integer>>();
public List<List<Integer>> levelOrder(TreeNode root) {
levelOrderByQueue(root);
return resList;
}
public void levelOrderByQueue(TreeNode node) {
if (node == null) return;
Queue<TreeNode> que = new LinkedList<TreeNode>();
que.offer(node);
while(!que.isEmpty()){
int size=que.size();
List<Integer> itemList = new ArrayList<Integer>();
while(size>0){
TreeNode tmpNode=que.poll();
itemList.add(tmpNode.val);
if(tmpNode.left!=null){
que.offer(tmpNode.left);
}
if(tmpNode.right!=null){
que.offer(tmpNode.right);
}
size--;
}
resList.add(itemList);
}
}
}
题目2:107. 二叉树的层序遍历 II
思路:在层序遍历的基础上翻转数组
class Solution {
List<List<Integer>> resList = new ArrayList<>();
public List<List<Integer>> levelOrderBottom(TreeNode root) {
//层序遍历
levelOrderByQueue(root);
//反转数组
List<List<Integer>> result = new ArrayList<>();
for (int i = resList.size() - 1; i >= 0; i-- ) {
result.add(resList.get(i));
}
return result;
}
public void levelOrderByQueue(TreeNode node) {
if (node == null) return;
Queue<TreeNode> que = new LinkedList<TreeNode>();
que.offer(node);
while(!que.isEmpty()){
int size=que.size();
List<Integer> itemList = new ArrayList<Integer>();
while(size>0){
TreeNode tmpNode=que.poll();
itemList.add(tmpNode.val);
if(tmpNode.left!=null){
que.offer(tmpNode.left);
}
if(tmpNode.right!=null){
que.offer(tmpNode.right);
}
size--;
}
resList.add(itemList);
}
}
}
题目3:199. 二叉树的右视图
思路:在层序遍历的基础上,单层遍历取最有一个值就是最右边的值
class Solution {
List<Integer> resList = new ArrayList<>();
public List<Integer> rightSideView(TreeNode root) {
//层序遍历
levelOrderByQueue(root);
return resList;
}
public void levelOrderByQueue(TreeNode node) {
if (node == null) return;
Queue<TreeNode> que = new LinkedList<TreeNode>();
que.offer(node);
while(!que.isEmpty()){
int size=que.size();
TreeNode tmpNode=null;
while(size>0){
tmpNode=que.poll();
if(tmpNode.left!=null){
que.offer(tmpNode.left);
}
if(tmpNode.right!=null){
que.offer(tmpNode.right);
}
size--;
}
resList.add(tmpNode.val);
}
}
}
题目4:637. 二叉树的层平均值
思路:在层序遍历的基础上,单层取平均值
class Solution {
List<Double> resList = new ArrayList<>();
public List<Double> averageOfLevels(TreeNode root) {
levelOrderByQueue(root);
return resList;
}
public void levelOrderByQueue(TreeNode node) {
if (node == null) return;
Queue<TreeNode> que = new LinkedList<TreeNode>();
que.offer(node);
while(!que.isEmpty()){
int size=que.size();
double levelSum = 0.0;
for (int i = 0; i < size; i++) {
TreeNode tmpNode=que.poll();
levelSum+=tmpNode.val;
if(tmpNode.left!=null){
que.offer(tmpNode.left);
}
if(tmpNode.right!=null){
que.offer(tmpNode.right);
}
}
resList.add(levelSum / size);
}
}
}
题目5:429. N 叉树的层序遍历
思路:在层序遍历的基础上,遍历children子节点
class Solution {
List<List<Integer>> resList = new ArrayList<>();
public List<List<Integer>> levelOrder(Node root) {
levelOrderByQueue(root);
return resList;
}
public void levelOrderByQueue(Node node) {
if (node == null) return;
Queue<Node> que = new LinkedList<Node>();
que.offer(node);
while(!que.isEmpty()){
int size=que.size();
List<Integer> itemList = new ArrayList<Integer>();
for (int i = 0; i < size; i++) {
Node tmpNode=que.poll();
itemList.add(tmpNode.val);
List<Node> children = tmpNode.children;
if (children == null || children.size() == 0) {
continue;
}
for (Node child : children) {
if (child != null) {
que.offer(child);
}
}
}
resList.add(itemList);
}
}
}
题目6:515. 在每个树行中找最大值
思路:在层序遍历的基础上,比较每一层的最大值
class Solution {
List<Integer> resList = new ArrayList<>();
public List<Integer> largestValues(TreeNode root) {
levelOrderByQueue(root);
return resList;
}
public void levelOrderByQueue(TreeNode node) {
if (node == null) return;
Queue<TreeNode> que = new LinkedList<TreeNode>();
que.offer(node);
while(!que.isEmpty()){
int size=que.size();
int maxValue = Integer.MIN_VALUE;
for(int i=0;i<size;i++){
TreeNode tmpNode = que.poll();
maxValue = Math.max(maxValue, tmpNode.val);
if(tmpNode.left!=null){
que.offer(tmpNode.left);
}
if(tmpNode.right!=null){
que.offer(tmpNode.right);
}
}
resList.add(maxValue);
}
}
}
题目7:116. 填充每个节点的下一个右侧节点指针
题目8:117. 填充每个节点的下一个右侧节点指针 II
思路:在层序遍历的基础上,做了很大改动,第一个节点单拿出来,因为最开始的next都是null值,所以每一层的null值不用额外设置,从第二个节点开始给next赋值
class Solution {
public Node connect(Node root) {
return levelOrderByQueue(root);
}
public Node levelOrderByQueue(Node node) {
Queue<Node> tmpQueue = new LinkedList<Node>();
if (node != null){
tmpQueue.offer(node);
}
while(!tmpQueue.isEmpty()){
int size=tmpQueue.size();
Node cur = tmpQueue.poll();
if (cur.left != null) tmpQueue.add(cur.left);
if (cur.right != null) tmpQueue.add(cur.right);
for(int i=1;i<size;i++){
Node next = tmpQueue.poll();
if (next.left != null) tmpQueue.add(next.left);
if (next.right != null) tmpQueue.add(next.right);
cur.next = next;
cur = next;
}
}
return node;
}
}
题目9:104. 二叉树的最大深度
思路:遍历完每一层后深度+1
class Solution {
public int maxDepth(TreeNode root) {
if (root == null) return 0;
Queue<TreeNode> que = new LinkedList<>();
que.offer(root);
int depth = 0;
while (!que.isEmpty())
{
int len = que.size();
while (len > 0)
{
TreeNode node = que.poll();
if (node.left != null) que.offer(node.left);
if (node.right != null) que.offer(node.right);
len--;
}
depth++;
}
return depth;
}
}
题目10:111.二叉树的最小深度
思路:遍历每层时,发现左右孩子都为空 返回深度
class Solution {
public int minDepth(TreeNode root){
if (root == null) {
return 0;
}
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
int depth = 0;
while (!queue.isEmpty()){
int size = queue.size();
depth++;
TreeNode cur = null;
for (int i = 0; i < size; i++) {
cur = queue.poll();
//如果当前节点的左右孩子都为空,直接返回最小深度
if (cur.left == null && cur.right == null){
return depth;
}
if (cur.left != null) queue.offer(cur.left);
if (cur.right != null) queue.offer(cur.right);
}
}
return depth;
}
}
二叉树递归法应用
重点:搞清楚每道题使用的前中后序遍历
二叉树前中后序递归法变种题型
题目1:226. 翻转二叉树
思路:前序和后序都行 中序会翻转后会导致第三层没翻转 可以想一下
1)递归法
class Solution {
public TreeNode invertTree(TreeNode root) {
if(root==null){
return root;
}
swap(root);
invertTree(root.left);
invertTree(root.right);
return root;
}
void swap(TreeNode root){
TreeNode temp=root.left;
root.left=root.right;
root.right=temp;
}
}
2)层序遍历 依靠队列
class Solution {
public TreeNode invertTree(TreeNode root) {
if (root == null) {return null;}
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()) {
TreeNode node = queue.poll();
swap(node);//中
if (node.right != null) queue.offer(node.right);//右
if (node.left != null) queue.offer(node.left);//左
}
return root;
}
void swap(TreeNode root){
TreeNode temp=root.left;
root.left=root.right;
root.right=temp;
}
}
题目2:101. 对称二叉树
思路:只能用后序遍历 因为需要返回是否对称的结果给中间节点 对于左子树来说是左右中 对于右子树来说是右左中
1)递归法
class Solution {
public boolean isSymmetric(TreeNode root) {
return compare(root.left, root.right);
}
boolean compare(TreeNode left,TreeNode right){
if(left==null&&right==null) return true;
if(left!=null&&right==null) return false;
if(left==null&&right!=null) return false;
if(left.val!=right.val) return false;
boolean outside=compare(left.left,right.right);
boolean inside=compare(left.right,right.left);
return outside&&inside;
}
}
2)迭代法 层序遍历 需借助队列
class Solution {
public boolean isSymmetric(TreeNode root) {
Queue<TreeNode> que=new LinkedList<>();
que.offer(root.left);
que.offer(root.right);
while(!que.isEmpty()){
TreeNode leftNode = que.poll();
TreeNode rightNode = que.poll();
if(leftNode==null&&rightNode==null) continue;
if(leftNode!=null&&rightNode==null) return false;
if(leftNode==null&&rightNode!=null) return false;
if(leftNode.val!=rightNode.val) return false;
que.offer(leftNode.left);
que.offer(rightNode.right);
que.offer(leftNode.right);
que.offer(rightNode.left);
}
return true;
}
}
题目3:617. 合并二叉树
思路:前中后序递归法变种题型的解决办法
1)递归法
class Solution {
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)迭代法
class Solution {
// 使用队列迭代
public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
if (root1 == null) return root2;
if (root2 ==null) return root1;
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root1);
queue.offer(root2);
while (!queue.isEmpty()) {
TreeNode node1 = queue.poll();
TreeNode node2 = queue.poll();
// 此时两个节点一定不为空,val相加
node1.val = node1.val + node2.val;
// 如果两棵树左节点都不为空,加入队列
if (node1.left != null && node2.left != null) {
queue.offer(node1.left);
queue.offer(node2.left);
}
// 如果两棵树右节点都不为空,加入队列
if (node1.right != null && node2.right != null) {
queue.offer(node1.right);
queue.offer(node2.right);
}
// 若node1的左节点为空,直接赋值
if (node1.left == null && node2.left != null) {
node1.left = node2.left;
}
// 若node1的右节点为空,直接赋值
if (node1.right == null && node2.right != null) {
node1.right = node2.right;
}
}
return root1;
}
}
题目4:104. 二叉树的最大深度
思路:用前中后序都可以
1)递归法 返回最大深度+1是因为加上根节点
class Solution {
public int maxDepth(TreeNode root) {
// 如果root为空,直接返回0
if (root == null) {
return 0;
}
int left= maxDepth(root.left);
int right=maxDepth(root.right);
return Math.max(left,right)+1;
}
}
2)迭代法 层序遍历借助队列
class Solution {
public int maxDepth(TreeNode root) {
if(root==null) return 0;
Queue<TreeNode> que=new LinkedList<>();
que.offer(root);
int depth = 0;
while(!que.isEmpty()){
int size=que.size();
depth++;
for(int i=0;i<size;i++){
TreeNode node= que.poll();
if(node.left!=null) que.offer(node.left);
if(node.right!=null) que.offer(node.right);
}
}
return depth;
}
}
题目5:111. 二叉树的最小深度
思路:这题有误区,最小深度是从根节点到最近叶子节点的最短路径上的节点数量 需要考虑左右子树是否为空的情况,如果左子树为空 最小深度=右子树最小深度+1 如果右子树为空 最小深度=左子树最小深度+1 如果左右子树都不为空 最小深度=左右子树深度最小值 + 1 。使用后序遍历方式
1)递归法
class Solution {
public int minDepth(TreeNode root) {
if(root==null){
return 0;
}
int left=minDepth(root.left);
int right=minDepth(root.right);
//中
if (root.left == null) {
return right + 1;
}
if (root.right == null) {
return left + 1;
}
// 左右结点都不为null
return Math.min(left, right) + 1;
}
}
2)迭代法 层序遍历借助队列
class Solution {
public int minDepth(TreeNode root) {
if (root == null) {
return 0;
}
Deque<TreeNode> que = new LinkedList<>();
que.offer(root);
int depth = 0;
while (!que.isEmpty()) {
int size = que.size();
depth++;
for (int i = 0; i < size; i++) {
TreeNode node = que.poll();
if (node.left == null && node.right == null) {
// 是叶子结点,直接返回depth,因为从上往下遍历,所以该值就是最小值
return depth;
}
if (node.left != null) {
que.offer(node.left);
}
if (node.right != null) {
que.offer(node.right);
}
}
}
return depth;
}
}
题目6:222. 完全二叉树的节点个数
思路:
1)递归法
class Solution {
public int countNodes(TreeNode root) {
if(root==null) return 0;
//统计左子树的数量
int left= countNodes(root.left);
//统计右子树的数量
int right= countNodes(root.right);
return left+right+1;
}
}
2)迭代法 层序遍历借助队列
class Solution {
public int countNodes(TreeNode root) {
if(root==null) return 0;
Queue<TreeNode> que=new LinkedList<>();
que.offer(root);
int length=1;
while(!que.isEmpty()){
int size=que.size();
for(int i=0;i<size;i++){
TreeNode node= que.poll();
if(node.left!=null){
que.offer(node.left);
length++;
}
if(node.right!=null){
que.offer(node.right);
length++;
}
}
}
return length;
}
}
二叉树前中后序递归法变种再变种题型
题目7:110. 平衡二叉树
思路:在求二叉树高度的基础上 如果高度差<=1并且左子树和右子树都为平衡二叉树
1)递归法
class Solution {
public boolean isBalanced(TreeNode root) {
//采用后序遍历
if(root == null){
return true;
}
int res = getHeight(root.left) > getHeight(root.right) ? getHeight(root.left) - getHeight(root.right) : getHeight(root.right) - getHeight(root.left);
//容易出错:高度差小于1 并且左右子树都要是平衡二叉树
if(res <= 1&&isBalanced(root.left)&&isBalanced(root.right)){
return true;
}else{
return false;
}
}
public int getHeight(TreeNode root){
if(root == null){
return 0;
}else{
int leftHeight = getHeight(root.left);
int rightHeight = getHeight(root.right);
return Math.max(leftHeight, rightHeight) + 1;
}
}
}
题目8:257. 二叉树的所有路径
思路:遍历二叉树节点的基础上通过SpringBuilder拼上每个节点
1)递归法
class Solution {
List<String> res=new ArrayList<>();
public List<String> binaryTreePaths(TreeNode root) {
if(root==null) return res;
getPaths(root,"");
return res;
}
public void getPaths(TreeNode root,String path){
if(root==null) return;
StringBuilder sb=new StringBuilder(path);
sb.append(root.val);
//叶子节点要收集
if(root.left==null&&root.right==null){
res.add(sb.toString());
}else{
sb.append("->");
getPaths(root.left,sb.toString());
getPaths(root.right,sb.toString());
}
}
}
题目9:404. 左叶子之和
思路:
1)递归法
class Solution {
int result = 0;
public int sumOfLeftLeaves(TreeNode root) {
if(root==null)return 0;
if(root.left!=null&&root.left.left==null&&root.left.right==null){
result+=root.left.val;
}
sumOfLeftLeaves(root.left);
sumOfLeftLeaves(root.right);
return result;
}
}
二叉树回溯I题型
题目10:513. 找树左下角的值
思路:
1)递归法 最后一行就是找到深度最大的叶子结点,保证优先左边搜索,然后记录深度最大的叶子节点,此时就是树的最后一行最左边的值
class Solution {
private int maxDeep = -1;
private int leftValue = 0;
public int findBottomLeftValue(TreeNode root) {
findLeftValue(root,0);
return leftValue;
}
void findLeftValue(TreeNode root,int deep){
if (root == null) return;
if (root.left == null && root.right == null) {
if (deep > maxDeep) {
leftValue = root.val;
maxDeep = deep;
}
}
if (root.left != null){
deep++;
findLeftValue(root.left,deep);
deep--;
}
if (root.right != null){
deep++;
findLeftValue(root.right,deep);
deep--;
}
}
}
- 迭代法 层序遍历 借助队列 这道题层序遍历相对简单 只要找到最后一层的第一个元素即可,所以在层序遍历的基础上加了一个判断
if(i==0){result=node.val;}
class Solution {
int result=0;
public int findBottomLeftValue(TreeNode root) {
if(root==null){
return 0;
}
returnBottomLeftValue(root);
return result;
}
void returnBottomLeftValue(TreeNode root){
if(root==null) return;
Queue<TreeNode>que=new LinkedList<>();
que.offer(root);
while(!que.isEmpty()){
int size=que.size();
for(int i=0;i<size;i++){
TreeNode node= que.poll();
if(i==0){result=node.val;}
if(node.left!=null) que.offer(node.left);
if(node.right!=null) que.offer(node.right);
}
}
}
}
题目11:112. 路径总和
思路:
1)递归法 总和-遇到的每个节点值 如果遇到叶子节点总和等于0说明找到路径
class Solution {
public boolean hasPathSum(TreeNode root, int targetSum) {
if(root==null) return false;
targetSum-=root.val;
if(root.left==null && root.right==null && targetSum==0) return true;
if(root.left!=null){
boolean leftResult= hasPathSum(root.left,targetSum);
if(leftResult) return true;
}
if(root.right!=null){
boolean rightResult= hasPathSum(root.right,targetSum);
if(rightResult) return true;
}
return false;
}
}
- 迭代法 借助两个队列 一个存节点 一个存节点加起来的值(不能替换成累加值 会把整个的节点加起来,用队列这种可以保证左树加左树的和 右树加右树的和)
class Solution {
public boolean hasPathSum(TreeNode root, int targetSum) {
if(root == null) return false;
Queue<TreeNode> q1 = new LinkedList<>();
Queue<Integer> q2 = new LinkedList<>();
q1.offer(root);
q2.offer(root.val);
while(!q1.isEmpty()) {
int size = q1.size();
for(int i = 0; i < size; i++) {
TreeNode node = q1.poll();
int sum = q2.poll();
if(node.left == null && node.right == null && sum == targetSum) {
return true;
}
if(node.right != null){
q1.offer(node.right);
q2.offer(sum + node.right.val);
}
if(node.left != null) {
q1.offer(node.left);
q2.offer(sum + node.left.val);
}
}
}
return false;
}
}
题目12:113. 路径总和 II
思路:
1)递归法 与路径总和提一样 就是多了个记录路径的变量
注意 result.add(new ArrayList(path)); 只添加path 最后结果是空
class Solution {
List<List<Integer>> result=new ArrayList<>();
List<Integer> path=new ArrayList<>();
public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
getPathSum(root,targetSum);
return result;
}
void getPathSum(TreeNode root, int targetSum){
if(root==null) return;
targetSum-=root.val;
path.add(root.val);
if(root.left==null && root.right==null && targetSum==0){
//注意
result.add(new ArrayList<Integer>(path));
}
if(root.left!=null){
getPathSum(root.left,targetSum);
//回溯
path.remove(path.size() - 1);
}
if(root.right!=null){
getPathSum(root.right,targetSum);
//回溯
path.remove(path.size() - 1);
}
}
}
二叉树构造I题型
题目13:106. 从中序与后序遍历序列构造二叉树
思路:
第一步:如果数组大小为零的话,说明是空节点了
第二步:如果不为空,那么取后序数组最后一个元素作为节点元素
第三步:找到后序数组最后一个元素在中序数组的位置,作为切割点
第四步:切割中序数组,切成中序左数组和中序右数组 (顺序别搞反了,一定是先切中序数组)
第五步:切割后序数组,切成后序左数组和后序右数组
第六步:递归处理左区间和右区间
1)递归法
class Solution {
Map<Integer, Integer> map=new HashMap<>();
public TreeNode buildTree(int[] inorder, int[] postorder) {
for (int i = 0; i < inorder.length; i++) {
// 用map保存中序序列的数值对应位置
map.put(inorder[i], i);
}
return findNode(inorder, 0, inorder.length, postorder,0, postorder.length);
}
public TreeNode findNode(int[] inorder, int inBegin, int inEnd, int[] postorder, int postBegin, int postEnd) {
// 参数里的范围都是前闭后开 防止越界
if (inBegin >= inEnd || postBegin >= postEnd) {
return null;
}
// 1.找到中间节点,后序数组最后一个元素
// 2.找到中序切割点 rootIndex
int rootIndex = map.get(postorder[postEnd - 1]);
// 构造结点
TreeNode root = new TreeNode(inorder[rootIndex]);
// 中序左子树个数
int lenOfLeft = rootIndex - inBegin;
//中序左数组, 后序左数组
root.left = findNode(inorder, inBegin, rootIndex,postorder, postBegin, postBegin + lenOfLeft);
//中序右数组, 后序右数组
root.right = findNode(inorder, rootIndex + 1, inEnd,postorder, postBegin + lenOfLeft, postEnd - 1);
return root;
}
}
题目14:105. 从前序与中序遍历序列构造二叉树
思路:与上题思路一样
1)递归法
class Solution {
Map<Integer, Integer> map=new HashMap<>();
public TreeNode buildTree(int[] preorder, int[] inorder) {
for (int i = 0; i < inorder.length; i++) {
// 用map保存中序序列的数值对应位置
map.put(inorder[i], i);
}
return findNode(inorder, 0, inorder.length, preorder,0, preorder.length);
}
public TreeNode findNode(int[] inorder, int inBegin, int inEnd, int[] preorder, int preBegin, int preEnd) {
// 参数里的范围都是前闭后开 防止越界
if (inBegin >= inEnd || preBegin >= preEnd) {
return null;
}
// 1.找到中间节点,前序数组第一个元素
// 2.找到中序切割点 rootIndex
int rootIndex = map.get(preorder[preBegin]);
// 构造结点
TreeNode root = new TreeNode(inorder[rootIndex]);
// 中序左子树个数
int lenOfLeft = rootIndex - inBegin;
//中序左数组, 前序左数组
root.left = findNode(inorder, inBegin, rootIndex,preorder, preBegin+1, preBegin + lenOfLeft+1);
//中序右数组, 前序右数组
root.right = findNode(inorder, rootIndex + 1, inEnd,preorder, preBegin + lenOfLeft+1, preEnd);
return root;
}
}
题目15:105. 从前序与中序遍历序列构造二叉树
思路:与上题思路一样
1)递归法
class Solution {
Map<Integer, Integer> map=new HashMap<>();
public TreeNode buildTree(int[] preorder, int[] inorder) {
for (int i = 0; i < inorder.length; i++) {
// 用map保存中序序列的数值对应位置
map.put(inorder[i], i);
}
return findNode(inorder, 0, inorder.length, preorder,0, preorder.length);
}
public TreeNode findNode(int[] inorder, int inBegin, int inEnd, int[] preorder, int preBegin, int preEnd) {
// 参数里的范围都是前闭后开 防止越界
if (inBegin >= inEnd || preBegin >= preEnd) {
return null;
}
// 1.找到中间节点,前序数组第一个元素
// 2.找到中序切割点 rootIndex
int rootIndex = map.get(preorder[preBegin]);
// 构造结点
TreeNode root = new TreeNode(inorder[rootIndex]);
// 中序左子树个数
int lenOfLeft = rootIndex - inBegin;
//中序左数组, 前序左数组
root.left = findNode(inorder, inBegin, rootIndex,preorder, preBegin+1, preBegin + lenOfLeft+1);
//中序右数组, 前序右数组
root.right = findNode(inorder, rootIndex + 1, inEnd,preorder, preBegin + lenOfLeft+1, preEnd);
return root;
}
}
题目16:654. 最大二叉树
思路:和上题思路一样有些变种 找到每轮递归最大值的下标
1)递归法
lass Solution {
public TreeNode constructMaximumBinaryTree(int[] nums) {
return findNode(nums,0,nums.length);
}
public TreeNode findNode(int[] nums,Integer begin, Integer end) {
// 没有元素了
if (end - begin < 1) {
return null;
}
Map<Integer, Integer> map=new HashMap<>();
int max=-1;
for (int i = begin; i < end; i++) {
// 用map保存中序序列的数值对应位置
map.put(nums[i], i);
if(nums[i]>max){
max=nums[i];
}
}
TreeNode root = new TreeNode(max);
Integer index=map.get(max);
root.left=findNode(nums,begin,index);
root.right=findNode(nums,index+1,end);
return root;
}
}
二叉搜索树模版变种题型
题目17:700. 二叉搜索树中的搜索
思路:针对二叉搜索树的题目,一样要利用其特性
1)递归法 利用二叉搜索树的特性 大于往右子树搜索 小于往左子树搜索
class Solution {
public TreeNode searchBST(TreeNode root, int val) {
if (root == null || root.val == val) return root;
TreeNode result=null;
if(val<root.val){
result=searchBST(root.left, val);
}
if(val>root.val){
result=searchBST(root.right, val);
}
return result;
}
}
2)迭代法 不需要队列(广度优先搜索)和栈(深度优先搜索)因为节点有序
普通二叉树需要有回溯过程 而二叉搜索树因为本身就是有序的所以不需要回溯过程
class Solution {
public TreeNode searchBST(TreeNode root, int val) {
while (root != null && root.val != val) {
root = root.val < val ? root.right : root.left;
}
return root;
}
}
题目18:98. 验证二叉搜索树
思路:左子树所有节点小于中间节点,右子树所有节点大于中间节点,利用中序,整个节点是递增的
陷阱1:不能单纯的比较左节点小于中间节点,右节点大于中间节点就完事了,例如:[10,5,15,null,null,6,20],节点10大于左节点5,小于右节点15,但右子树里出现了一个6 这就不符合了!
1)递归法 中序遍历保证递增,始终与前一个节点进行比较就行
class Solution {
TreeNode pre=null;
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;
}
}
题目19:530. 二叉搜索树的最小绝对差
思路:模版变种题 借助前一个节点
1)递归法
class Solution {
TreeNode pre=null;
int result = Integer.MAX_VALUE;
public int getMinimumDifference(TreeNode root) {
if(root==null) return 0;
int left=getMinimumDifference(root.left);
if(pre!=null){
result = Math.min(result,root.val-pre.val);
}
pre=root;
getMinimumDifference(root.right);
return result;
}
}
题目20:501. 二叉搜索树中的众数
思路:模版变种题 借助前一个节点
1)递归法 中序遍历
class Solution {
ArrayList<Integer> resList=new ArrayList<>();
int maxCount=0;
int count=0;
TreeNode pre=null;
public int[] findMode(TreeNode root) {
findMode1(root);
int[] res = new int[resList.size()];
for (int i = 0; i < resList.size(); i++) {
res[i] = resList.get(i);
}
return res;
}
public void findMode1(TreeNode root) {
if (root == null) {
return;
}
findMode1(root.left);
int rootValue = root.val;
// 计数
if (pre == null || rootValue != pre.val) {
count = 1;
} else {
count++;
}
// 更新结果以及maxCount
if (count > maxCount) {
resList.clear();
resList.add(rootValue);
maxCount = count;
} else if (count == maxCount) {
resList.add(rootValue);
}
pre = root;
findMode1(root.right);
}
}
二叉搜索树回溯II题型
题目21:236. 二叉树的最近公共祖先
思路:
1)递归法 后序遍历
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if (root == null || root == p || root == q) { // 递归结束条件
return root;
}
// 后序遍历
TreeNode left = lowestCommonAncestor(root.left, p, q);
TreeNode right = lowestCommonAncestor(root.right, p, q);
if(left == null && right == null) {
return null;
}else if(left == null && right != null) {
return right;
}else if(left != null && right == null) {
return left;
}else {
return root;
}
}
}
题目22:235. 二叉搜索树的最近公共祖先
思路:利用搜索二叉树有序特性,从上向下去递归遍历,第一次遇到 cur节点是数值在[p, q]区间中,那么cur就是 p和q的最近公共祖先。前中后序都可
1)递归法
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if (root.val > p.val && root.val > q.val){
return lowestCommonAncestor(root.left, p, q);
}
if (root.val < p.val && root.val < q.val){
return lowestCommonAncestor(root.right, p, q);
}
return root;
}
}
二叉搜索树构造II题型
题目23:701. 二叉搜索树中的插入操作
思路:利用二叉搜索树有序特性 不用遍历整棵树
1)递归法
class Solution {
public TreeNode insertIntoBST(TreeNode root, int val) {
if (root == null) return new TreeNode(val);
if (root.val < val){
root.right = insertIntoBST(root.right, val);
}else if (root.val > val){
root.left = insertIntoBST(root.left, val);
}
return root;
}
}
题目24:450. 删除二叉搜索树中的节点
思路:
1.没找到删除的节点
遍历到空节点直接返回
2.找到删除的节点
1)左右孩子都为空(叶子节点),直接删除节点, 返回NULL为根节点
2) 删除节点的左孩子为空,右孩子不为空,删除节点,右孩子补位,返回右孩子为根节点
3)删除节点的右孩子为空,左孩子不为空,删除节点,左孩子补位,返回左孩子为根节点
4) 左右孩子节点都不为空,则将删除节点的左子树头结点(左孩子)放到删除节点的右子树的最左面节点的左孩子上,返回删除节点右孩子为新的根节点
1)递归法
class Solution {
public TreeNode deleteNode(TreeNode root, int key) {
//1.没找到删除的节点,遍历到空节点直接返回
if (root == null) return root;
//2.找到删除的节点
if (root.val == key) {
if (root.left == null) {
//1)删除节点的左孩子为空,右孩子不为空
return root.right;
} else if (root.right == null) {
//2)删除节点的右孩子为空,左孩子不为空
return root.left;
} else {
//3)左右孩子节点都不为空,则将删除节点的左子树头结点(左孩子)放到删除节点的右子树的最左面节点的左孩子上,返回删除节点右孩子为新的根节点
TreeNode cur = root.right;
while (cur.left != null) {
cur = cur.left;
}
cur.left = root.left;
root = root.right;
return root;
}
}
if (root.val > key) root.left = deleteNode(root.left, key);
if (root.val < key) root.right = deleteNode(root.right, key);
return root;
}
}
题目25:669. 修剪二叉搜索树
思路:
1)递归法
class Solution {
public TreeNode trimBST(TreeNode root, int low, int high) {
if (root == null) {
return null;
}
if (root.val < low) {
return trimBST(root.right, low, high);
}
if (root.val > high) {
return trimBST(root.left, low, high);
}
// root在[low,high]范围内
root.left = trimBST(root.left, low, high);
root.right = trimBST(root.right, low, high);
return root;
}
}
题目26:108. 将有序数组转换为二叉搜索树
思路:本质就是寻找分割点,分割点作为当前节点,然后递归左区间和右区间。
1)递归法 与最大二叉树很像
class Solution {
public TreeNode sortedArrayToBST(int[] nums) {
return sortedArrayToBST(nums, 0, nums.length);
}
public TreeNode sortedArrayToBST(int[] nums, int left, int right) {
if (left >= right) {
return null;
}
int mid = left + (right - left) / 2;
TreeNode root = new TreeNode(nums[mid]);
root.left = sortedArrayToBST(nums, left, mid);
root.right = sortedArrayToBST(nums, mid + 1, right);
return root;
}
}
题目27:538. 把二叉搜索树转换为累加树
思路:按右中左遍历累加
class Solution {
int sum;
public TreeNode convertBST(TreeNode root) {
sum = 0;
convertBST1(root);
return root;
}
// 按右中左顺序遍历,累加即可
public void convertBST1(TreeNode root) {
if (root == null) {
return;
}
convertBST1(root.right);
sum += root.val;
root.val = sum;
convertBST1(root.left);
}
}