算法基础入门——二叉树递归遍历与非递归遍历、二叉树最大宽度、判断是否是二叉搜索树、判断是否是完全二叉树、判断是否是平衡二叉树、最低公共祖先节点、获取某一节点的后继节点、对折纸张的折痕问题

package com.zuoshen.jichurumen.class05;

import java.util.*;

/**
 * @author ShiZhe
 * @create 2022-02-28 20:41
 */
public class code01 {

    /**
     * 二叉树节点
     */
    public static class Node {
        public int value;
        public Node left;
        public Node right;

        public Node(int data) {
            this.value = data;
        }
    }

    /**
     * 二叉树前序递归遍历
     * @param head
     */
    public static void preOrderRecur(Node head) {
        if (head == null) {
            return;
        }
        System.out.print(head.value + " ");
        preOrderRecur(head.left);
        preOrderRecur(head.right);
    }

    /**
     * 二叉树前序非递归遍历
     * 通过栈来实现
     * 先右后左
     * @param head
     */
    public static void preOrderUnRecur(Node head) {
        System.out.print("pre-order: ");
        if (head != null) {
            Stack<Node> stack = new Stack<Node>();
            stack.add(head);
            while (!stack.isEmpty()) {
                head = stack.pop();
                System.out.print(head.value + " ");
                if (head.right != null) {
                    stack.push(head.right);
                }
                if (head.left != null) {
                    stack.push(head.left);
                }
            }
        }
        System.out.println();
    }

    /**
     * 二叉树中序递归遍历
     * @param head
     */
    public static void inOrderRecur(Node head) {
        if (head == null) {
            return;
        }
        inOrderRecur(head.left);
        System.out.print(head.value + " ");
        inOrderRecur(head.right);
    }

    /**
     * 二叉树中序非递归遍历
     * 通过栈来实现
     * 先放左,再放右
     * @param head
     */
    public static void inOrderUnRecur(Node head) {
        System.out.print("in-order: ");
        if (head != null) {
            Stack<Node> stack = new Stack<>();
            while (!stack.isEmpty() || head != null) {
                if (head != null) {
                    stack.push(head);
                    head = head.left;
                } else {
                    head = stack.pop();
                    System.out.print(head.value + " ");
                    head = head.right;
                }
            }
        }
        System.out.println();
    }

    /**
     * 二叉树后序递归遍历
     * @param head
     */
    public static void posOrderRecur(Node head) {
        if (head == null) {
            return;
        }
        posOrderRecur(head.left);
        posOrderRecur(head.right);
        System.out.print(head.value + " ");
    }

    /**
     * 二叉树后序递归遍历-1
     * 与二叉树前序遍历类似
     * @param head
     */
    public static void posOrderUnRecur1(Node head) {
        System.out.print("pos-order: ");
        if (head != null) {
            Stack<Node> s1 = new Stack<Node>();
            Stack<Node> s2 = new Stack<Node>();
            s1.push(head);
            while (!s1.isEmpty()) {
                head = s1.pop();
                s2.push(head);
                if (head.left != null) {
                    s1.push(head.left);
                }
                if (head.right != null) {
                    s1.push(head.right);
                }
            }
            while (!s2.isEmpty()) {
                System.out.print(s2.pop().value + " ");
            }
        }
        System.out.println();
    }

    /**
     * 二叉树后序递归遍历-2
     * 利用栈的peek()函数获取栈顶的节点
     * 我们什么时候才能访问节点。有如下两种情况:
     * 当前经过节点是叶子节点。
     * 当前经过节点的右子节点是上一次访问的节点。
     * @param head
     */
    public static void posOrderUnRecur2(Node head) {
        System.out.print("pos-order: ");
        Stack<Node> stack = new Stack<>();
        // 当前节点
        Node cur = head;
        // 用于记录上一次访问的节点
        Node pre = null;
        while (cur != null || !stack.isEmpty()) {
            while (cur != null) {
                stack.push(cur);
                cur = cur.left;
            }
            if (!stack.isEmpty()) {
                cur = stack.pop();
                // 访问节点的调节
                if (cur.right == null || pre == cur.right) {
                    // 访问
                    System.out.print(cur.value + " ");
                    // 这一步是记录上一次访问的节点
                    pre = cur;
                    // 此处为了跳过下一次循环的访问左子节点的过程,直接进入栈的弹出阶段,因为但凡在栈中的节点,它们的左子节点都肯定被经过且已放入栈中。
                    cur = null;
                } else { // 不访问节点的条件
                    // 将已弹出的根节点放回栈中
                    stack.push(cur);
                    // 经过右子节点
                    cur= cur.right;
                }
            }
        }
        System.out.println();
    }

