二叉树
前后中遍历指的是中间节点遍历的顺序,使用栈来实现
递归算法考虑三要素
- 确定递归函数的参数和返回值
- 确定终止条件
- 确定单层递归的逻辑
前序遍历(递归)
递归是调用自身函数,代码量少但可能会出现栈溢出;
public class FirstTest {
static List<Integer> result=new ArrayList();
public static List<Integer> preorderTraversal(TreeNode root) {
if(root == null){
return new ArrayList<>();
}
result.add(root.val);
preorderTraversal(root.left);
preorderTraversal(root.right);
return result;
}
}
迭代(借助栈)
迭代是重复执行某一段代码,代码量多但没有额外的内存消耗。
public static List<Integer> preorderTraversal(TreeNode root){
List<Integer> result=new ArrayList<>();
if (root == null){
return result;
}
Stack<TreeNode> stack = new Stack<>();
stack.push(root);
while (!stack.isEmpty()){
TreeNode node = stack.pop();
result.add(node.val);
if (node.right!=null){
stack.push(node.right);
}
if (node.left!=null){
stack.push(node.left);
}
}
return result;
中序遍历(迭代)⭐
与前序的迭代不同在于: 中序一直要访问到树的最左边,当左边的节点是空时再出栈并将当前出栈节点的右节点入栈。
cur代表当前进行判断的节点,而不是cur.left
public static List<Integer> inorderTraversal(TreeNode root) {
List<Integer> result=new ArrayList<>();
if (root == null){
return result;
}
TreeNode cur=root;
Stack<TreeNode> stack = new Stack<>();
while (!stack.isEmpty() || cur != null){
if (cur!=null){
stack.push(cur);
cur=cur.left;
}else {
TreeNode pop = stack.pop();
result.add(pop.val);
cur=pop.right;
}
}
return result;
}
后续遍历
将前序的迭代稍微修改一下,将左右节点入栈的顺序调转,最后结果逆转。
public static List<Integer> postorderTraversal(TreeNode root){
List<Integer> result=new ArrayList<>();
Stack<TreeNode> stack = new Stack<>();
stack.push(root);
while (!stack.isEmpty()){
TreeNode peek = stack.pop();
result.add(peek.val);
if (peek.left!=null){
stack.push(peek.left);
}
if (peek.right!=null){
stack.push(peek.right);
}
}
Collections.reverse(result);
return result;
}
总结:
- 迭代法——前中后遍历的思路都是一样的,先判断root==null,然后根据顺序进行左右子树迭代;
- 递归法——后序在前序的基础上改了左右节点入栈顺序,中序使用cur指向当前位置,即使stack==null,若cur!=null,也可以继续循环执行代码,在循环中,对cur==null和cur!=null进分别进行相应的代码逻辑执行。
层次遍历(借助队列)
使用了迭代法,关键点在于len,记录每层的节点个数。
public static List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> resList=new ArrayList<>();
if (root == null){
return resList;
}
Queue<TreeNode> queue=new LinkedList<>();
queue.offer(root);
while ((!queue.isEmpty())){
List<Integer> itemList=new ArrayList<>();
int len =queue.size();
while (len>0){
TreeNode tmpNode=queue.poll();
itemList.add(tmpNode.val);
if (tmpNode.left!=null){
queue.offer(tmpNode.left);
}
if (tmpNode.right!=null){
queue.offer(tmpNode.right);
}
len--;
}
resList.add(itemList);
}
return resList;
}
总结:
- 队列的增加和移除方法为
offer(),poll();双端队列的增加和移除方法是add(),remove() - 栈是
push()和pop()
翻转二叉树
使用前序遍历,遍历到左右子树的时候swap。
public TreeNode invertTree(TreeNode root) {
if (root==null) return root;
Stack<TreeNode> stack=new Stack<>();
stack.push(root);
while (!stack.isEmpty()){
TreeNode node = stack.pop();
swap(node);
if (node.left!=null){
stack.push(node.left);
}
if(node.right!=null){
stack.push(node.right);
}
}
return root;
}
public void swap(TreeNode node){
TreeNode temp=node.left;
node.left=node.right;
node.right=temp;
}
⭐重点:交换时要将root传入而不是两个子节点,传入子节点相当于新建了两个局部变量,只改变了这两个局部变量,没有改变结构。
二叉树是否对称
需要对左右节点是否为null进行判断
public boolean isSymmetric(TreeNode root) {
return isEqual(root.left,root.right);
}
public boolean isEqual(TreeNode left,TreeNode right){
if(left==null && right==null){
return true;
}else if(left==null || right ==null){
return false;
}
if(left.val!=right.val){
return false;
}
return isEqual(left.left,right.right) && isEqual(left.right,right.left);
}
最大深度
当变量定义为局部变量时,每次递归都会把结果存到对应栈层里,这样才有效,如果是全局变量则会被覆盖。
public static int maxDepth(TreeNode root) {
if (root ==null) return 0;
int l_depth=maxDepth(root.left)+1;
int r_depth=maxDepth(root.right)+1;
return Math.max(l_depth,r_depth);
}
二叉树的最小深度(节点数)⭐
二叉树的最小深度不会因为左子树为空就是1,当左子树为空时取右子树的深度+1
如果左子树为null,直接判断右子树;如果右子树为null,直接判断左子树;若两个子树都不为null,则都判断取最小值
public int minDepth(TreeNode root) {
if (root==null) return 0;
if (root.left==null) {
return minDepth(root.right)+1;
}else if (root.right==null){
return minDepth(root.left)+1;
}
int l_legth=minDepth(root.left);
int r_legth=minDepth(root.right);
return Math.min(l_legth,r_legth)+1;
}
平衡二叉树
获得左右子树的高度差,如果高度差大于1直接递归向上返回,如果高度差小于=1,返回该节点的最大长度。
public boolean isBalanced(TreeNode root) {
if(root ==null){
return true;
}
return Depth(root)!=-1;
}
public Integer Depth(TreeNode node){
int depthL=0;
int depthR=0;
if(node.left!=null){
depthL=Depth(node.left);
}
if(node.right!=null){
depthR=Depth(node.right);
}
if(Math.abs(depthL-depthR)>1|| depthL==-1 || depthR==-1){
return -1;
}
return Math.max(depthL,depthR)+1;
}
二叉树的所有路径⭐
别想着直接用StringBuffer,搞不明白,用ArrayList存结果,然后进行转换
逻辑:终止条件是左右节点都为null
单层逻辑是将当前节点的值存入到list中
回溯对左右节点分别是否为null进行判断,如果不为null各自执行完后都需要回溯,必须要有判断
class Solution {
List<String> result=new ArrayList<>();
List<Integer> temp=new ArrayList<>();
public List<String> binaryTreePaths(TreeNode root) {
PathNode(root);
return result;
}
public void PathNode(TreeNode root){
//单层处理逻辑
if (root==null){
return;
}
temp.add(root.val);
//终止条件
if (root.left==null && root.right==null){
//将路径传入结果
StringBuffer sb=new StringBuffer();
for (int i = 0; i < temp.size()-1; i++) {
sb.append(temp.get(i)+"->");
}
sb.append(temp.get(temp.size()-1));
result.add(sb.toString());
return;
}
if (root.left!=null){
PathNode(root.left);
temp.remove(temp.size()-1);
}
if (root.right!=null){
PathNode(root.right);
temp.remove(temp.size()-1);
}
}
}
二叉树的最近公共祖先
分情况讨论:如果根节点为空或者p或q,直接返回root;
如果递归左右子树得到的结果都不为null,也返回root;
如果左子树结果不为null右子树为null,最近公共祖先是左节点,反过来为右节点
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 root;
}else if(left !=null){
return left;
}
return right;
}
完全二叉树的节点个数
没有结合完全二叉树的特点,而是使用了普遍适用的


浙公网安备 33010602011771号