数据结构之巅:揭秘树的奥秘与力量

61 阅读8分钟

数据结构之巅:揭秘树的奥秘与力量

关于作者

  • 作者介绍

🍓 博客主页:作者主页
🍓 简介:JAVA领域优质创作者🥇、一名初入职场小白🎓、曾在校期间参加各种省赛、国赛,斩获一系列荣誉🏆
🍓 关注我:关注我学习资料、文档下载统统都有,每日定时更新文章,励志做一名JAVA资深程序猿👨‍💻


二叉树

结构描述:

Class Node {
	V value;
	Node left;
	Node right;
}

先序、中序、后序遍历

  • 先序:任何子树的处理顺序都是,先头节点、再左子树、然后右子树
  • 中序:任何子树的处理顺序都是,先左子树、再头节点、然后右子树
  • 后序:任何子树的处理顺序都是,先左子树、再右子树、然后头节点

递归方式

  1. 理解递归序
  2. 先序、中序、后序都可以在递归序的基础上加工出来
  3. 第一次到达一个节点就打印就是先序、第二次打印即中序、第三次即后序
package com.cz.Tree;

/**
 * @ProjectName: 递归版本遍历树
 * @Package: com.cz.Tree
 * @ClassName: RecursiveTraversalTree
 * @Author: 张晟睿
 * @Date: 2022/3/20 14:53
 * @Version: 1.0
 */
public class RecursiveTraversalTree {
    public static void main(String[] args) {
        Node head = new Node(1);
        head.left = new Node(2);
        head.right = new Node(3);
        head.left.left = new Node(4);
        head.left.right = new Node(5);
        head.right.left = new Node(6);
        head.right.right = new Node(7);
//        1
//        23
//        4567


        pre(head);
        System.out.println("先序========");
        middle(head);
        System.out.println("中序========");
        post(head);
        System.out.println("后序========");
    }
    public static class Node {
        public int value;
        public Node left;
        public Node right;

        public Node(int val) {
            value = val;
        }
    }
    public static void pre(Node head){
        if(head == null) return;
        System.out.print(head.value + " ");
        pre(head.left);
        pre(head.right);
    }
    public static void middle(Node head){
        if(head == null) return;
        middle(head.left);
        System.out.print(head.value + " ");
        middle(head.right);
    }
    public static void post(Node head){
        if(head == null) return;
        post(head.left);
        post(head.right);
        System.out.print(head.value + " ");
    }
}

非递归方式

  1. 任何递归函数都可以改成非递归
  2. 自己设计压栈的来实现
package com.cz.Tree;

import java.util.Stack;

/**
 * @ProjectName: Data_structure
 * @Package: com.cz.Tree
 * @ClassName: UnRecursiveTraversalTree
 * @Author: 张晟睿
 * @Date: 2022/3/20 16:06
 * @Version: 1.0
 */
public class UnRecursiveTraversalTree {
    public static void main(String[] args) {
        Node1 head = new Node1(1);
        head.left = new Node1(2);
        head.right = new Node1(3);
        head.left.left = new Node1(4);
        head.left.right = new Node1(5);
        head.right.left = new Node1(6);
        head.right.right = new Node1(7);

        pre(head);
        System.out.println("========");
        middle(head);
        System.out.println("========");
        post(head);
        System.out.println("========");
    }
    public static class Node1 {
        public int value;
        public Node1 left;
        public Node1 right;

        public Node1(int val) {
            value = val;
        }
    }

    public static void pre(Node1 head) {
        System.out.println("先序遍历:");
        Stack<Node1> s = new Stack<>();
        if(head != null) {
            s.push(head);
            while(!s.isEmpty()) {
                Node1 node = s.pop();
                System.out.print(node.value + " ");
                if(node.right != null) s.push(node.right);
                if(node.left != null) s.push(node.left);
            }
        }
        System.out.println();
    }

