简单二叉树的介绍

news2024/12/29 10:18:56

1.树的结构(了解)

1.1概念

树是一种非线性的数据结构,它是由n(n>=0)个有限节点总成一个具有层次关系的集合。把它叫做树是因为它看起来像一颗倒挂的树,也就是说它的根是朝上,而叶子是朝下的(本人而言更像一个类似于植物根),它有以下的特点:

  • 有一个特殊的节点,称为根节点,根节点没有前驱节点;

  • 除根节点外,其余节点被分成M(M>0)个互补相交的集合T1、T2........Tm,其中每个集合Ti(1<=i<=m)又是一颗与树类似的子树。每棵子树的根节点有且只有一个前驱,可以有0个或者多个后继;

  • 树是递归定义的(很多方法都要使用递归来解决)

【注意】树形结构中,子树间是没有交集的,否则就不是线性结构(如下图就不是树结构)

1.2树的相关概念

节点的度:一个节点含有子树的个数称为该节点的度;如上图A的度为2,B的度为2;

树的度:一颗树中,所有节点度的最大值为树的度;如上图节点的度的最大值是2,所以树的度为2;

叶子节点或终端节点:度为0的节点称为叶节点;如上图的DEFG为叶子节点或终端节点;

双亲节点或父节点:如一个节点含有子节点,则这个节点称为其子节点的父节点;如上图D的父节点是B;

根节点:一颗树中,没有双亲节点的节点,也是树开始的节点;如上图的A;

节点的层次:从根开始定义起,根为第一层,根的子节点是第二层,依次类推;

树的高度或深度:树中节点的最大层次;如上图树的高度为3;

以下概念了解即可:

非终端节点或分支节点:度不为0的节点;如上图的ABC为分支节点或非终端节点;

兄弟节点:具有相同父节点的节点互为兄弟节点;如上图的BC为兄弟节点;

堂兄弟节点:双亲在同一层的节点互为堂兄弟;如上图的DF为堂兄弟节点;

节点的祖先:从根节点到该节点所经分支的所有节点;如上图的D的祖先是ABD

子孙:以某个节点为根的子树中任一节点都称为该节点的子孙。如上图都是A的子孙;

森林:由M(M>=0)棵互不相交的树组成的集合称为森林。

1.3树的表示形式(了解)

树结构相对于线性结构复杂很多,要粗存起来就比较复杂了,实际中

树有很多表示方式,比如双亲表示法,孩子表示法,孩子双亲表示法,孩子兄弟表示法等等。我们这里简单了解以下孩子兄弟表示法

//孩子兄弟表示法
class Node {
    int val;
    Node firstChild;  //第一个孩子
    Node nextBrother; //下一个兄弟引用
}

1.4树的应用

文件系统管理(目录和文件)

图并不完全,但足以看出是孩子兄弟表示法。

2.二叉树

2.1概念

一棵二叉树是节点的一个有限集合,该集合:

  • 可以为空

  • 或者由一个根节点加上两颗别称为左子树右子树的二叉树组成。

从上图可以看出:

  • 二叉树不存在度大于2的节点;

  • 二叉树的子树有左右之分,次序不能颠倒,因此二叉树是有序树。

【注意】对于任意的二叉树都有以下几种情况复合而成的:

自然阶中的“二叉树”

2.2两种特殊的二叉树

1.满二叉树:一棵二叉树,如果每层的节点数都达到最大值,则这棵二叉树就是满二叉树。也就是说,如果一颗二叉树的层数为K,且节点总数是2的K次方-1,则它就是满二叉树

2.完全二叉树:完全二叉树是效率很高的数据结构,完全二叉树是由满二叉树而引出来的。对于深度为K的,有n个节点的二叉树,当且仅当其每一个节点都与深度为K的满二叉树中编号从0到n-1的节点一一对应时称之为完全二叉树。要注意满二叉树是一种特殊的完全二叉树。

错误示范:只是普通的二叉树。

2.3二叉树的性质

  • 若根节点的层数为1,则一颗非空二叉树的第i层上最多有2的i-1次方个节点

  • 若只有根节点的二叉树的深度为1,则深度为K的二叉树的最大节点数是2的k次方-1

  • 对于任何一个二叉树,如果其叶节点个数为n0,度为2的非叶子节点个数为n2,则有n0=n2+1;

