二叉树遍历

你好呀~我是陌陌❀❀❀

1. 二叉树概述

二叉树(Binary tree)是树形结构的一个重要类型。许多实际问题抽象出来的数据结构往往是二叉树形式,即使是一般的树也能简单地转换为二叉树,而且二叉树的存储结构及其算法都较为简单,因此二叉树显得特别重要。二叉树特点是每个节点最多只能有两棵子树,且有左右之分 。

二叉树是n个有限元素的集合,该集合或者为空、或者由一个称为根(root)的元素及两个不相交的、被分别称为左子树和右子树的二叉树组成,是有序树。当集合为空时,称该二叉树为空二叉树。在二叉树中,一个元素也称作一个节点

2. 二叉树遍历

2.1 递归实现

/**
  * 递归实现前、中、后序的遍历
  * @param head
  */
 public static void f(Node head){
     if(head == null){
         return;
     }
     //1. 先序
     f(head.left);
     //2. 中序
     f(head.right);
     //3. 后序
 }

2.2 非递归实现

一切递归都可以转换为非递归!!!

2.2.1 前序遍历

/**
 * 非递归实现先序遍历
 * @param head
 */
public static void preOrderUnRecur(Node head){
    if(head != null){
        Stack<Node> stack = new Stack<Node>();
        stack.add(head);
        while(!stack.isEmpty()){
            head = stack.pop();
            System.out.println(head.value+" ");
            if(head.right != null){
                stack.add(head.right);
            }if(head.left != null){
                stack.push(head.left);
            }
        }
    }
}

2.2.2 中序遍历

/**
 * 非递归实现中序遍历
 * @param head
 */
public static void inOrderUnRecur(Node head){
    if(head != null){
        Stack<Node> stack = new Stack<>();
        while(!stack.isEmpty() || head != null){
            if(head != null){
                stack.push(head);
                head = head.left;
            }else{
                head = stack.pop();
                System.out.println(head.value + " ");
                head = head.right;
            }

        }
    }
}

2.2.3 后续遍历

/**
 * 非递归实现后续遍历
 */
public static void posOrderUnRecur1(Node head){
    if(head == null){
        return;
    }
    Stack<Node> stack1 =new  Stack<>();
    Stack<Node> stack2 =new  Stack<>();
    stack1.add(head);
    while(!stack1.isEmpty()){
        head = stack1.pop();
        stack2.push(head);
        if(head.left != null){
            stack1.push(head.left);
        }
        if(head.right != null){
            stack1.push(head.right);
        }
    }
    while(!stack2.isEmpty()){
        System.out.println(stack2.pop().value +" ");
    }
}

2.3 广度优先遍历

/**
* 广度优先遍历
 * @param head
 */
public static void w(Node head){
    if(head == null){
        return;
    }
    Queue<Node> queue = new LinkedList<Node>();
    queue.add(head);
    while(!queue.isEmpty()){
        Node cur = queue.poll();
        System.out.println(cur.value);
        if(cur.left != null){
            queue.add(cur.left);
        }
        if(cur.right != null){
            queue.add(cur.right);
        }
    }
}

3. 相关练习

3.1 练习

练习一:二叉树层结点最大数

计算一颗二叉树某层中结点个数最大的个数和
和下一题的区别在于,我计算的是每一层的结点数目,然后返回结点数最大值就可以了,而下一道题是最大的宽度,换句话说计算的是每层第一个结点和最后一个结点的索引差

//计算一颗二叉树中某一层中结点个数最大的数字
public static int w1(Node head){
    if(head == null){
        return 0;
    }
    Queue<Node> queue = new LinkedList<Node>();
    queue.add(head);
    HashMap<Node,Integer> levelMap = new HashMap<>();
    levelMap.put(head,1);
    int curLevel = 1;
    int curLevelNods = 0;
    int max = Integer.MIN_VALUE;
    while(!queue.isEmpty()){
        Node cur = queue.poll();
        int curNodeLevel = levelMap.get(cur);
        if(curNodeLevel == curLevel){
            curLevelNods++;
        }else{
            max = Math.max(max,curLevelNods);
            curLevel++;
            curLevelNods=1;
        }
        if(cur.left != null){
            levelMap.put(cur.left,curNodeLevel+1);
            queue.add(cur.left);
        }
        if(cur.right != null){
            levelMap.put(cur.right,curNodeLevel+1);
            queue.add(cur.right);
        }
    }
    return max;
}