    public static void middle(Node1 head){
        System.out.println("中序遍历:");
        if (head != null) {
            Stack<Node1> s = new Stack<>();
            while(!s.isEmpty() || head != null) {
                //步骤1:如果头结点不为空的话,一直向左边执行
                if (head != null) {
                    s.push(head);
                    head = head.left;
                }else {//根节点打印后,来到右树,继续执行步骤1
                    head = s.pop();
                    System.out.print(head.value + " ");
                    head = head.right;
                }
            }
            System.out.println();
        }
    }

    public static void post(Node1 head){
        System.out.println("后序遍历:");
        if(head != null) {
            Stack<Node1> s = new Stack<>();
            s.push(head);
            Node1 c = null; //指向栈顶的某个元素的位置
            while(!s.isEmpty()) {
                c = s.peek();
                //判断c左孩子 是否已经处理过
                if(c.left != null && head != c.left && head != c.right) {
                    s.push(c.left);
                    //判断c右孩子 是否已经处理过
                }else if(c.right != null && head != c.right) {
                    s.push(c.right);
                }else {
                    System.out.print(s.pop().value+" ");
                    head = c;   //head用来记录上次打印的内容
                }
            }
            System.out.println();
        }
    }
}

层序遍历

  1. 其实就是宽度优先遍历,用队列
  2. 可以通过设置flag变量的方式,来发现某一层的结束(看题目)
package com.cz.Tree;

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

/**
 * @ProjectName: 层序遍历
 * @Package: com.cz.Tree
 * @ClassName: LevelTraversalTree
 * @Author: 张晟睿
 * @Date: 2022/4/7 21:04
 * @Version: 1.0
 */
public class LevelTraversalTree {
    public static class Node {
        public int value;
        public Node left;
        public Node right;

        public Node(int val) {
            value = val;
        }
    }
    //层序遍历
    public static void level(Node head) {
        if(head == null) return ;

        Queue<Node> queue = new LinkedList<>();
        queue.add(head);
        while(!queue.isEmpty()) {
            Node cur = queue.poll();
            System.out.print(cur.value + ",");
            if(cur.left != null) {
                queue.add(cur.left);
            }
            if (cur.right != null) {
                queue.add(cur.right);
            }
        }
    }

    public static void main(String[] args) {
        Node head = new Node(1);
        head.left = new Node(2);
        head.right = new Node(3);
        head.left.left = new Node(4);
        head.left.right = new Node(5);
        head.right.left = new Node(6);
        head.right.right = new Node(7);
        level(head);
    }
}

序列化和反序列化

  1. 可以用先序或者中序或者后序或者按层遍历,来实现二叉树的序列化
  2. 用了什么方式序列化,就用什么样的方式反序列化
package com.cz.Tree;

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

/**
 * @ProjectName: 二叉树的序列化和反序列化
 * @Package: com.cz.Tree
 * @ClassName: SerializableTree
 * @Author: 张晟睿
 * @Date: 2022/4/8 10:58
 * @Version: 1.0
 */
public class SerializableTree {
    public static class Node {
        public int value;
        public Node left;
        public Node right;

        public Node(int val) {
            value = val;
        }
    }
    //先序 序列化
    public static Queue<String> preSerializable(Node head) {
        Queue<String> ans = new LinkedList<>();
        pre(head,ans);
        return ans;
    }

    public static void pre(Node head, Queue<String> ans) {
        if(head == null) ans.add(null);
        else {
            ans.add(String.valueOf(head.value));
            pre(head.left,ans);
            pre(head.right,ans);
        }
    }
    //先序反序列化
    public static Node buildByPreQueue(Queue<String> prelist){
        if (prelist == null || prelist.size() <= 0) return null;
        return preBuild(prelist);
    }

    public static Node preBuild(Queue<String> prelist) {
        String val = prelist.poll();
        if( val == null) return null;
        Node head = new Node(Integer.valueOf(val));
        head.left = preBuild(prelist);
        head.right = preBuild(prelist);
        return head;
    }