【推导】

  • 有n个节点的完全二叉树的深度为log以2为底n+1取上整

  • 对于具有n个节点的完全二叉树,如果从上至下从左至右的顺序对所有节点从0开始编码,则对序列号为i的节点有:

  1. 若i>0,双亲序号:(i-1)/2;i=0,i为根节点标号,无双亲节点

  1. 若2i+1<n,左孩子序号为2i+1,否则无左孩子;

  1. 若2i+2<n,右孩子序号为2i+2,否则无右孩子。

1.某二叉树共有399个节点,其中199个度为2的节点,则该二叉树的叶子节点的个数是( 200);
2.在具有2n个节点的完全二叉树中,叶子节点个数为( n

【解析】

3.一个具有767个节点的完全二叉树,其叶子节点个数为( 384
4.一颗完全二叉树的节点数是531,那么这棵树的高度为( 10)(利用公式即可得出)

2.4二叉树的储存

二叉树的存储结构分为:顺序储存类似于链表的链式储存

顺序储存在以后进行讲解。

二叉树的链式储存是通过一个个的节点引用起来的,常见的表示方式有二叉和三叉表示方式,具体如下

//孩子表示法
class Node1 {
    int val;
    Node1 left;//左孩子
    Node1 right;//右孩子
}    

//孩子双亲表示法
class Node2 {
    int val;
    Node2 left;//左孩子的引用
    Node2 right;//有孩子的引用
    Node2 parent;//父节点的引用
}

孩子双亲表示法后续在平衡树的位置介绍,本文采用的是孩子表示法来构建二叉树。

2.5二叉树的基本操作

2.5.1前置说明

在学习二叉树的基本操作前,需先要创建一颗二叉树,然后才能学习其相关的基本操作。由于现在大家对二叉树结构掌握还不够深入,为了降低大家的学习成本,此处手动快速创建一棵简单的二叉树,快速进入二叉树操作的学习,等到二叉树结构了解差不多的时候,我们发过来了解二叉树真正的创建方式

//简单创建一个二叉树
class BinaryTree {
    public static class Node{
        Node left;
        Node right;
        int val;

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

    private Node root;

    //并不是真正创建二叉树的方式,但这里我们只是方便理解
    public void createBinaryTree() {
        Node n1 = new Node(1);
        Node n2 = new Node(2);
        Node n3 = new Node(3);
        Node n4 = new Node(4);
        Node n5 = new Node(5);
        Node n6 = new Node(6);

        root = n1;
        n1.left = n2;
        n1.right = n3;
        n2.left = n4;
        n2.right = n5;
        n3.left = n6;
    }
}

2.5.2二叉树的遍历

1.前中后序遍历

遍历:指沿着某条搜索路线,依次对树中每个节点均作一次且仅作一次访问。访问节点所做的操作依靠于具体的应用问题(比如:打印节点的内容,节点内容+1)。遍历二叉树是最重要的操作之一,又是最基础的操作。

在遍历二叉树时,如果没有进行某种约定,每个人都可以按照自己的想法来实现,那么所得到的结果比较混乱。如果我们按照某种规则进行约定,则每个人对于同一棵树的遍历结构是相同的。如果N代表根节点,L代表根节点的左子树,R代表根节点的右子树,则根据遍历根节点的先后次序有以下遍历方式:

  • NLR:前序遍历:先访问根节点,再访问根的左子树,最后访问根的右子树。

  • LNR:中序遍历:先访问根的左子树,再访问根节点,最后访问根的右子树。

  • LRN:后续遍历:先访问根的左子树,再访问根的右子树,最后访问根节点

我们根据自己的理解画图

前序遍历的结果:1 2 3 4 5 6

中序遍历的结果:3 2 1 5 4 6

后续遍历的结果:3 2 5 6 4 1

接下来我们完成前中后序遍历的代码:

//简单创建一个二叉树
class BinaryTree {
    public static class Node{
        Node left;
        Node right;
        int val;

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

    private Node root;

    //并不是真正创建二叉树的方式,但这里我们只是方便理解
    public void createBinaryTree() {
        Node n1 = new Node(1);
        Node n2 = new Node(2);
        Node n3 = new Node(3);
        Node n4 = new Node(4);
        Node n5 = new Node(5);
        Node n6 = new Node(6);

        root = n1;
        n1.left = n2;
        n1.right = n3;
        n2.left = n4;
        n2.right = n5;
        n3.left = n6;
    }

    //前序遍历
    public void preOrder(Node root) {
        if(root == null) {
            return;
        }
        System.out.print(root.val + " ");
        preOrder(root.left);
        preOrder(root.right);
    }
    //中序遍历
    public void inOrder(Node root) {
        if(root == null) {
            return;
        }
        inOrder(root.left);
        System.out.print(root.val + " ");
        inOrder(root.right);
    }
    //后续遍历
    public void postOrder(Node root) {
        if(root == null) {
            return;
        }
        postOrder(root.left);
        postOrder(root.right);
        System.out.print(root.val + " ");
    }

    public static void main(String[] args) {
        BinaryTree tree = new BinaryTree();
        tree.createBinaryTree();
        tree.preOrder(tree.root);
        System.out.println();
        tree.inOrder(tree.root);
        System.out.println();
        tree.postOrder(tree.root);
    }
}

2.层序遍历

层序遍历:除了先序遍历、中序遍历、后续遍历外,还可以对二叉树进行层序遍历。设二叉树的根节点所在的层数为1,层序遍历就是从所在二叉树的根节点出发,首先访问第一层的根节点,让后从左往右访问第二层上的根节点,依次类推,自上而下从左往右逐层的访问树的节点的过程就是层序遍历。

【选择题】

  1. 某个完全二叉树按层次输出(同一层从左到右)的序列为ABCDEFGH。则该完全二叉树的前序遍历顺序为(ABDHECFG

  1. 二叉树的前序遍历和中序遍历如下:先序遍历:EFHIGJK,中序遍历:HFIEJKG,则二叉树的后续遍历是(HIFKJGE

  1. 设一颗二叉树的中序遍历:badce,后序遍历:bdeca,则二叉树前序为(abcde

  1. 某二叉树的后序遍历和中序遍历均为ABCDEF,按照层次输出(从一层从左到右)的序列为(FEDCBA

2.5.3模拟实现二叉树的基本操作

//模拟实现二叉树的基本操作
class Node{
    int val;
    Node left;
    Node right;

    public Node(int val) {
        this.val = val;
    }
}
class Tree {
    Node root;

    public Tree(){

    }
    public Tree(Node root) {
        this.root = root;
    }

    // 获取树中节点的个数
    int size(Node root){
        if(root == null) {
            return 0;
        }
        int leftCount = size(root.left);
        int rightCount = size(root.right);
        return leftCount + rightCount + 1;
    }

    // 获取叶子节点的个数
    int getLeafNodeCount(Node root) {
        if(root == null) {
            return 0;
        }
        if(root.left == null && root.right == null) {
            return 1;
        }
        int leftCount = getLeafNodeCount(root.left);
        int rightCount = getLeafNodeCount(root.right);
        return leftCount + rightCount;
    }

    // 获取第K层节点的个数
    int getKLevelNodeCount(Node root,int k) {
        if(k == 0 || root == null) {
            return 0;
        }
        if(k == 1) {
            return 1;
        }
        int count = 0;
        return getKLevelNodeCount(root.left, k - 1) + getKLevelNodeCount(root.right, k - 1);

    }
    // 获取二叉树的高度
    int getHeight(Node root) {
        if(root == null) {
            return 0;
        }
        int leftCount = getHeight(root.left);
        int rightCount = getHeight(root.right);
        return ((leftCount > rightCount) ? leftCount:rightCount) + 1;
    }
    // 检测值为value的元素是否存在
    Node find(Node root, int val) {
        if(root == null) {
            return null;
        }
        if(root.val == val) {
            return root;
        }
        Node ret1 = find(root.left, val);
        Node ret2 = find(root.right, val);
        if(ret1 != null) {
            return ret1;
        }
        return ret2;
    }
    //这两个较难,我们最后再看
    //层序遍历
    void levelOrder(Node root) {
        if(root == null) {
            return;
        }
        Queue<Node> q = new LinkedList<>();
        Node cur = root;
        q.offer(cur);
        while (!q.isEmpty()) {
            cur = q.poll();
            System.out.print(cur.val + " ");
            if(cur.left != null) {
                q.offer(cur.left);
            }
            if(cur.right != null) {
                q.offer(cur.right);
            }
        }
        System.out.println();
    }
    // 判断一棵树是不是完全二叉树
    boolean isCompleteTree(Node root) {
        if(root == null) {
            return true;
        }
        Node cur = root;
        Queue<Node> q = new LinkedList<>();
        q.offer(cur);
        while(true) {
            cur = q.poll();
            if(cur != null) {
                q.offer(cur.left);
                q.offer(cur.right);
            }else {
                break;
            }
        }
        while(!q.isEmpty()) {
            cur = q.poll();
            if(cur != null) {
                return false;
            }
        }
        return true;
    }
}

public class Test {
    public static void main(String[] args) {
        Node n1 = new Node(1);
        Node n2 = new Node(2);
        Node n3 = new Node(3);
        Node n4 = new Node(4);
        Node n5 = new Node(5);
        Node n6 = new Node(6);
        Tree t = new Tree();
        t.root = n1;
        n1.left = n2;
        n1.right = n3;
        n2.left = n4;
        n2.right = n5;
        n3.left = n6;
        System.out.println("=================输出节点个数================");
        System.out.println(t.size(t.root));
        System.out.println("=================输出叶子节点数===============");
        System.out.println(t.getLeafNodeCount(t.root));
        System.out.println("================输出第K层节点个数==============");
        System.out.println(t.getKLevelNodeCount(t.root, 2));
        System.out.println("================二叉树的高度=================");
        System.out.println(t.getHeight(t.root));
        System.out.println("================检测是否包含某一元素============");
        System.out.println(t.find(t.root, 5));//这里不能直接输出值,防止为空时报错;
        System.out.println(t.find(t.root, 9));
        System.out.println("=================完全二叉树的判断=============");
        System.out.println(t.isCompleteTree(t.root));
        System.out.println("=================层序遍历====================");
        t.levelOrder(t.root);
    }
}

2.6二叉树相关习题

  1. 检查两棵树是否相同。

//检查两颗树是否相同
class TreeNode {
     int val;
     TreeNode left;
     TreeNode right;
     TreeNode() {}
     TreeNode(int val) { this.val = val; }
     TreeNode(int val, TreeNode left, TreeNode right) {
         this.val = val;
         this.left = left;
         this.right = right;
    }
}
class Solution {
    public boolean isSameTree(TreeNode p, TreeNode q) {
        if(q == null && p == null) {
            return true;
        }else if(q == null || p == null) {
            return false;
        }
        if (q.val != p.val) {
            return false;
        }
        boolean ret1 = isSameTree(p.left, q.left);
        boolean ret2 = isSameTree(p.right, q.right);
        return ret1 && ret2;
    }
}
  1. 检测一棵树是否是另一棵树的子树(自身也是自身的子树)

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    //检查两颗树是否相同
    public boolean isSameTree(TreeNode p, TreeNode q) {
        if(q == null && p == null) {
            return true;
        }else if(q == null || p == null) {
            return false;
        }
        if (q.val != p.val) {
            return false;
        }
        boolean ret1 = isSameTree(p.left, q.left);
        boolean ret2 = isSameTree(p.right, q.right);
        return ret1 && ret2;
    }
    //检测一棵树是否是另一棵树的子树(自身也是自身的子树)
    public boolean isSubtree(TreeNode root, TreeNode subRoot) {
        if (subRoot == null) {
            return true;
        }
        if(root == null) {
            return false;
        }
        boolean ret = isSameTree(root, subRoot);
        if(ret) {
            return true;
        }
        boolean ret1 = isSubtree(root.left, subRoot);
        boolean ret2 = isSubtree(root.right, subRoot);
        return ret1 || ret2;
    }
}
  1. 反转二叉树

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public TreeNode invertTree(TreeNode root) {
if(root == null) {
            return null;
        }
        if(root.left == null && root.right == null) {
            return root;
        }
        invertTree(root.left);
        invertTree(root.right);
        TreeNode tmp;
        tmp = root.left;
        root.left = root.right;
        root.right = tmp;
        return root;
    }
}
  1. 判断一颗二叉树是否是平衡树

【平衡树】每颗二叉树的左右子树的高度差不大于1

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public boolean isBalanced(TreeNode root) {
        if(getHeight(root) == -1) {
            return false;
        }
        return true;
        

    }
    private int getHeight(TreeNode root) {
        if(root == null) {
            return 0;
        }
        int leftHeight = getHeight(root.left);
        int rightHeight = getHeight(root.right);
        if(leftHeight == -1 || rightHeight == -1) {
            return -1;
        }
        if(Math.abs(leftHeight - rightHeight) > 1 ) {
            return -1;
        }else {
            return Math.max(leftHeight, rightHeight) + 1;
        }
    }
}
  1. 判断一棵树是否是轴对称

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public boolean isSymmetric(TreeNode root) {
        if(root == null) {
            return true;
        }
        return isSymmetric2(root.left, root.right);

    }
    private boolean isSymmetric2(TreeNode root1, TreeNode root2) {
        if(root1 == null && root2 == null) {
            return true;
        }
        if(root1 == null || root2 == null) {
            return false;
        }
        if(root1.val != root2.val) {
            return false;
        }
        boolean ret1 = isSymmetric2(root1.left, root2.right);
        if(ret1 == false) {
            return false;
        }
        boolean ret2 = isSymmetric2(root1.right, root2.left);
        return ret2;
    }
}
  1. 输入一个二叉树的前序遍历,要求构建这个二叉树,然后输出中序遍历的结果。(输入的前序遍历,如果某个节点为null,前序遍历中则用#代替)

import java.util.Scanner;

/**
 * Describe:输入一个二叉树的前序遍历,要求构建这个二叉树,
 * 然后输出中序遍历的结果。(输入的前序遍历,如果某个节点为null,前序遍历中则用#代替)
 * User:lenovo
 * Date:2023-01-18
 * Time:17:08
 */
import java.util.Scanner;

// 注意类名必须为 Main, 不要有任何 package xxx 信息
class Node {
    char val;
    Node left = null;
    Node right = null;

    public Node(char val) {
        this.val = val;
    }

    public Node() {
    }
}
class Main {

    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        while (in.hasNext()) {
            String s = in.nextLine();
            Node root = new Node();
            root = buildTree(root, s);
            show(root);
            System.out.println();
        }

    }
    public static int i = 0;
    public static Node buildTree(Node root, String s) {
        char tmp = s.charAt(i);
        if(tmp == '#') {
            return null;
        }
        root = new Node(tmp);
        i = i + 1;
        root.left = buildTree(root.left, s);
        i++;
        root.right = buildTree(root.right, s);
        return root;
    }
    public static void show(Node root) {
        if(root == null) {
            return;
        }
        show(root.left);
        System.out.print(root.val + " ");
        show(root.right);
    }
}
  1. 二叉树的分层遍历(二叉树常用的方法中有答案)

  • 我们创立一个queue,用于存放二叉树的各个节点;

  • 我们先将root放入队列;

  • 我们弹出队列中的元素并打印,判断此节点的左节点是否为空,不为空,放入队列;右节点是否为空,不为空,放入队列;直到队列完全为空

  • 换行

  1. 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先 。

【公共祖先】从根节点到此节点两个路径相遇的节点。

【解析】我们把它根据是否包含pq中的任意一个可分为三种情况:

  • 左子树包含,右子树不包含

  • 左子树不包含,右子树包含

  • 左右子树都包含,放回根节点

根据这三种情况返回不同的节点即可。

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if(root == null || root == p || root == q) return root;
        TreeNode left = lowestCommonAncestor(root.left, p, q);
        TreeNode right = lowestCommonAncestor(root.right, p, q);
        if(left == null) return right;
        if(right == null) return left;
        return root;
    }
}
  1. 根据一颗树的前序遍历和中序遍历构建二叉树。

【解析】

  • 我们先看前序遍历,第一个就是根节点,把它放入二叉树中,然后在中序遍历中找根节点的坐标;

  • 在中序遍历的根节点的左边是这个树的左子树,右边是右子树

  • 根节点的左子树(根据前序遍历的性质,先根后左再右)按照原来的方法再次执行;

  • 根节点的右子树,按照原来的方法再次执行;

  • 返回根节点;

【难点】前序遍历需要一个全局变量来控制,以便真正能够遍历一遍;每次找到根节点时,我们要知道在中序遍历中这个根节点对应的左子树的范围和右子树的范围

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {

    private int pi = 0;
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        pi = 0;
        return build(preorder, inorder, 0, inorder.length - 1);

    }
    public TreeNode build(int[] preorder, int[] inorder, int ib, int ie) {
        if(ib > ie) {
            return null;
        }
        int key = preorder[pi];
        pi++;
        TreeNode root = new TreeNode(key);
        int i = 0;
        for(i = ib; i < ie; i++) {
            if(inorder[i] == key) {
                break;
            }
        }
        root.left = build(preorder, inorder, ib, i-1);
        root.right = build(preorder, inorder, i+1, ie);
        return root;
    }
}
  1. 根据一颗树的中序遍历和后续遍历构造二叉树(与上题基本相同)

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    private int pi = 0;
    public TreeNode buildTree(int[] inorder, int[] postorder) {
        pi = postorder.length - 1;
        return build(inorder, postorder, 0, pi);
    
    }
    public TreeNode build(int[] inorder, int[] postorder, int left, int right) {
        if(left > right) {
            return null;
        }
        int key = postorder[pi];
        pi--;
        TreeNode root = new TreeNode(key);
        int i = 0;
        for(i = left; i < right + 1; i++) {
            if(inorder[i] == key) {
                break;
            }
        }
        root.right = build(inorder, postorder, i + 1, right);
        root.left = build(inorder, postorder, left, i - 1);
        return root;
    }
}
  1. 根据二叉树按照前序遍历的方式创建字符串,要求:左子树要跟在根节点的后面,用括号括住左子树;右子树要跟在左子树的后面,用括号括住右子树。

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    
public String tree2str(TreeNode root) {
        return func(root).toString();
    }
    private StringBuilder func(TreeNode root) {
        if(root == null) {
            return null;
        }
        StringBuilder s = new StringBuilder();
        s.append(root.val);
        if(root.left == null && root.right == null) {
            return s;
        }else {
            if(root.left != null) {

                s.append('(');
                s.append(func(root.left));
                s.append(')');
            }else {
                s.append('(');
                //s.append(func(root.left));
                s.append(')');
            }
        }
        if(root.right == null) {
            return s;
        }else {
            s.append('(');
            s.append(func(root.right));
            s.append(')');
        }
        
        return s;

    }
}
  1. 二叉树的前序遍历(非递归)

