剑指offer---关于树的题目解答---第一弹

目录

二叉树的深度

重建二叉树

树的子结构

二叉树的镜像

对称的二叉树

从上往下打印二叉树

把二叉树打印成多行

二叉搜索树的第k个结点


作为一名程序猿,树这种结构是我们必须要掌握的,关于树的基本操作可以参考之前的博文

https://blog.csdn.net/szy2333/article/details/88804090

今天主要是对剑指offer中的树是相关题目进行整理

首先树的基本结构定义为:

public class TreeNode {
    public Integer val;
    public TreeNode left;
    public TreeNode right;

    public TreeNode(Integer v) {
        val = v;
    }
}

二叉树的深度

解题思路:

使用递归的方式

当树为空的时候,返回0

如果树只有左子树没有右子树,那么树的深度为左子树的深度+1

如果树只有右子树没有左子树,那么树的深度为右子树的深度+1

如果该树既有左子树又有右子树,那么深度为左子树和右子树的最大值+1

代码实现:

public class TreeDepth {
    public int TreeDepth(TreeNode root) {
        if(root == null) {
            return 0;
        }

        int left = TreeDepth(root.left);
        int right= TreeDepth(root.right);
        return Math.max(left, right) + 1;
    }
}

也可以使用非递归的方式,即 使用层数遍历,设置一个变量来记录层数,思路和代码可以参考题目把二叉树打印成多行

重建二叉树

解题思路:

根据不同遍历方式的不同特点

前序遍历:根左右    --->    第一个结点一定是根节点             

中序遍历:左根右    --->    第一个结点一定是左子节点               

后序遍历:左右根    --->   最后一个结点一定是根节点 

从本题目出发,知道前序遍历和中序遍历分别为

前:      1      2      4      7      3      5      6      8

中:      4      7      2      1      5      3      8      6

根节点右边是同样的方法,这样通过递归就可以实现二叉树的重构啦

同样的,我们可以想到,只要知道任意两种遍历方式,就可以进行二叉树的重构

代码实现:

import java.util.Arrays;

public class RebuildTreeNode {
    public TreeNode reBuildBinaryTree(int[] pre, int[] in) {
        if(pre.length == 0 || in.length == 0) {
            return null;
        }

        TreeNode root = new TreeNode(pre[0]);
        for(int i = 0; i < pre.length; i++) {
            if(pre[0] == in[i]) {
                root.left = reBuildBinaryTree(Arrays.copyOfRange(pre, 1, i + 1),
                                                  Arrays.copyOfRange(in, 0, i));
                root.right = reBuildBinaryTree(Arrays.copyOfRange(pre, i + 1, pre.length),
                                                   Arrays.copyOfRange(in, i + 1, pre.length));
                break;
            }
        }
        return root;
    }
}

树的子结构

解题思路:

所谓树的子结构:B是A的一部分,则B为A的子结构,如下图中,圈出的部分都是大树的子结构

首先比较A,B的根节点是否相同,

如果相同,则分别比较B的左右子结点是否为null或等于A的左右子结点

如果不同,则判断B的根节点和A的左子节点是否相同,

如果相同,则分别比较B的左右子结点是否为null或等于A的左子节点的左右子结点

如果不同,则判断B的根节点和A的右子节点是否相同,

如果相同,则分别比较B的左右子结点是否为null或等于A的右子节点的左右子结点

...............

不断递归,得到结果

代码实现:

public class HasSubtree {
    public boolean HasSubtree(TreeNode root1,TreeNode root2) {
        boolean result = false;
        if(root1 != null && root2 != null) {
            if(root1.val == root2.val) {
                result = isSubtree(root1, root2);
            }

            if(!result) {
                result = isSubtree(root1.left, root2);
            }

            if(!result) {
                result = isSubtree(root1.right, root2);
            }
        }

        return result;
    }

    public boolean isSubtree(TreeNode root1, TreeNode root2) {
        if(root2 == null) {
            return true;
        }

        if(root1 == null) {
            return false;
        }

        if(root1.val != root2.val) {
            return false;
        }

        return isSubtree(root1.left, root2.left) && isSubtree(root1.right, root2.right);
    }
}

二叉树的镜像

解题思路:

通过输入描述中的图片,我们可以看出,所谓镜像,就是:

树的左子树成为了右子树,右子树成为了左子树,并且每个子树都遵循同样的原则

通过递归可以实现

代码实现:

public class Mirror {
    public void Mirror(TreeNode root) {
        if(root == null) {
            return;
        }

        TreeNode left = root.left;
        TreeNode right = root.right;
        root.left = right;
        root.right = left;

        Mirror(root.left);
        Mirror(root.right);
    }
}

对称的二叉树

解题思路1:

如果二叉树对称,那么其本身和自己的镜像树应该是一样的

首先调用镜像树,然后遍历结点,依次进行比较即可

解题思路2:

使用队列的方式对结点进行存储,由于队列先进先出,那么将节点以对称位置的方式存入,每次取出两个进行比较

首先将根节点的左右结点值存入,当队列不为空时,每次取出两个值

如果结点值不相同,则树不是对称的,返回false

否则,分别将左结点的左结点和右结点,右结点的右结点和左结点依次存入,然后重复操作

代码实现:

import java.util.LinkedList;
import java.util.Queue;

public class IsSymmetrical {
    boolean isSymmetrical(TreeNode pRoot) {
        if(pRoot == null) {
            return true;
        }

        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(pRoot.left);
        queue.offer(pRoot.right);
        while (!queue.isEmpty()) {
            TreeNode left = queue.poll();
            TreeNode right = queue.poll();
            if(left == null && right == null) continue;
            if(left == null || right == null) return false;
            if(left.val != right.val) return false;

            queue.offer(left.left);
            queue.offer(right.right);
            queue.offer(left.right);
            queue.offer(right.left);
        }

        return true;
    }
}

从上往下打印二叉树

解题思路:

其本质就是层序遍历,然后将遍历的值依次输出

将元素存储在list中,并利用队列这种先进先出的结构特点进行操作

首先将根节点存入队列,如果队列不为空,则进行操作

得到队列的第一个结点元素node,将元素值存储于list中,然后让node的左右子结点分别进入队列并移除node

代码实现:

import java.util.ArrayList;

public class PrintFromTopToBottom {
    public ArrayList<Integer> PrintFromTopToBottom(TreeNode root) {
        ArrayList<Integer> list = new ArrayList<>();
        ArrayList<TreeNode> queue = new ArrayList<>();
        if(root == null) {
            return list;
        }

        queue.add(root);
        while (queue.size() != 0) {
            TreeNode node = queue.remove(0);
            if(node.left != null) {
                queue.add(node.left);
            }

            if(node.right != null) {
                queue.add(node.right);
            }

            list.add(node.val);
        }

        return list;
    }
}

把二叉树打印成多行

解题思路:

和上一个题原理相同,只不过稍微复杂了一点

需要按照每一层分别进行存储,那么是如何判断层数发生变化了呢?

我们采用list进行所有结点的存储,设置标志位start和end,end为list的大小,list的初始元素为根节点

当list不为空时

start从0开始,end = list.size(), 每次移除一个元素,start++,并将该元素的左右子结点存入,直至start等于end时,本层结束,进入下一层,此时start重新赋值为0,end也重新赋值为list.size(), 不断循环

代码实现:

import java.util.ArrayList;

public class PrintTreeByLayer {
    ArrayList<ArrayList<Integer>> Print(TreeNode pRoot) {
        ArrayList<ArrayList<Integer>> resultList = new ArrayList<>();
        if(pRoot == null) {
            return resultList;
        }

        ArrayList<TreeNode> layer = new ArrayList<>();
        ArrayList<Integer> layerList = new ArrayList<>();

        layer.add(pRoot);
        int start = 0;
        int end = 1;
        while (!layer.isEmpty()) {
            TreeNode node = layer.remove(0);
            layerList.add(node.val);
            start++;
            if(node.left != null) {
                layer.add(node.left);
            }

            if(node.right != null) {
                layer.add(node.right);
            }

            if(start == end) {
                start = 0;
                end = layer.size();
                resultList.add(layerList);
                layerList = new ArrayList<Integer>();
            }
        }
        return resultList;
    }
}

二叉搜索树的第k个结点

解题思路:

二叉搜索树就是:任何一个根节点的左子节点的值都比自己小,右子结点的值都比自己大

如果使用中序遍历,那么得到的结果刚好是按顺序排列的

根据这种特点,我们将树中的结点按照中序遍历的顺序存放入list中,最后取下标为k - 1的值即可

不过注意:不需要进行所有结点的遍历,在list的大小远远大于k的值时,就会大大提高效率,不需要对所有结点进行遍历

代码实现:

import java.util.ArrayList;
public class Solution {
    TreeNode KthNode(TreeNode pRoot, int k) {
        if(pRoot == null || k <= 0) {
            return null;
        }

        ArrayList<TreeNode> list = new ArrayList<>();
        in(pRoot, list, k);

        if(k > list.size()) {
            return null;
        }
        
        return list.get(k - 1);
    }

    public static void in(TreeNode pRoot, ArrayList<TreeNode> list, int k) {
        if(pRoot == null) {
            return;
        }

        in(pRoot.left, list, k);
        list.add(pRoot);
        if(list.size() ==  k) {
            return;
        }
        in(pRoot.right, list, k);
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值