数据结构 二叉树是什么?看完这篇你就知道了(包含二叉树面试题详解)

系列文章目录

文章目录

一、二叉树的概念

1.1为什么会存在树结构?

高效的查找与搜素语义:例如企业管理中,想要查找一个员工只需要5次,但如果是一个线性结构,最坏情况需要找300次
在这里插入图片描述

1.2树的基本概念

  • 线性数据结构——线性表,逻辑上元素之间相连,呈先行排列。例如数组,链表,栈,队列等

  • 树是一种非线性的数据结构,它是由n(n>=0)个有限结点组成一个具有层次关系的集合。把它叫做树是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。

  • 树的度:一个节点产生的子树个数称为节点的度,例如下图根节点D有3个子树,则该节点有3个度;一个树中,某个最大节点的度,成为该树的度,例如图中度最大的节点为根节点D,所以该二叉树的度为3

在这里插入图片描述

1.3辨别树与非树

从根节点出发有M个子集,这些子集之间互不相交,相交就不是树结构
在这里插入图片描述

二、二叉树

2.1概念

在这里插入图片描述

2.2二叉树重要性质(笔试常考选择题)

在这里插入图片描述
注意事项:重点记忆第三点(数据结构必考)
推导过程:
一:二叉树中只存在三种情况的节点,一种是一个子树n1,一种是两个子树n2,还有一种没有子树的n0。三种类型节点相加之后等于总结点个数n。
二:边和节点存在关系x=n-1,只有一个子树的有一条边,两个子树的有两条边,n1+2n2=n-1这是边长关系

两式相减就得出点三条结论:度为0的节点一定比度为2的节点多一个
在这里插入图片描述

2.3常见二叉树

满二叉树

在这里插入图片描述
区分是不是完全二叉树的关键点:完全二叉树节点编号与满二叉树是否完全一致,并且完全二叉树只能是在最深层不满,其它层数的节点都是满的。
在这里插入图片描述

练习:辨别下列图中是不是完全二叉树

在这里插入图片描述
不是,因为编号与满二叉树节点编号不一致。也不满足只能最深层节点数不满,图中第二层的节点数不满,关键点就是编号看是否一致。

二分搜索树

在这里插入图片描述

2.4完全二叉树编号问题(考点)

在这里插入图片描述
在这里插入图片描述

2.5二叉树遍历问题(递归)

关于"序"可以理解为访问输出根节点的次序,前就是第一次遇到就输出,以此类推。
在这里插入图片描述
前序遍历:根 左 右
在这里插入图片描述
中序遍历:左 根 右
在这里插入图片描述
后序遍历:左 右 根
在这里插入图片描述
层序遍历:从左往右,从上到下
在这里插入图片描述

三中遍历方式特点总结(刷题用的上)

在这里插入图片描述
代码练习:
在这里插入图片描述

/*
* 二叉树基本操作
* */
class TreeNode<E>{
   
    //当前节点的值
    E val;
    //左子树的根
    TreeNode<E> left;
    //右子树的根
    TreeNode<E> right;

    public TreeNode(E val) {
   
        this.val = val;
    }
}

public class MyBinTree<E> {
   
    public TreeNode<Character> root;

    // 建立一个测试二叉树
    public void build() {
   
        TreeNode<Character> node = new TreeNode<>('A');
        TreeNode<Character> node1 = new TreeNode<>('B');
        TreeNode<Character> node2 = new TreeNode<>('C');
        TreeNode<Character> node3 = new TreeNode<>('D');
        TreeNode<Character> node4 = new TreeNode<>('E');
        TreeNode<Character> node5 = new TreeNode<>('F');
        TreeNode<Character> node6 = new TreeNode<>('G');
        TreeNode<Character> node7 = new TreeNode<>('H');
        node.left = node1;
        node.right = node2;
        node1.left = node3;
        node1.right = node4;
        node4.left = node6;
        node6.right = node7;
        node2.right = node5;
        root = node;
    }

代码示例:掌握递归本质

 /**
    * 传入一颗二叉树根节点root。按照前序遍历根左右方式进行输出
    * */
    public void preOrder(TreeNode root){
   
        if(root==null){
   
            return;
        }
        //打印根
        System.out.print(root.val+" ");
        //左子树
        preOrder(root.left);
        //右子树
        preOrder(root.right);
    }
/**
     * 传入一颗二叉树的根节点root。就能按照中序遍历左根右的方式进行输出
     * @param root
     */
    public void inOrder(TreeNode root) {
   
        if (root == null) {
   
            return;
        }
        // 先打印左子树,交给子函数
        inOrder(root.left);
        // 打印根
        System.out.print(root.val + " ");
        // 最后打印右子树
        inOrder(root.right);
    }
 /**
     * 传入一颗二叉树的根节点root。就能按照后序遍历左右根的方式进行输出
     * @param root
     */
    public void postOrder(TreeNode root) {
   
        if (root == null) {
   
            return;
        }
        // 先打印左子树,交给子函数
        postOrder(root.left);
        // 再打印右子树
        postOrder(root.right);
        // 最后打印根
        System.out.print(root.val + " ");
    }
}

总结:
在这里插入图片描述

2.6二叉树的遍历问题(迭代)

1.前序遍历

易错点:深度优先是借助栈的数据结构,所以压栈时,如果节点左右子节点都不为空应该先压右节点,在下次循环时就可以先弹出左节点。

public List<Integer> preOrder(TreeNode root){
   
        List<Integer> ret=new ArrayList<>();
        if(root==null){
   
            return ret;
        }
        Deque<TreeNode> stack=new LinkedList<>();
        stack.push(root);
        while (!stack.isEmpty()){
   
        //先访问根节点
            TreeNode cur= stack.pop();
            ret.add(cur.val);
            //将右子树先压入栈中
            if(cur.right!=null){
   
                stack.push(cur.right);
            }
            //再处理左子树
            if(cur.left!=null){
   
                stack.push(cur.left);
            }
        }
        return ret;
    }
}

2.中序遍历

核心思路:左根右,先一路向左走到底,碰到第一个左子树为空的根节点出栈
在这里插入图片描述

在这里插入图片描述

public List<Integer> inorderTraversal(TreeNode root){
   
        List<Integer> ret=new ArrayList<>();
        if(root==null){
   
            return ret;
        }
        Deque<TreeNode> stack=new ArrayDeque<>();
        TreeNode cur=root;
        //以下两个条件一个不满足都不可以停止循环
        while(cur!=null||!stack.isEmpty()){
   
            //先一路向左走到底
            while (cur!=null){
   
                stack.push(cur);
                cur=cur.left;
            }
            //cur已经为空,弹出栈顶元素,遇到第一个左子树为空的节点
            cur= stack.pop();
            ret.add(cur.val);
            //继续访问右子树
            cur=cur.right;
        }
        return  ret;
    }
}

总结:1.第一个大的while加上cur!=null的原因是此时根节点不入栈,但栈是空的,所以表示循环还没有结束。但是cur=cur.right可能出现空节点,此时cur=null但是栈不等于空,所以他俩只要一个条件不满足循环都还要继续。
2.一开始根节点不能先入栈,要一路走到底。
3.内层while循环就是为了一路向左子树走到底

解题思路:
在这里插入图片描述

3.后序遍历

在这里插入图片描述
难点:如何判断右树也走完了?

  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值