【解析】此题还是比较难的。

  • 我们先创建一个ArrayList对象名为list,用于存放前序遍历的顺序;

  • 再创建一个Stack对象,用于存放没有走到右子树的节点,以便我们遇到cur = null时,我们能找到上父节点的右子树。(我们采用Stack的目的是,我们需要的是后进先出的情况)

  • 接下来我们让cur = cur.left,让它一直往左走,如果不为空,放入stack和list;如果遇到了空,我们弹出刚刚进入栈的元素,走这个元素的右子树。(此时cur=null,我们没有将它放入stack,所以弹出的是cur的父节点)

  • 本题最难的和最难想出来的是,总的循环条件,我们要保证cur = null和stack为空时才能退出循环;

  • 最后,返回list

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
if(root == null) {
            return new ArrayList<>();
            //return null;
        }
        List<Integer> list = new ArrayList<>();
        Stack<TreeNode> stack = new Stack<>();
        TreeNode cur = root;
        
        while(cur != null || !stack.isEmpty()) {
            while(cur != null) {
                stack.push(cur);
                list.add(cur.val);
                cur = cur.left;
            }
            TreeNode key = stack.pop();
            cur = key.right;
        }
        return list;
    }
}
  1. 二叉树的中序遍历(非递归)

  • 请先了解非递归的前序遍历的方法(上题);

  • 此题与上题不同的是进入list的时机不同:我们遇到cur = null时,我们才会讲这个节点的父节点放入list;同时又会让cur = 父节点.right;

  • 循环往下进行,直到不符合条件;

  • 返回null.

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> list = new ArrayList<>();
        if(root == null) {
            return list;
        }
        Stack<TreeNode> stack = new Stack<>();
        TreeNode cur = root;
        while(cur != null || !stack.isEmpty()) {
            if(cur != null) {
                stack.push(cur);
                cur = cur.left;
            }else {
                TreeNode key = stack.pop();
                list.add(key.val);
                cur = key.right;
            }
        }
        return list;
    }
}
  1. 二叉树的后序遍历(非递归)

  • 此题和前两题不同;

  • 这里我们新增了一个Stack名为past,用于记录哪些节点已经走过右边。(为了方便判断是否该往右边走,或进入list)

  • 我们还是先让cur != null的元素进入stack;

  • 如果cur = null,我们要判断key(stack弹出的元素,即没有走右边的元素)是否走过右边:没走过,往右边走;走右边了,key放入list,stack和past要弹出元素

  • 重复此循环,直到条件不满足

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> list = new ArrayList<>();
        if(root == null) {
            return list;
        }
        TreeNode cur = root;
        Stack<TreeNode> stack = new Stack<>();
        TreeNode prev = new TreeNode();
        //Stack<TreeNode> past = new Stack<>();
        while(cur != null || !stack.isEmpty()) {
            if(cur != null) {
                stack.push(cur);
                cur = cur.left;
            }else {
                /*TreeNode key = stack.peek();
                if(!past.isEmpty() && key == past.peek()) {
                    list.add(key.val);
                    stack.pop();
                    past.pop();
                    //cur = stack.peek().right;
                }else {
                    cur = key.right;
                    past.push(key);
                }
                 */
                TreeNode key = stack.peek();
                if(key.right == null || key.right == prev) {
                    list.add(key.val);
                    stack.pop();
                    prev = key;
                }else {
                    cur = key.right;
                }
            }

        }
        return list;
    }
}