    /**
     * 二叉树最大宽度
     * 使用队列
     * @param head
     */
    public static int getMaxWidth(Node head) {
        if (head == null) {
            return 0;
        }
        // hashMap将节点与层数绑定,通过层数的变化来计算每一层的width
        HashMap<Node, Integer> levelMap = new HashMap<>();
        // queue存储每层的节点
        // linkedList与arrayList的区别在于linkedList使用双链表存储,而arrayList使用数组存储。
        LinkedList<Node> queue = new LinkedList<>();
        // 需要求的最大的width
        int maxWidth = 0;
        // 当前层的width
        int curWidth = 0;
        // 当前层数
        int curLevel = 1;
        // 初始化
        levelMap.put(head, 1);
        queue.add(head);
        Node node = null;
        Node left = null;
        Node right = null;
        while (!queue.isEmpty()) {
            node = queue.poll();
            left = node.left;
            right = node.right;
            if (left != null) {
                levelMap.put(left, levelMap.get(node) + 1);
                queue.add(left);
            }
            if (right != null) {
                levelMap.put(right, levelMap.get(node) + 1);
                queue.add(right);
            }
            if (levelMap.get(node) > curLevel) {
                curWidth = 1;
                curLevel = levelMap.get(node);
            } else {
                curWidth++;
            }
            maxWidth = Math.max(curWidth, maxWidth);
        }
        return maxWidth;
    }

    /**
     * 判断是否是二叉搜索树,中序遍历是有序的。
     * @param head
     * @return
     */
    public static boolean isBST(Node head) {
        if (head == null) {
            return true;
        }
        LinkedList<Node> inOrderList = new LinkedList<>();
        isBSTProcess(head, inOrderList);
        int pre = Integer.MIN_VALUE;
        Node cur = null;
        while (!inOrderList.isEmpty()) {
            cur = inOrderList.poll();
            if (pre >= cur.value) {
                return false;
            }
            pre = cur.value;
        }
        return true;
    }

    /**
     * BST的中序遍历
     * @param node
     * @param inOrderList
     */
    public static void isBSTProcess(Node node, LinkedList<Node> inOrderList) {
        if (node == null) {
            return;
        }
        isBSTProcess(node.left, inOrderList);
        inOrderList.add(node);
        isBSTProcess(node.right, inOrderList);
    }

    /**
     * CBT:完全二叉树,如果编号为i(1≤i≤n)的结点与满二叉树中编号为i的结点在二叉树中的位置相同,
     * 则这棵二叉树称为完全二叉树。
     * 叶子结点只能出现在最下层和次下层,且最下层的叶子结点集中在树的左部。
     * @param head
     * @return
     */
    public static boolean isCBT(Node head) {
        if (head == null) {
            return true;
        }
        LinkedList<Node> queue = new LinkedList<>();
        // 判断是否为叶子结点
        boolean leaf = false;
        queue.add(head);
        Node cur = null;
        while (!queue.isEmpty()) {
            cur = queue.poll();
            // 左无右有返回false
            // 已经出现叶子节点了,后面出现非叶子节点的返回false
            if ((cur.left == null && cur.right!=null) || (leaf && (cur.left != null ||cur.right != null))) {
                return false;
            }
            if (cur.left != null) {
                queue.add(cur.left);
            }
            // 右边为空,则后面均为叶子结点
            if (cur.right != null) {
                queue.add(cur.right);
            } else {
                leaf = true;
            }
        }
        return true;
    }

    /**
     * 判断是否是平衡二叉树:左右子树的高度差的绝对值不超过1
     * @param head
     * @return
     */
    public static boolean isBalanced(Node head) {
        return process(head).isBalanced;
    }