练习二:最大宽度

给你一棵二叉树的根节点 root ,返回树的 最大宽度 。
树的 最大宽度 是所有层中最大的 宽度 。
每一层的 宽度 被定义为该层最左和最右的非空节点(即,两个端点)之间的长度。将这个二叉树视作与满二叉树结构相同,两端点间会出现一些延伸到这一层的 null 节点,这些 null 节点也计入长度。
题目数据保证答案将会在 32 位 带符号整数范围内。

原题链接

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode 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 int widthOfBinaryTree(TreeNode head) {
      //1. head二叉树有至少一个结点
      int res = 1;
      List<Pair<TreeNode,Integer>> arr = new ArrayList<Pair<TreeNode,Integer>>();
      arr.add(new Pair<TreeNode,Integer>(head,1));
      while(!arr.isEmpty()){
          List<Pair<TreeNode,Integer>> tmp = new ArrayList<Pair<TreeNode,Integer>>();
          for(Pair<TreeNode,Integer> pair : arr){
              TreeNode node = pair.getKey();
              int index = pair.getValue();
              if(node.left != null){
                  tmp.add(new Pair<TreeNode,Integer>(node.left,index * 2));
              }
              if(node.right != null){
                  tmp.add(new Pair<TreeNode,Integer>(node.right ,index * 2 + 1));
              }
          }
          res = Math.max(res,arr.get(arr.size() - 1).getValue() - arr.get(0).getValue() + 1);
          arr = tmp;
      }
      return res;
    }
}

练习三:搜索二叉树

如何判断一颗二叉树是否是搜索树:

搜索树:若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值

/**
 * 判断是否是搜索二叉树
 * @return
 */
public static boolean  checkBST(Node head){
    if(head == null){
        return true;
    }
    boolean isLeftBST = checkBST(head.left);
    if(!isLeftBST){
        return false;
    }
    if(head.value <= preValue){
        return false;
    }else{
        preValue = head.value;
    }
    return checkBST(head.right);
}

练习四 :完全二叉树

如何判断一颗树是否是完全二叉树

完全二叉树:一棵深度为k的有n个结点的二叉树,对树中的结点按从上至下、从左到右的顺序进行编号,如果编号为i(1≤i≤n)的结点与满二叉树中编号为i的结点在二叉树中的位置相同,则这棵二叉树称为完全二叉树

 /**
 * 判断一颗二叉树是否是完全二叉树
 */
 //使用宽度优先遍历,
 //1. 遇到的任何一个结点只有右孩子没有左孩子,淘汰
 //2. 如果遇到了第一个左右两个孩子不双全的情况,接下来遇到的所有结点都必须是叶子结点
public static boolean isCBT(Node head){
    if(head == null){
        return true;
    }
    LinkedList<Node> queue = new LinkedList<>();
    //是否遇到过左右两个孩子不双全的结点
    boolean leaf = false;
    Node l = null;
    Node  r = null;
    while(!queue.isEmpty()){
        head = queue.poll();
        l = head.left;
        r = head.right;
        if(
                (leaf && (l != null || r != null))
                ||
                (l == null && r != null)
        ){
            return false;
        }
        if(l != null){
            queue.add(l);
        }
        if(r != null){
            queue.add(r);
        }
        if( l == null || r == null){
            leaf = true;
        }
    }
        return true;
}

练习五 :满二叉树

如何判断一颗树是否是满二叉树

满二叉树:除最后一层无任何子节点外,每一层上的所有结点都有两个子结点的二叉树。

/**
 * 判断一颗树是否是满二叉树
 */