希望大家多多支持与鼓励,如有相关问题请留言

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/174975.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

工作玩手机识别监测系统 YOLOv5

工作玩手机识别监测系统通过YOLOV5网络深度学习算法模型对画面中人员玩手机行为进行实时监测&#xff0c;当识别到有人在玩手机行为时&#xff0c;无需人为干预立即抓拍存档触发告警。YOLO算法- YOLO算法是一种基于回归的算法&#xff0c;它不是选择图像中有趣的部分&#xff0…

WT588D语音芯片介绍

WT588D语音芯片简介WT588D 语音芯片是一款功能强大的可重复擦除烧写的语音单片机芯片。WT588D 让语音芯片不再为控制方式而寻找合适的外围单片机电路&#xff0c;高度集成的单片机技术足于取代复杂的外围控制电路。配套WT588DVoiceChip 上位机操作软件可随意更换WT588D 语音单片…

基于 docker 搭建 mysql5.7 主从复制

安装 docker 的教程可以看我的另一篇文章&#xff0c;拉取 mysql 镜像的步骤也在里面&#xff0c;在这不再重复&#xff1a;https://blog.csdn.net/wanzijy/article/details/128695674 1. 主机搭建 因为本人虚拟机中已经存在了 mysql &#xff0c;所以在使用镜像创建容器的时…

