二叉树和面试题

本文详细介绍了二叉树的基本概念,包括满二叉树与完全二叉树的区别、树的深度等,并提供了遍历二叉树的示例代码,帮助读者深入理解二叉树的特性及其应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

二叉树的每个结点至多只有二棵子树(不存在度大于2的结点),二叉树的子树有左右之分,次序不能颠倒。二叉树的第i层至多有2^{i-1}个结点;深度为k的二叉树至多有2^k-1个结点;对任何一棵二叉树T,如果其终端结点数为n_0,度为2的结点数为n_2,则n_0=n_2+1。

一棵深度为k,且有2^k-1个节点称之为满二叉树;深度为k,有n个节点的二叉树,当且仅当其每一个节点都与深度为k的满二叉树中,序号为1至n的节点对应时,称之为完全二叉树。

1 满二叉树和完全二叉树

满二叉树一定是完全二叉树,但是反过来就不一定。满二叉树的定义是除了叶子结点,其它结点左右孩子都有,深度为k的满二叉树,结点数就是2的k次方减1。完全二叉树是每个结点都与深度为k的满二叉树中编号从1到n一一对应。

2 树的深度

树的最大层次就是深度,比如上图,深度是3。很容易得出,深度为k的树,拥有的最大结点数是2的k次方减1。

3 树的孩子,兄弟,双亲

上图中,B,C是A的孩子,B,C之间互为兄弟,A是B,C的双亲。

笔试/面试常见的2个问题:
1、写出三种遍历方式的代码
2、给出先序、中序的结果, 给出后续的结果;(考验你是否真的理解三种排序)
public class TestClass { 
	private static int[] array = { 1, 2, 3, 4, 5, 6, 7};  
    private static List<Node> nodeList = null;  
	
   private static class Node {  
	   Node leftChild;  //左子树
	   Node rightChild; //右子树
       int data;  //节点数据
 
       Node(int newData) {  
           leftChild = null;  
           rightChild = null;  
           data = newData;  
       }  
   }  

   public static void createBinTree() {  
       nodeList = new LinkedList<Node>();  
       for (int nodeIndex = 0; nodeIndex < array.length; nodeIndex++) {  
           nodeList.add(new Node(array[nodeIndex]));  
       }  
       for (int parentIndex = 0; parentIndex < array.length / 2 - 1; parentIndex++) {  
           nodeList.get(parentIndex).leftChild = nodeList  
                   .get(parentIndex * 2 + 1);  
           nodeList.get(parentIndex).rightChild = nodeList  
                   .get(parentIndex * 2 + 2);  
       }  
       int lastParentIndex = array.length / 2 - 1;  
       nodeList.get(lastParentIndex).leftChild = nodeList  
               .get(lastParentIndex * 2 + 1);  
       if (array.length % 2 == 1) {  
           nodeList.get(lastParentIndex).rightChild = nodeList  
                   .get(lastParentIndex * 2 + 2);  
       }  
   }  
   
   /** 
    * 先序遍历 
    */  
   public static void preOrderTraverse(Node node) {  
       if (node == null)  
           return;  
       System.out.print(node.data + " ");  
       preOrderTraverse(node.leftChild);  
       preOrderTraverse(node.rightChild);  
   }  
 
   /** 
    * 中序遍历 
    */  
   public static void inOrderTraverse(Node node) {  
       if (node == null)  
           return;  
       inOrderTraverse(node.leftChild);  
       System.out.print(node.data + " ");  
       inOrderTraverse(node.rightChild);  
   }  
 
   /** 
    * 后序遍历 
    */  
   public static void postOrderTraverse(Node node) {  
       if (node == null)  
           return;  
       postOrderTraverse(node.leftChild);  
       postOrderTraverse(node.rightChild);  
       System.out.print(node.data + " ");  
   }  

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		createBinTree(); 
        // nodeList中第0个索引处的值即为根节点  
        Node root = nodeList.get(0);  
        System.out.print("先序:");
        preOrderTraverse(root);
        System.out.println("");
        System.out.print("中序:");
        inOrderTraverse(root);
        System.out.println("");
        System.out.print("后序:");
        postOrderTraverse(root);
        
	}

}
按照层级遍历, 原理是使用FIFO队列实现。  
//层级遍历   
 private static void levelTraverse(Node node) {
        if (node == null) {
            return;
        }
        
        LinkedList<Node> queue = new LinkedList<>();
        Node current = null;
        queue.offer(node);
        
        while (!queue.isEmpty()) {
            current = queue.poll();
            
            System.out.println(current.data + ""); //访问节点
            
            if (current.leftChild != null) {
                queue.offer(current.leftChild)
            }
            
            if (current.rightChild != null) {
                queue.offer(current.rightChild);
            }
        }
    }
      稍微修改一下按层级遍历的代码,还可以实现获取二叉树最大深度、最小深度、每层节点宽度/最大值等等;
public List<Integer> largestValues(TreeNode root) {
        List<Integer> result = new ArrayList<>();
        LinkedList<TreeNode> ll = new LinkedList<>();
        ll.add(root);
        //队列里是每层节点, 内循环遍历当前层所有节点,然后队列里是下层节点
        //本算法还计算二叉树最浅深度, 在外循环深度自增1,内循环判断左右节点为空时跳出循环即可
        while (true) {
            int len = ll.size(); //每层节点个数
            if (len == 0) {
                break;
            }

            int max = 0;
            while (len > 0) {  //遍历当前层所有节点
                TreeNode node = ll.poll();  //从队列头部移出
                if (node.left != null) {
                    ll.add(node.left);  //添加到队列尾部
                }
                if (node.right != null) {
                    ll.add(node.right);
                }
                 if (node.val>max) max = node.val;
                len--;
            }
            result.add(max);
        }

        return result;
    }

 输出结果:
先序:1 2 4 5 3 6 7 
中序:4 2 5 1 6 3 7 
后序:4 5 2 6 7 3 1 

面试时可能告诉你先序和中序的结果, 让你画出二叉树并写出后序排序。  
解题技巧:
 1、先序排序的第一个数据是根节点, 中序排序中根节点左边的都是左子树、右边的都是右子树。 即1是根节点, 4、2、5是左子树,6、3、7是右子树;
 2、 先序的第2个值即2是根节点左子节点;在先序里的3、6、7中第一个值是根节点的右子节点。 
 3、记住三种排序是按照根节点的读取时序定义, 先序是先访问根节点,中序是第2次访问根节点,后序是第3次访问根节点。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值