    //层序 序列化
    public static Queue<String> levelSerializable(Node head){
        Queue<String> ans = new LinkedList<>();
        if(head == null) {
            ans.add(null);
        }else {
            ans.add(String.valueOf(head.value));
            Queue<Node> queue = new LinkedList<>();
            queue.add(head);
            while(!queue.isEmpty()) {
                head = queue.poll();
                if (head.left != null) {
                    ans.add(String.valueOf(head.left.value));
                    queue.add(head.left);
                }else {
                    ans.add(null);
                }

                if (head.right != null) {
                    ans.add(String.valueOf(head.right.value));
                    queue.add(head.right);
                }else {
                    ans.add(null);
                }
            }
        }
        return ans;
    }

    public static Node buildByLevelQueue(Queue<String> levellist){
        if(levellist == null || levellist.size() <= 0) return null;
        Node head = generateNode(levellist.poll());
        Queue<Node> queue = new LinkedList<>();
        queue.add(head);
        Node node = null;
        while(!queue.isEmpty()) {
            node = queue.poll();
            node.left = generateNode(levellist.poll());
            node.right = generateNode(levellist.poll());
            if (node.left != null) {
                queue.add(node.left);
            }
            if (node.right != null) {
                queue.add(node.right);
            }
        }
        return head;
    }

    public static Node generateNode(String val) {
        if(val == null) return null;
        return new Node(Integer.valueOf(val));
    }

    public static void prePrintf(Node head) {
        Queue<Node> queue = new LinkedList<>();
        queue.add(head);
        while(!queue.isEmpty()) {
            Node node = queue.poll();
            System.out.print(node.value + ",");
            if (node.left != null) queue.add(node.left);
            if (node.right != null) queue.add(node.right);
        }
    }

    public static void main(String[] args) {
        Node head = new Node(1);
        head.left = new Node(2);
        head.right = new Node(3);
        head.left.left = new Node(4);
        head.left.right = new Node(5);
        head.right.right = new Node(7);
        System.out.println("先序序列化:"+preSerializable(head));
        System.out.print("先序反序列化:");
        System.out.println(buildByPreQueue(preSerializable(head)));

        System.out.println();
        System.out.println("层序序列化:"+levelSerializable(head));
        System.out.print("层序反序列化:");
        System.out.println(buildByLevelQueue(levelSerializable(head)));
    }
}

练习

1、设计一个打印整棵树的打印函数

package com.cz.Tree;

/**
 * @ProjectName: Data_structure
 * @Package: com.cz.Tree
 * @ClassName: printBinaryTree
 * @Author: 张晟睿
 * @Date: 2022/4/9 16:07
 * @Version: 1.0
 */
public class printBinaryTree {
    public static class Node {
        public int value;
        public Node left;
        public Node right;

        public Node(int val) {
            value = val;
        }
    }

    public static void printBinaryT(Node head) {
        System.out.println("BinartTree:");
        printInOrder(head, 0, "H", 17);
        System.out.println();
    }

    /**
     *
     * @param    head 树的节点
     * @param    height 当前节点的高度
     * @param to    用一个字符来表示是左孩子(\)还是右孩子(/)还是头节点(H)
     * @param len   规定每个节点值的宽度
     */
    public static void printInOrder(Node head, int height, String to, int len){
        if(head == null) return ;
        printInOrder(head.right, height + 1, "/", len);

        String val = to + head.value + to;
        int lenM = val.length();
        int lenL = (len - lenM) >> 1;
        int lenR = len - lenM - lenL;
        val = getspace(lenL) + val + getspace(lenR);
        System.out.println(getspace(height * len) + val);

        printInOrder(head.left, height + 1, "\\", len);
    }