【论文翻译】End-to-End Human Pose and Mesh Reconstruction with Transformers

【cvpr论文】End-to-End Human Pose and Mesh Reconstruction with Transformers (thecvf.com) 【github】microsoft/MeshTransformer: Research code for CVPR 2021 paper "End-to-End Human Pose and Mesh Reconstruction with Transformers" (github.com) 摘要 我…

学习笔记:Java 并发编程③

若文章内容或图片失效&#xff0c;请留言反馈。 部分素材来自网络&#xff0c;若不小心影响到您的利益&#xff0c;请联系博主删除。 视频链接&#xff1a;https://www.bilibili.com/video/av81461839配套资料&#xff1a;https://pan.baidu.com/s/1lSDty6-hzCWTXFYuqThRPw&am…

在甲骨文云容器实例(Container Instances)上部署Ubuntu Desktop

甲骨文云推出了容器实例&#xff0c;这是一项无服务器计算服务&#xff0c;可以即时运行容器&#xff0c;而无需管理任何服务器。 今天我们尝试一下通过容器实例部署Ubuntu Bionic Desktop。 创建容器实例 在甲骨文容器实例页面&#xff0c;单击"创建容器实例"&…

Java 笔试题

Java 笔试题目录概述需求&#xff1a;设计思路实现思路分析1.java 面试题参考资料和推荐阅读Survive by day and develop by night. talk for import biz , show your perfect code,full busy&#xff0c;skip hardness,make a better result,wait for change,challenge Surviv…

