算法学习之路(3)--二叉树基础

二叉树的基本操作

继续看左神的视频,听他的思路之后自己写代码实现,过程中遇到很多问题,好在一一克服。写了很多注释和自己对其思路的理解。
一下代码包括了:

  • 二叉树前中后序遍历的递归和非递归
  • 层次遍历的非递归
  • 二叉树深度
  • 二叉树的序列化和反序列化非递归实现
  • 判断二叉树是否是AVL树
  • 判断二叉树是否是完全二叉树并计算其节点数(时间复杂度小于O(N))。
import java.util.*;
import static java.lang.Integer.max;
import static java.lang.Math.pow;
import static java.lang.StrictMath.abs;

/**
 * 对二叉树的操作:
 *      1.遍历(递归和非递归)
 *      2.初始化
 *      3.寻找深度
 *      4. 特殊二叉树的相关操作
 */
public class BinaryTree {
    public static int[] flag = new int[] {1,0};
    //先序创建二叉树
    public static TreeNode init()
    {
        int val = new Scanner(System.in).nextInt();
        if(val == -1)
            return null;
        TreeNode root = new TreeNode(val);
        root.left = init();
        root.right = init();
        return root;
    }
    // 先序遍历递归版本
    public static void travelPriorRecursion(TreeNode root)
    {
        if(root == null)
            return;
        //功能语句
        System.out.println(root.val);
        travelPriorRecursion(root.left);
        travelPriorRecursion(root.right);
    }
    //中序遍历递归版本
    public static void travelMidRecursion(TreeNode root)
    {
        if(root == null)
            return;
        //功能语句
        travelPriorRecursion(root.left);
        System.out.println(root.val);
        travelPriorRecursion(root.right);
    }
    //后序遍历递归版本
    public static void travelLaterRecursion(TreeNode root)
    {
        if(root == null)
            return;
        //功能语句
        travelPriorRecursion(root.left);
        travelPriorRecursion(root.right);
        System.out.println(root.val);
    }

    // 先序非递归
    public static void travelPrior(TreeNode root)
    {
        //java提供的系统栈1
        Stack<TreeNode> st = new Stack();
        TreeNode current = root;
        st.push(current);
        while(!st.isEmpty())
        {
            current = st.pop();
            //功能语句
            if(current != null)
            {
                System.out.println(current.val);
                st.push(current.right);
                st.push(current.left);
            }
        }
    }

    //中序非递归 遇到根节点先不打印,遇到最后的节点再开始弹出
    public static void travelMid(TreeNode root)
    {
        Stack<TreeNode> st = new Stack();
        TreeNode current = root;
        //由于右边是不放节点的,
        // 所以在根节点的左边子树全部访问完成之后栈就空了,但是右边没有访问,
        // 所以while的判断需要再加一条
        while(!st.isEmpty()|| current != null)
        {
            //一直找到最左端的节点
            if(current != null )
            {
                st.push(current);
                current = current.left;
            }
            else
            {
                //功能语句
                current = st.pop();
                System.out.println(current.val);
                current = current.right;
            }
        }
    }

    //后序非递归 :利用两个栈 先按照中右左的顺序走一遍,即调整一下先序遍历的入栈顺序即可
    //遍历时打印的内容改为入另一个辅助栈,最后依次弹出辅助栈的全部内容即可
    public static void travelLater(TreeNode root)
    {
        Stack<TreeNode> st1 = new Stack();
        Stack<TreeNode> st2 = new Stack();
        TreeNode current = root;
        st1.push(current);
        while(!st1.isEmpty())
        {
            current = st1.pop();

            if(current != null)
            {
                st2.push(current);
                //先放左结点,顺序就是 中--右---左
                st1.push(current.left);
                st1.push(current.right);

            }
        }
        while (!st2.isEmpty())
        {
            System.out.println(st2.pop().val);
        }
    }

    //层次遍历
    public static void travelLevelOrder(TreeNode root)
    {
        Queue<TreeNode> queue = new LinkedList<>();
        TreeNode current = root;
        queue.add(current);

        while(!queue.isEmpty())
        {
            current = queue.poll();
            System.out.println(current.val);
            if(current.left != null)
                queue.add(current.left);
            if(current.right != null)
                queue.add(current.right);
        }
    }
    //二叉树序列化与反序列:序列化用于持久化,将二叉树的结构转化成存储在内存中的文件,再利用反序列还原。
    // 本质上就是来实现非递归遍历
    // 不同节点间用“_“隔开,空节点用"#" 表示
    public static String binaryTreeSerialization(TreeNode root)
    {
        String res = "";
        Queue<TreeNode> queue = new LinkedList<>();
        TreeNode current = root;
        queue.add(current);
        res += root.val + "_";
//       层次顺序序列化
        while(!queue.isEmpty())
        {
            current = queue.poll();
            if(current.left == null)
                res += "#_";
            else
            {
                res += current.left.val + "_";
                queue.add(current.left);
            }
            if(current.right == null)
                res += "#_";
            else
            {
                res += current.right.val + "_";
                queue.add(current.right);
            }

        }
        return res;
    }
    //反序列化
    public static TreeNode binaryTreeCounterSerialization(String str)
    {
        String[] arr = str.split("_");
        if(arr.length == 0)
            return null;
        TreeNode root;
        Queue<TreeNode> queue = new LinkedList<>();
        root = new TreeNode(Integer.parseInt(arr[0]));
        queue.add(root);
        TreeNode current = root;
        for(int i = 1; i < arr.length; i++)
        {
            current = queue.poll();
            if(!arr[i].equals("#"))
            {
                current.left = new TreeNode(Integer.parseInt(arr[i]));
                queue.add(current.left);
            }
            else
            {
                current.left = null;
            }
            i++;
            if(!arr[i].equals("#"))
            {
                current.right = new TreeNode(Integer.parseInt(arr[i]));
                queue.add(current.right);
            }
            else
            {
                current.right = null;
            }
        }
        return root;
    }