    public static String getspace(int num){
        String space = " ";
        StringBuffer sb = new StringBuffer("");
        for (int i = 0; i < num; i++) {
            sb.append(space);
        }
        return sb.toString();
    }
    public static void main(String[] args) {
        Node head = new Node(1);
        head.left = new Node(2);
        head.right = new Node(3);
        head.left.left = new Node(4);
        head.left.right = new Node(5);
        head.right.left = new Node(6);
        head.right.right = new Node(7);
        printBinaryT(head);

        Node head1 = new Node(1);
        head1.left = new Node(Integer.MIN_VALUE);
        head1.right = new Node(7777);
        head1.left.left = new Node(798);
        head1.left.right = new Node(56);
        head1.right.left = new Node(666666);
        head1.right.right = new Node(520);
        printBinaryT(head1);
    }
}

image-20220409171047456

2、求二叉树最宽的层有多少个节点

package com.cz.Tree;

import com.cz.Queuearray.LinkedListDemo;

import java.io.Serializable;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Queue;

/**
 * @ProjectName: Data_structure
 * @Package: com.cz.Tree
 * @ClassName: TreeMaxWidth
 * @Author: 张晟睿
 * @Date: 2022/4/7 21:14
 * @Version: 1.0
 */
public class TreeMaxWidth {
    public static class Node {
        public int value;
        public Node left;
        public Node right;

        public Node(int val) {
            value = val;
        }
    }
    public static int MaxWidthuseMap(Node head) {
        if(head == null) return 0;
        Queue<Node> queue = new LinkedList<>();
        Map<Node, Integer> levelMap = new HashMap<>();
        queue.add(head);
        levelMap.put(head, 1);
        int curLevel = 1;//记录当前统计的在那一层
        int curLevelNode = 0;//记录curLevel层的宽度
        int max = 0;
        int curNodelevel = 1;
        while (!queue.isEmpty()) {
            Node cur = queue.poll();
            curNodelevel = levelMap.get(cur);//当前节点在那一层

            if (cur.left != null) {
                levelMap.put(cur.left , curNodelevel + 1);
                queue.add(cur.left);
            }
            if (cur.right != null) {
                levelMap.put(cur.right, curNodelevel + 1);
                queue.add(cur.right);
            }

            if (curNodelevel == curLevel) {//判断当前的层与统计的层在同一层,节点的个数+1
                curLevelNode++;
            }else {//当前的层与统计的层不在同一层
                max = Math.max(max, curLevelNode);
                curLevel++;//将统计的那一层下移
                curLevelNode = 1;//当前下一层的第一个节点已经出了队列所以宽度为1
            }
        }
        max = Math.max(max, curLevelNode);
        System.out.println("curNodelevel = " + curNodelevel);
        return max;
    }

    public static int MaxWidthnoMap(Node head) {
        if(head == null) {
            return 0;
        }
        Queue<Node> queue = new LinkedList<>();
        queue.add(head);
        Node curEnd = head; //记录当前统计层的最后节点
        Node nextEnd = null;//记录当前统计层的下一层最后节点
        int curLevelNodes = 0;//记录当前统计层的节点个数,也就是宽度
        int max = 0;//用来存放最大宽度
        while(!queue.isEmpty()) {
            Node cur = queue.poll();
            if (cur.left != null) {
                queue.add(cur.left);
                nextEnd = cur.left;
            }
            if (cur.right != null) {
                queue.add(cur.right);
                nextEnd = cur.right;
            }
            curLevelNodes++;
            if(cur == curEnd) {
                max = Math.max(max, curLevelNodes);
                curLevelNodes = 0;
                curEnd = nextEnd;
            }
        }
        return max;
    }

    public static void main(String[] args) {
        Node head = new Node(1);
        head.left = new Node(2);
        head.right = new Node(3);
        head.left.left = new Node(4);
        head.left.right = new Node(5);
        head.right.left = new Node(6);
        head.right.right = new Node(7);
        //暴力
        System.out.println(MaxWidthuseMap(head));
        System.out.println(MaxWidthnoMap(head));

    }
}

