二叉树
标签: 面试
二叉树的数据结构
定义一个BinaryTree类来表示二叉树,二叉树BinaryTree 又是由各个结点组成的,因此需要定义一个结点类BinaryNode,BinaryNode作为BinaryTree的内部类。
此外,在BinaryTree中需要一定一个BinaryNode属性来表示树的根结点。
public class BinaryTree<T extends Comparable<? super T>> {
private static class BinaryNode<T>{ //节点类
T element;
BinaryNode<T> left;
BinaryNode<T> right;
public BinaryNode(T element) { //叶子节点
this.element = element;
left = right = null;
}
public BinaryNode(T element, BinaryNode<T> left, BinaryNode<T> right) { //非叶子节点
this.element = element;
this.left = left;
this.right = right;
}
}
private BinaryNode<T> root; //二叉树的根
求二叉树的最大深度
最大深度即二叉树节点的最大层次。用递归,找出该节点的左右子树中最大深度,再加1,即本层次的最大深度
int maxDeath(TreeNode node){
//如果该节点为空,则返回0
if(node==null){
return 0;
}
int left = maxDeath(node.left);
int right = maxDeath(node.right);
return Math.max(left,right) + 1;
}
求二叉树的最小深度
最小深度是指从根节点往下通过最短路径到他最近的叶节点所经过的节点数。就最短路径的节点个数,还是用深度优先搜索DFS来完成
public int run(TreeNode root) {
if (root == null) { //考虑根节点为空的情况,此树为空树,应当返回0
return 0;
}
int left = run(root.left); //左子树递归,得到左子树的最小深度
int right = run(root.right); //右子树递归,得到右子树的最小深度
return (left == 0 || right == 0) ? left + right + 1 : Math.min(left,right) + 1;
//考虑左子树或者右子树为空的情况,则返回另外一颗子树的深度即可。如果左右子树均不为空,则返回左右子树中较小的那个值。
}
求二叉树中节点的个数
递归,求左右子树的节点的个数和再+1
int numOfTreeNode(TreeNode root){
if(root == null){ //考虑节点为空的情况
return 0;
}
int left = numOfTreeNode(root.left); //左子树递归,求个数
int right = numOfTreeNode(root.right); //右子树递归,求个数
return left + right + 1; //最后返回的左右子树的节点的个数和+1
}
求二叉树中叶子节点的个数
递归左右子树,找到叶子节点(没有左右子树)返回1,最后算总和
int numsOfNoChildNode(TreeNode root){
if(root == null){
return 0;
}
if(root.left==null&&root.right==null){ //如果是叶子节点就返回1
return 1;
}
return numsOfNodeTreeNode(root.left)+numsOfNodeTreeNode(root.right);
}
求二叉树中第k层节点的个数
int numsOfkLevelTreeNode(TreeNode root,int k){
if(root == null||k<1){ //若二叉树为空或者K小于1,返回0
return 0;
}
if(k==1){ //若K等于1,第1层就是树根,根只有一个,返回1
return 1;
}
//若K大于1,返回左子树中第K-1层结点个数加上右子树中第K-1层结点的个数
int numsLeft = numsOfkLevelTreeNode(root.left,k-1);
int numsRight = numsOfkLevelTreeNode(root.right,k-1);
return numsLeft + numsRight;
}
判断二叉树是否是平衡二叉树
如果某二叉树中任意结点的左右子树的深度相差不超过1,那么它就是一棵平衡二叉树。
简单思路:
在遍历树的每个结点的时候,调用函数TreeDepth得到它的左右子树的深度。如果每个结点的左右子树的深度相差不超过1,按照定义它就是一棵平衡的二叉树。由于一个结点会被重复遍历多次,这种思路的时间效率不高。
boolean isBalanced(TreeNode root){
if(root ==null) //空树是平衡数
return true;
int left = treeDepth(root.leftNode); //左子树的深度
int right = treeDepth(root.rightNode); //右子树的深度
int diff = left - right; //左右子树的深度相减,若绝对值大于1,则不平衡
if(diff > 1 || diff <-1)
return false;
//若该节点处平衡,且左右子树都平衡,才返回true
return isBalanced(root.leftNode) && isBalanced(root.rightNode);
}
int treeDepth(TreeNode node) {
if(node == null) return 0;
int left = treeDeath(node.left);
int right = treeDeath(node.right);
return Math.max(left,right) + 1;
}
改进算法
用后序遍历的方式遍历二叉树的每个结点,在遍历一个结点之前就已经遍历了它的左右子树。只要在遍历每个结点的时候记录它的深度,就可以一边遍历一边判断每个结点是不是平衡二叉树。
boolean isBalanced2(TreeNode root){
int depth = 0; //初始深度为0
return isBalanced2(root,depth);
}
public boolean isBalanced2(TreeNode root,int depth){
if(root == null){
depth = 0;
return true;
}
int left = 0,right = 0;
//如果左右子树都是平衡二叉树,那么则可以判断该子树是不是二叉树
if(isBalanced2(root.leftNode,left) && isBalanced2(root.rightNode,right)){
int diff = left-right;
//如果平衡,则计算一下该节点的深度
if(diff <= 1 && diff >= -1){
depth = 1+(left > right?left : right);
return true;
}
}
return false;
}
判断二叉树是否是完全二叉树
完全二叉树定义:
若设二叉树的深度为h,除第h层外,其它各层(1~h-1)的结点数都达到最大个数,第h层所有的结点都连续集中在最左边,这就是完全二叉树。
判断完全二叉树:
叶节点只能出现在最下层和次下层,并且最下面一层的结点都集中在该层最左边的若干位置的二叉树
层次遍历:
要进行层次遍历,需要建立一个队列,先将二叉树的头结点入队,然后出队,访问该节点,如果它有左子树,则将左子树的根节点入队;如果它有右子树,则将右子树的根节点入队。然后出队列,对出队节点访问。
思路:
因为涉及到每层的节点数,因此要对二叉树进行层次遍历,
boolean isCompleteTreeNode(TreeNode root){
if(root == null){
return false;
}
//创建一个队列,将根节点入队
Queue<TreeNode> queue = new LinkedList<TreeNode>();
queue.add(root);
boolean result = true;
boolean hasNoChild = false; //设立一个标志位
//若队列不为空,则出队
while(!queue.isEmpty()){
TreeNode current = queue.remove();
//若hasNoChile为真,代表前一个节点只有一个孩子
if(hasNoChild){
//这个节点有孩子节点,那么就不符合完全二叉树的要求
if(current.left!=null||current.right!=null){
result = false;
break;
}
}
//若hasNoChile为假
else{
//若该节点有两个孩子,那么将左孩子入队,有孩子入队
if(current.left!=null&¤t.right!=null){
queue.add(current.left);
queue.add(current.right);
}
//若该节点只有左孩子,则左孩子入队,并且将标志位置为true
else if(current.left!=null&¤t.right==null){
queue.add(current.left);
hasNoChild = true;
}
//若该节点没有左孩子,只有右孩子,那么已经不是完全二叉树了
else if(current.left==null&¤t.right!=null){
result = false;
break;
}
//若没有孩子,将标志位置为true
else{
hasNoChild = true;
}
}
}
return result;
}
两个二叉树是否完全相同
遍历二叉树的每一个节点,看其值是否相同
boolean isSameTreeNode(TreeNode t1,TreeNode t2){
if(t1==null&&t2==null){
return true;
}
else if(t1==null||t2==null){
return false;
}
if(t1.val != t2.val){
return false;
}
boolean left = isSameTreeNode(t1.left,t2.left);
boolean right = isSameTreeNode(t1.right,t2.right);
return left&&right;
}
翻转二叉树or镜像二叉树
将每一个子树的左右节点对调
TreeNode mirrorTreeNode(TreeNode root){
if(root == null){
return null;
}
TreeNode left = mirrorTreeNode(root.left);
TreeNode right = mirrorTreeNode(root.right);
root.left = right;
root.right = left;
return root;
}
求两个二叉树的最低公共祖先节点
两个二叉树,使用其根节点来表示。如果找到一个所求节点,左右节点分别在其左右子树中,则该节点即是两个二叉树的最低公共祖先节点;若在其中一个子树中未找到所求节点,则说明要在另一子树中寻找。(寻找的代价太大)
TreeNode getLastCommonParent(TreeNode root,TreeNode t1,TreeNode t2){
//对该节点的左右节点进行查找
if(findNode(root.left,t1)){
//如果左右节点
if(findNode(root.right,t2)){
return root;
}else{
return getLastCommonParent(root.left,t1,t2);
}
}else{
if(findNode(root.left,t2)){
return root;
}else{
return getLastCommonParent(root.right,t1,t2)
}
}
}
// 查找节点node是否在当前 二叉树中
boolean findNode(TreeNode root,TreeNode node){
if(root == null || node == null){
return false;
}
if(root == node){
return true;
}
boolean found = findNode(root.left,node);
if(!found){
found = findNode(root.right,node);
}
return found;
}