    /**
     * 平衡二叉树的返回数据格式
     */
    public static class ReturnTypeIsBalanced {
        // 当前数的高度
        public int height;
        // 当前数是否是平衡二叉树
        public boolean isBalanced;

        // 构造函数
        public ReturnTypeIsBalanced(boolean isBalanced, int height) {
            this.height = height;
            this.isBalanced = isBalanced;
        }
    }

    /**
     * 判断是否是平衡二叉树的处理函数
     * 向左数要数据,向右数要数据,比较,得到答案
     * @param head
     * @return
     */
    public static ReturnTypeIsBalanced process(Node head) {
        if (head == null) {
            return new ReturnTypeIsBalanced(true, 0);
        }
        ReturnTypeIsBalanced leftData = process(head.left);
        ReturnTypeIsBalanced rightData = process(head.right);
        int height = Math.max(leftData.height, rightData.height) + 1;
        boolean isBalanced = leftData.isBalanced && rightData.isBalanced &&
                Math.abs(leftData.height - rightData.height) < 2;
        return new ReturnTypeIsBalanced(isBalanced, height);
    }

    /**
     * 最低公共祖先节点
     * 递归遍历,寻找节点
     * @param head
     * @param o1
     * @param o2
     * @return
     */
    public static Node lowestAncestor(Node head, Node o1, Node o2) {
        // 发现目标节点,标记返回
        if (head == null || head == o1 || head == o2) {
            return head;
        }
        // 在左子树上找寻目标节点
        Node left = lowestAncestor(head.left, o1, o2);
        // 在右子树找寻目标节点
        Node right = lowestAncestor(head.right, o1, o2);
        // 在左子树和右子树上分别找到,则改树的头为最低公共祖先节点
        if (left != null && right != null) {
            return head;
        }
        // 有一个为空,则表示2个在同侧,返回非空节点就行
        return left != null ? left : right;
    }

    /**
     * 拥有parent指针的二叉树节点结构
     */
    public static class NodeNew {
        public int value;
        public NodeNew left;
        public NodeNew right;
        public NodeNew parent;
        public NodeNew(int data) {
            this.value = data;
        }
    }

    /**
     * 获取某一节点的后继节点
     * 在二叉树的中序遍历的序列中, node的下一个节点叫作node的后继节点。
     * @param node
     * @return
     */
    public static NodeNew getSuccessorNode(NodeNew node) {
        if (node == null) {
            return node;
        }
        // 右子树不为空,后继节点为右子树最左节点
        if (node.right != null) {
            return getLeftMost(node.right);
        } else {
            // 右子树为空
            // 当该节点为父亲节点的左子树时,该节点的后继节点就是父亲节点
            // 当该节点不是父亲节点的左子树的,将父亲节点设为子节点,循环找子节点为父亲节点的左子树的父亲节点
            NodeNew parent = node.parent;
            while (parent.left != node && parent != null) {
                node = parent;
                parent = node.parent;
            }
            return parent;
        }
    }

    /**
     * 获取该节点最左节点
     * @param node
     * @return
     */
    public static NodeNew getLeftMost(NodeNew node) {
        if (node.left == null) {
            return node;
        }
        while (node.left != null) {
            node = node.left;
        }
        return node;
    }

    /**
     * 按照先序遍历序列化二叉树
     * @param head
     * @return
     */
    public static String serialByPre(Node head) {
        if (head == null) {
            return "#!";
        }
        String res = head.value + "!";
        res += serialByPre(head.left);
        res += serialByPre(head.right);
        return res;
    }

    /**
     * 二叉树的反序列化
     * @param preStr
     * @return
     */
    public static Node reconByPreString(String preStr) {
        // 将value按照!划分出来
        String[] values = preStr.split("!");
        LinkedList<String> queue = new LinkedList<String>();
        // 放入队列
        for (int i = 0; i != values.length; i++) {
            queue.offer(values[i]);
        }
        return reconPreOrder(queue);
    }