分享151个PHP源码,总有一款适合您

PHP源码 分享151个PHP源码&#xff0c;总有一款适合您 下面是文件的名字&#xff0c;我放了一些图片&#xff0c;文章里不是所有的图主要是放不下...&#xff0c; 151个PHP源码下载链接&#xff1a;https://pan.baidu.com/s/1T_Hs4j0t39b-Y8UWHmAKyw?pwd7ao0 提取码&#…

论文浅尝 | DB4Trans:数据库内置知识图谱嵌入模型训练引擎

笔记整理&#xff1a;柳鹏凯&#xff0c;天津大学硕士发表期刊&#xff1a;计算机学报 第45卷Vol.45, 第9期 No.9链接&#xff1a;http://cjc.ict.ac.cn/online/onlinepaper/lpk-202297212908.pdf动机知识图谱嵌入技术主要将知识图谱中的实体和关系嵌入到连续的向量空间中&…

Centos Java1.8+Nginx+redis+pgsql 手工配置

一、系统升级&#xff0c;安装系统常用工具及配置 1.1 升级软件及Centos 内核 yum update -y yum clean all cat /etc/redhat-release 1.2 安装虚拟机守护进程 yum install qemu-guest-agent -y 1.3 安装系统常用工具包 yum install lrzsz vim wget dnf -y 1.4关…

