一、二叉树相关概念
1.1 基本术语
- 结点的度:一个结点的子结点的个数称为结点的度。
- 树的度:树中结点的最大度数为树的度
- 树的深度(高度):树中结点的最大层数,从1开始。
1.2 二叉树分类
- 满二叉树:一颗高度为h,并且含有2^h-1个结点的二叉树称为满二叉树。即树中每一层都含有最多的节点。除叶子节点每个节点的度都为2。
- 完全二叉树:当高度为h,具有n个结点的二叉树,与高度为h的满二叉树结点编号完全对应时,即为完全二叉树。(若存在度为1的节点,只能有一个,且只有左孩子无右孩子)
- 二叉排序树:左子树上所有的结点的关键字均小于根节点的关键字,右子树上所有节点的关键字均大于根结点的关键字。(中序遍历可得到递增序列)
- 平衡二叉树:树上任一结点的左子树和右子树深度之差不超过1。
- 哈夫曼树:在含有n个带权叶子节点的二叉树中,其中带权路径长度最小的二叉树为哈夫曼树(最优二叉树)—哈夫曼编码
- 红黑树:红黑树是每个节点都带颜色的树,节点颜色或是红色或是黑色,红黑树是查找树。最重要的性质,从根节点到叶子节点的最长的路径不多于最短路径的长度的2倍。红黑树插入、查找、删除的时间复杂度都为O(logn)
1.3 二叉树的几条重要性质:
- 非空二叉树上叶子节点的个数等于度数为2的节点数+1
- 结点i所在的层次(深度)为
(logn)+1 - N个节点的二叉树的高度为(logn)+1或是log(n+1)
1.4 二叉树的常见面试题
注意事项:
- 根节点是否为空(递归结点是否为null)
- 是否只存在根节点(一个节点)
- 二叉树通常使用递归,清楚递归结束的条件。例如找路径时是遇到叶子节点停止,大多数是当前结点为null
题目汇总
- 二叉树的遍历:前序遍历、中序遍历、后序遍历、层次遍历(队列)
- 根据中序遍历序列和前序遍历序列(或后序遍历)重建二叉树。
- 求二叉树的所有路径(根到叶子节点)
- 判断二叉树中是否存在一条路径使得节点之和为给定的值。
- 求二叉树的最大深度、最小深度
- 求二叉树第k层的节点个数(层次遍历:队列)
- 求二叉树中叶子节点的个数
- 交换二叉树的左右孩子
- 判断两棵二叉树是否相同(结构和值都相等)
- 判断二叉树是否为完全二叉树
- 判断二叉树是否为平衡二叉树
- 求二叉树的镜像
- 求二叉树中节点的最大距离
- 哈夫曼树的构建
- 将二叉查找树变为有序的双向链表
- 求二叉树中两个节点的最低公共祖先节点
- 输入两棵二叉树A和B,判断B是否为A的子树
详细代码
- 二叉树的遍历:前序遍历、中序遍历、后序遍历、层次遍历(队列)
前序遍历:
//递归:前序遍历 public void preOrder(Node node){ if(node!=null){ System.out.print(node.getPerson().getKey()+"\t"); preOrder(node.getLeftChild()); preOrder(node.getRightChild()); } } //非递归:前序遍历 private void preOrder_2(){ Node curNode = rootNode; while(curNode!=null){ //打印当前节点 System.out.print(curNode.getPerson().getKey()+"\t"); //入栈 stack.push(curNode); //指向左子节点 curNode = curNode.getLeftChild(); //查找最左边的子节点 while(curNode == null&&!stack.isEmpty()){ curNode = stack.peek();//取栈顶元素 stack.pop();//出栈 curNode = curNode.getRightChild(); } } }
中序遍历:
<span style="white-space:pre"> </span>//递归:中序遍历 public void midOrder(Node node){ if(node!=null){ midOrder(node.getLeftChild()); System.out.print(node.getPerson().getKey()+"\t"); midOrder(node.getRightChild()); } } //非递归:中序遍历(左根右) public void midOrder_2(){ Node curNode = rootNode; while(curNode!=null||!stack.isEmpty()){ if(curNode.getLeftChild()!=null){ stack.push(curNode); curNode = curNode.getLeftChild(); }else { //打印最左端的节点 System.out.print(curNode.getPerson().getKey()+"\t"); curNode = curNode.getRightChild();//指向右子节点 while(curNode == null&&!stack.isEmpty()){ curNode = stack.peek(); System.out.print(curNode.getPerson().getKey()+"\t"); stack.pop(); curNode = curNode.getRightChild(); } } } }
后序遍历:
//递归:后序遍历 public void behOrder(Node node){ if (node!=null) { behOrder(node.getLeftChild()); behOrder(node.getRightChild()); System.out.print(node.getPerson().getKey()+"\t"); } } //非递归:后序遍历(左根右) public void behOrder_2(){ Node curNode = rootNode; Node preNode = null; //先将根入栈 stack.push(curNode); while(!stack.isEmpty()){ curNode = stack.peek();//当前节点设置为栈顶节点 if(curNode.getLeftChild()==null&&curNode.getRightChild()==null ||(preNode!=null&&(curNode.getLeftChild()==preNode||curNode.getRightChild()==preNode))){ //当前节点无左右节点,或者有左节点或右节点,但已经被访问过 //则直接输出该节点,将其出栈,将其设为上一个访问的节点 System.out.print(curNode.getPerson().getKey()+"\t"); stack.pop(); preNode = curNode;//已被访问过 }else { //如果不满足上面两种情况,则将其右孩子左孩子依次入栈(先右节点再左节点) if (curNode.getRightChild()!=null) { stack.push(curNode.getRightChild()); } if(curNode.getLeftChild()!=null){ stack.push(curNode.getLeftChild()); } } } }
层次遍历:
<span style="white-space:pre"> </span>public int levelOrder(TreeNode root,int k) { Queue queue = new Queue(1024); List<List<Integer>> list = new ArrayList<List<Integer>>(); if(root==null){ return 0; } queue.push(root); //每层节点集 List<Integer> level = new ArrayList<Integer>(); //每层的节点个数 int number = 0; int levelCount = 0; do{ Queue queue2 = new Queue(1024); while(!queue.isEmpty()){ //取队首节点 TreeNode head = queue.peek(); level.add(head.val); number++;//结点个数加1 //弹出队头 queue.pop(); if(head.left!=null){ queue2.push(head.left); } if(head.right!=null){ queue2.push(head.right); } } levelCount++;//层级加1 if(levelCount==k){ return number; } list.add(level); level = new ArrayList<Integer>(); number = 0; queue = queue2; }while(!queue.isEmpty()); return number; } public class Queue { /** * 实现队列 */ int first,last,maxSize; TreeNode[] queue; public Queue(int size){ first = last = -1; maxSize = size; queue = new TreeNode[maxSize]; } public boolean isEmpty(){ if(first==-1){ return true; }else { return false; } } public boolean isFull(){ if(first==(last+1)%maxSize){ return true; }else { return false; } } //push public boolean push(TreeNode num){ if(this.isFull()){ System.out.println("queue is full!"); return false; }if(this.isEmpty()){ first = last = 0; }else{ last = (last+1)%maxSize; } queue[last] = num; return true; } public TreeNode pop(){ TreeNode num = queue[first]; if(this.isEmpty()){ queue = new TreeNode[maxSize]; return null; } if(first==last){ //到达队尾,清空数组 queue = new TreeNode[maxSize]; first = last = -1; return num; } first=(first+1)%maxSize; return num; } public TreeNode peek(){ if(first==-1) return null; else return queue[first]; } }
- 根据中序遍历序列和前序遍历序列(或后序遍历)重建二叉树。
- 求二叉树的所有路径(根到叶子节点)
- 判断二叉树中是否存在一条路径使得节点之和为给定的值。
- 求二叉树的最大深度、最小深度
- 求二叉树第k层的节点个数(层次遍历:队列)
- 求二叉树中叶子节点的个数
- 交换二叉树的左右孩子
-
判断两棵二叉树是否相同(结构和值都相等)
- 判断二叉树是否为完全二叉树
- 判断二叉树是否为平衡二叉树
- 求二叉树的镜像
- 求二叉树中节点的最大距离
- 哈夫曼树的构建
- 将二叉查找树变为有序的双向链表
- 求二叉树中两个节点的最低公共祖先节点
- 输入两棵二叉树A和B,判断B是否为A的子树
时间: 2024-08-10 01:55:06