    /**
     * 递归调用
     * @param queue
     * @return
     */
    public static Node reconPreOrder(LinkedList<String> queue) {
        String value = queue.poll();
        // #表示空
        if (value.equals("#")) {
            return null;
        }
        Node head = new Node(Integer.valueOf(value));
        head.left = reconPreOrder(queue);
        head.right = reconPreOrder(queue);
        return head;
    }

    /**
     * 对折纸张的折痕问题
     * 看做一个中序遍历二叉树的问题
     * @param N
     */
    public static void printAllFolds(int N) {
        printProcess(1, N, true);
    }

    /**
     * 题意是从上至下,二叉树就需要先右后左
     * 右是下 down,左是上 up
     * @param i 当前层数,也是当前次数
     * @param N 总层数,也是总次数
     * @param down
     */
    public static void printProcess(int i, int N, boolean down) {
        if (i > N) {
            return;
        }
        printProcess(i + 1, N, true);
        System.out.println(down ? "down " : "up ");
        printProcess(i + 1, N, false);
    }

    public static void main(String[] args) {
        Node head = new Node(5);
        head.left = new Node(3);
        head.right = new Node(8);
        head.left.left = new Node(2);
        Node node2 = new Node(4);
        head.left.right = node2;
        Node node1 = new Node(1);
        head.left.left.left = node1;
        head.right.left = new Node(7);
        head.right.left.left = new Node(6);
        head.right.right = new Node(10);
        head.right.right.left = new Node(9);

        head.right.right.right = new Node(11);
        head.left.right.left = new Node(12);
        head.left.right.right = new Node(13);
        head.left.left.right = new Node(14);

        Node head1 = new Node(9);
        head1.left = new Node(3);
        head1.left.left = new Node(4);

        // recursive
        System.out.println("==============recursive==============");
        System.out.print("pre-order: ");
        preOrderRecur(head);
        System.out.println();
        System.out.print("in-order: ");
        inOrderRecur(head);
        System.out.println();
        System.out.print("pos-order: ");
        posOrderRecur(head);
        System.out.println();

        // unrecursive
        System.out.println("============unrecursive=============");
        preOrderUnRecur(head);
        inOrderUnRecur(head);
        posOrderUnRecur1(head);
        posOrderUnRecur2(head);

        // maxWidth
        int maxWidth = getMaxWidth(head);
        System.out.println(maxWidth);

        // isBST
        boolean bst = isBST(head);
        System.out.println(bst);

        // isBalanced
        boolean balanced = isBalanced(head);
        System.out.println(balanced);
        boolean balanced1 = isBalanced(head1);
        System.out.println(balanced1);

        // 最低公共祖先
        Node node = lowestAncestor(head, node1, node2);
        System.out.println(node.value);

        // 后继节点
        NodeNew head2 = new NodeNew(6);
        head2.parent = null;
        head2.left = new NodeNew(3);
        head2.left.parent = head2;
        head2.left.left = new NodeNew(1);
        head2.left.left.parent = head2.left;
        head2.left.left.right = new NodeNew(2);
        head2.left.left.right.parent = head2.left.left;
        head2.left.right = new NodeNew(4);
        head2.left.right.parent = head2.left;
        head2.left.right.right = new NodeNew(5);
        head2.left.right.right.parent = head2.left.right;
        head2.right = new NodeNew(9);
        head2.right.parent = head2;
        head2.right.left = new NodeNew(8);
        head2.right.left.parent = head2.right;
        head2.right.left.left = new NodeNew(7);
        head2.right.left.left.parent = head2.right.left;
        head2.right.right = new NodeNew(10);
        head2.right.right.parent = head2.right;

        NodeNew test = head2.left.left;
        System.out.println(test.value + " next: " + getSuccessorNode(test).value);

        // 序列化与反序列化
        String s = serialByPre(head);
        String s1 = serialByPre(head1);
        System.out.println(s);
        System.out.println(s1);

        Node node3 = reconByPreString(s);
        Node node4 = reconByPreString(s1);
        System.out.println(node3.value);
        System.out.println(node4.value);

    }
}

 

posted @ 2022-03-12 13:37  北漂的尘埃  阅读(22)  评论(0编辑  收藏  举报