2023牛客寒假算法基础集训营3 -- E-勉强拼凑的记忆(贪心 + 二分)

题目如下&#xff1a; 题解 or 思路&#xff1a; 我们可以发现&#xff1a;除了 n2n 2n2 无解&#xff0c; 其他情况答案至少为 n12\frac{n 1}{2}2n1​ 答案在 n12\frac{n 1}{2}2n1​ 到 nnn 之间 我们可以假设 答案为 ansansans 最优摆放为&#xff1a; 所以可以二分去求…

软件工程 黄金点游戏

这个故事最初出现在 《移山之道》中&#xff0c;我经常拿来做和创新的时机相关课堂练习和讨论&#xff0c;效果很好。我把这个练习和它的一些延伸话题都搬到这个新博客里。 黄金点游戏 N个同学&#xff08;N通常大于10&#xff09;&#xff0c;每人写一个 0~100之间的有理数 …

1、认识IntelliJ IDEA

文章目录1、认识IntelliJ IDEA1.1 JetBrains公司介绍1.2 IntelliJ IDEA介绍1.3 IDEA的主要优势&#xff08;对比Eclipse&#xff09;1.3.1 功能强大1.3.2 符合人体工程学1.4 IDEA的下载【尚硅谷】idea实战教程-讲师&#xff1a;宋红康 生活是属于每个人自己的感受&#xff0c;不…