//1. 方法一:先求二叉树的最大深度,再求树中结点个数
//2. 方法二:按套路来~👇
public static class Info{
    public int height;
    public int nodes;
    public Info(int hei,int nods){
        height = hei;
        nodes = nods;
    }
}
public static Info fB(Node head){
    if(head == null){
        return new Info(0,0);
    }
    Info l = fB(head.left);
    Info r = fB(head.right);
    int height = Math.max(l.height,r.height) + 1;
    int nodes = l.nodes + r.nodes + 1;
    return new Info(height,nodes);
}

public static boolean isF(Node head){
    if(head == null){
        return  true;
    }
    Info f = fB(head);
    return f.nodes == f.height << 1 + 1;
}

练习六:平衡二叉树

如何判断一颗二叉树是平衡二叉树

平衡二叉树:平衡二叉树,又称AVL树,用于解决二叉排序树高度不确定的情况,如果二叉排序树的子树间的高度相差太大,就会让二叉排序树操作的时间复杂度升级为O(n),为了避免这一情况,为最坏的情况做准备,就出现了平衡二叉树,使树的高度尽可能的小,其本质还是一棵二叉搜索树。

 /**
 * 判断一颗树是否是平衡二叉树
 * 对于任何一个子树来说,它左树的高度和右树的高度差都不超过1
 */

public static class ReturnType{
    public boolean isBalanced;
    public int height;

    public ReturnType(boolean isB,int hei){
        isBalanced = isB;
        height = hei;
    }
}
public static boolean isBalance(Node head){
    return process(head).isBalanced;
}

public static ReturnType  process(Node x){
    if(x == null){
        return new ReturnType(true,0);
    }
    ReturnType leftData = process(x.left);
    ReturnType rightData = process(x.right);
    int height = Math.max(leftData.height,rightData.height);
    boolean isBalance = leftData.isBalanced && rightData.isBalanced && Math.abs(leftData.height-rightData.height) < 2;
    return new ReturnType(isBalance,height);
}

练习七:公共父节点

求一颗二叉树中某两个结点的公共父节点

① 方法一:

使用Map集合,假设要求o1和o2的公共父节点

  1. 将每一个结点的父节点都存入Map集合中
  2. 设置Set集合,先将o1的路径存放到Set集合中去
  3. 再将设置Set集合,存放o2的路径,在存放o2的过程中,同时判断o2中路径中的结点是否在o1路径集合中,如果存在返回该结点,如果不存在返回head结点
public static Node lca(Node head,Node o1,Node o2){
    HashMap<Node,Node> fatherMap = new HashMap<>();
    process(head,fatherMap);
    HashSet<Node> set1 = new HashSet<>();
    Node cur =  o1;
    while(cur != fatherMap.get(cur)){
        set1.add(cur);
        cur = fatherMap.get(cur);
    }
    cur = o2;
    set1.add(head);
    while(!set1.contains(cur)){
        //得到当前结点的父节点
        cur = fatherMap.get(cur);
    }
    return cur;
}

public static void process(Node head,HashMap<Node,Node> fatherMap){
    if(head == null){
        return;
    }
    fatherMap.put(head.left,head);
    fatherMap.put(head.right,head);
    process(head.left,fatherMap);
    process(head.right,fatherMap);
}
② 方法二

这个方法就很牛逼,最好是画图来食用

  1. 首先o1和o2的情况有两种
    1. 两个结点一个是另一个的父节点
    2. 两个结点有公共的父节点,但肯定不是对方
  2. 看图片

解释一下该图片,其中黑色路线为父节点向字节点索要结果
紫色路线为子节点给父节点返回的结果
可以对照它来解读下面的代码

/**
* 找到O1,O2两个结点的公共祖先
* 1. 首先o1和o2的情况右两种
*      1. o1 和 o2互为父节点
*      2. O1 和 o2不互为父节点
*/
public static Node lowestAncestor(Node head,Node o1,Node o2){
   if(head == null || head == o1 || head == o2){
       return head;
   }
   Node left = lowestAncestor(head.left,o1,o2);
   Node right = lowestAncestor(head.right, o1,o2);
   if(left != null && right != null){
       return  head;
   }
   return left != null ? left : right;
}

3.2 小结

大部分的二叉树的题目都可以归为一个套路:
我需要什么?为了得到结果我需要和子节点要什么信息?
然后将需要的信息封装为一个类,逐层返回即可

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值