    /**
     * 判断是否是AVL树
     *
     * @param head
     * @return
     * avL树是所有子树的高度差小于2 0表示是否是AVL树, 1表示树的高度
     * 思路就是:利用递归的结构,三次访问一个节点,我第一次访问这个节点的时候往左走,收集左子树的全部信息,
     * 第二次访问到这个节点的时候往右走,收集右子树的高度信息
     * 第三次访问节点时就开始计算高度,判断是否是AVL树
     * 如果是则向上返回,如果不是就返回false。
     */
    public static boolean isVALTree(TreeNode head)
    {
         if(isVALTreeProcess(head) != -1)
             return true;
         else
             return false;
    }
    public static int isVALTreeProcess(TreeNode head)
    {
        //遍历到叶子节点就返回 此时高度为1,并且认为是AVL树
        //从遍历的视角来,我们是从最下层向上看,叶子节点是第一个被记录的节点,所以高度是1
        if(head == null)
        {
            return 1;
        }
        //从左子树收集高度
        int left = isVALTreeProcess(head.left);
        if(left == -1)
            return -1;
        //从右子树收集高度
        int right = isVALTreeProcess(head.right);
        if(right == -1)
            return -1;
        //不满足AVL定义就返回-1
        if(abs(left - right) > 1)
            return -1;
        else
            //否则就向该节点的父节点返回自己的收集的高度
            return max(left,right)+1;
    }

    /**
     * 判断是否是完全二叉树
     *
     * 思路:使用层次遍历,利用二叉树节点和高度的关系,在遍历的过程中记录二叉树的子节点的情况
     *  当一个节点的左子树为空而右子树不为空,返回false
     *  当一个右子树为空,那么之后遍历的节点一定是叶子节点,否则返回false
     * @param head
     * @return
     */
    public static boolean isPerfectTree(TreeNode head)
    {
        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(head);
        TreeNode current;
        boolean flag = false;
        while(!queue.isEmpty())
        {
            current = queue.poll();


            if(flag)
            {
                if(current.left != null || current.right != null)
                    return false;
            }
            //这样的树一定不是完全二叉树
            if(current.left == null && current.right != null)
                return false;
            //这样的树可能是,但是之后的所有节点必须是叶子节点
            else if(current.left != null && current.right != null)
            {
                if(current.left != null)
                    queue.add(current.left);
                if(current.right != null)
                    queue.add(current.right);
            }
            else
            {
                flag = true;//开启新的判断
            }

        }
        return true;
    }

    /**
     * 计算一颗完全二叉树的节点个数,要求时间复杂度小于N
     * 思路:首先找到根节点的右子树的最左节点,如果这个节点达到了树的最大高度,则其左子树一定是满二叉树,利用公式计算即可。
     * 如果没有达到最后一层,则整个树的节点数为H-1层的满二叉树加上叶子左子树上的叶子节点。
     * @param head
     * @return
     */
    public static int conutNum(TreeNode head)
    {
        int num  = 0;

        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(head);
        TreeNode current = head;

        while (!queue.isEmpty())
        {
            int deep = 1;
            int maxDeep = 0;
            current = queue.poll();
            TreeNode temp = current;
            while (temp != null)
            {
                temp = temp.left;
                maxDeep ++;
            }
            temp = current;
            //寻找右子树的最左节点
            while (temp.right != null)
            {
                temp = temp.right;
                deep ++;
            }
            //第二种情况,我们此时需要遍历左子树,把左结点入队列 deep就是最大层数减一
            if(deep != maxDeep)
            {
                if(current.left != null)
                    queue.add(current.left);
            }
            else
            {
                if(current.right != null)
                queue.add(current.right);
            }
            num += (int)pow(2, deep - 1);
        }
        return num;
    }

    // for test
    // 工具类中不要写main函数
    public static void main(String[] args) {
        TreeNode root = init();
//测试遍历
//        travelLevelOrder(root);
//        String[] data = str.split("_");
//        for(int i = 0; i < data.length; i++)
//        {
//            System.out.println(data[i]);
//        }
//测试序列化
//        String str = binaryTreeSerialization(root);
//        travelLevelOrder(binaryTreeCounterSerialization(str));
//        travelPriorRecursion(binaryTreeCounterSerialization(str));
//        travelLater(binaryTreeCounterSerialization(str));
//测试AVL树
//        System.out.println(isVALTree(root));
//测试完全二叉树
        travelPrior(root);
        if(isPerfectTree(root))
            System.out.println(conutNum(root));
        else
            System.out.println("不是完全二叉树");
//测试计算完全二叉树节点的个数

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值