Python:Docx文档模板创建使用

✨博文作者 wangzirui32 &#x1f496; 喜欢的可以 点赞 收藏 关注哦~~ &#x1f449;本文首发于CSDN&#xff0c;未经许可禁止转载 &#x1f60e;Hello&#xff0c;大家好&#xff0c;我是wangzirui32&#xff0c;今天我们来学习Docx文档模板创建与使用&#xff0c;开始学习吧…

2023新春祝福html代码,包你学会

前言大家新年好&#xff01;今天是年三十&#xff0c;在这个充满喜悦和欢乐的节日里&#xff0c;祝大家新年快乐。不论你在外面过的风生水起还是不尽人意&#xff0c;回到家一家人团团聚聚才是最好的。进入正题&#xff0c;我们作为IT民工&#xff0c;我们要用自己的方式表达对…

第三天总结 之 商品管理界面的实现 之 页面中 下拉框问题的解决

页面中下拉框问题的解决 在页面中 点击商品类型这个图标 会出现下拉框 展示所有的商品类型 然后通过选择的 类型 来作为 查询时的一个条件 即 当不选或选择展示所有商品时 按照 不对这个条件进行操作 选择其他的商品类型时 会查询出含有该类型的商品 下拉框中 数据的展示与 如…

java设计模式中责任链模式是什么/怎么用责任链模式避免if-else语句

继续整理记录这段时间来的收获&#xff0c;详细代码可在我的Gitee仓库SpringBoot克隆下载学习使用&#xff01; 6.5 责任链模式 6.5.1 定义 职责链模式&#xff0c;为避免请求发生者与多个处理者耦合在一起&#xff0c;将所有请求处理者通过前一对象记住其下一对象的引用而连…

重学Attention

注意力机制对比RNN 主要解决了RNN无法并行&#xff0c;并且不能解决长序列依赖问题 所以为什么设计 Q K V这三个矩阵 一边来是让 K V的 首先通过Q 和 K点击计算Attention矩阵&#xff0c;这个矩阵表明的是V上每一个特征与Q的相关程度&#xff0c;相关程度高的&#xff0c;权重…

解剖一道有意思的指针题

这道指针题挺有意思的&#xff0c;将各级指针之间的联系联系起来&#xff0c;仔细分析会发现也不难&#xff0c;重在逻辑思维&#xff0c;做完将会加深你对指针的理解的&#xff0c;好好享受指针带来的乐趣吧&#xff01;&#xff01;&#xff01;结果是什么呢&#xff1f;//题…

FPGA 20个例程篇:19.OV7725摄像头实时采集送HDMI显示(三)

第七章 实战项目提升&#xff0c;完善简历 19.OV7725摄像头实时采集送HDMI显示&#xff08;三&#xff09; 在详细介绍过OV7725 CMOS Sensor的相关背景知识和如何初始化其内部寄存器达到输出预期视频流的目的后&#xff0c;就到了该例程的核心内容即把OV7725输出的视频流预先缓…