3、给你二叉树中的某个节点,返回该节点的后继节点

二叉树结构如下定义(带父亲节点):

Class Node {
	V value;
	Node left;
	Node right;
	Node parent;	//指向父节点
}

给你二叉树中的某个节点,返回该节点的后继节点

package com.cz.Tree;

/**
 * @ProjectName: 中序遍历的后继节点
 * @Package: com.cz.Tree
 * @ClassName: SuccessOrTree
 * @Author: 张晟睿
 * @Date: 2022/4/10 16:50
 * @Version: 1.0
 */
public class SuccessOrTree {
    public static class Node {
        public int value;
        public Node left;
        public Node right;
        public Node parent;

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

        @Override
        public String toString() {
            return "Node{" +
                    "value=" + value +
                    '}';
        }
    }
    //中序遍历 左 根 右
    public static Node getSuccessOrTree(Node node){
        if(node == null) return node;

        if(node.right != null) { //该节点如果有右子树,即根 右(左根右) 找到右数上最左侧的节点,则后继节点就是右数上最左侧的节点
            return goingLeft(node.right);
        }
        else { //该节点无右树,则来到左树
            Node parent = node.parent;
            while(parent != null && parent.right == node) { //如果当前节点是其父亲节点的,必须找到这个节点是他父亲节点的左孩子停止
                node = parent;
                parent = node.parent;
            }
            return parent;
        }
    }

    public static Node goingLeft(Node node){
        if (node == null) return node;

        while(node.left != null) {
            node = node.left;
        }
        return node;
    }

    public static void middle(Node head) {
        if(head == null) return ;
        middle(head.left);
        System.out.print(head.value + " ");
        middle(head.right);
    }
    public static void main(String[] args){
        Node head = new Node(1);
        head.left = new Node(2);
        head.left.parent = head;
        head.right = new Node(3);
        head.right.parent = head;
        head.left.left = new Node(4);
        head.left.left.parent = head.left;
        head.left.right = new Node(5);
        head.left.right.parent = head.left;
        head.right.right = new Node(7);
        head.right.right.parent = head.right;
        //中序遍历 4,2,5,1,3,7
        System.out.println("中序遍历");
        middle(head);
        System.out.println();
        System.out.println(getSuccessOrTree(head.left.right));
        System.out.println(getSuccessOrTree(head.left.left));
        System.out.println(getSuccessOrTree(head));
    }
}

4、给定一个输入参数N,代表纸条都从下边向上方连续对折N次。 请从上到下打印所有折痕的方向

请把一段纸条竖着放在桌子上,然后从纸条的下边向上方对折1次,压出折痕后展开。此时折痕是凹下去的,即折痕突起的方向指向纸条的背面。 如果从纸条的下边向上方连续对折2次,压出折痕后展开,此时有三条折痕,从上到下依次是下折痕、下折痕和上折痕。

例如:N=1时,打印: down N=2时,打印: down down up

package com.cz.Tree;

/**
 * @ProjectName: Data_structure
 * @Package: com.cz.Tree
 * @ClassName: PaperFolding
 * @Author: 张晟睿
 * @Date: 2022/4/10 17:23
 * @Version: 1.0
 */
public class PaperFolding {

    public static void printAllFolds(int N){
        printProcess(1, N, true);
    }
    /**
     *  递归过程
     * @param i 节点的层数
     * @param N 一共N层
     * @param down  down==true 凹    down==false 凸
     */
    public static void printProcess(int i, int N, boolean down){
        if (i > N) return ;

        printProcess(i + 1, N , true);
        System.out.println(down ? "凹" : "凸");
        printProcess(i + 1, N , false);
    }

    public static void main(String[] args) {
        int N = 3;
        printAllFolds(N);
    }
}