【数据结构Java版】树与二叉树的相关知识全解

news2025/1/18 11:46:46

目录

一、树型结构

(1)树的定义

(2)树的基本术语

(3)树的存储结构

二、二叉树

(1)二叉树的定义

(2)两种特殊二叉树

1.满二叉树

2.完全二叉树

(3)二叉树的性质

1.公式

2.习题

(4)二叉树的存储

(5)二叉树的基本操作

1.二叉树的遍历之前序遍历

2.二叉树的遍历之中序遍历

3.二叉树的遍历之后序遍历

4.二叉树的遍历之层序遍历 

5. 统计二叉树中结点个数

6.统计二叉树中叶子节点的个数

7.统计二叉树的第K层上的结点个数

8.求一棵二叉树的高度

9.给定一个值,在二叉树中进行查找,返回包含这个值的结点

10.判断是否是完全二叉树

三、二叉树oj题 

(1)检查两颗树是否相同

(2)另一颗树的子树

(3)翻转二叉树

(4)判断一颗二叉树是否是平衡二叉树

(5)对称二叉树

(6)二叉树的构建及遍历

(7)二叉树的分层遍历 

(8)给定一个二叉树, 找到该树中两个指定节点的最近公共祖先 

(9)根据一棵树的前序遍历与中序遍历构造二叉树

(10)根据一棵树的中序遍历与后序遍历构造二叉树

(11)二叉树创建字符串

(12)二叉树前序非递归遍历实现 

(13)二叉树中序非递归遍历实现

(14)二叉树后序非递归遍历实现


一、树型结构

(1)树的定义

        a.树是一种非线性的数据结构,它是由n(n>=0)个有限结点组成一个具有层次关系的集合。把它叫做树是因为它看起来像一棵倒挂的树,(根朝上,而叶朝下)它具有以下的特点:
        b.有一个特殊的结点,称为根结点,根结点没有前驱结点。除根结点外,其余结点被分成M(M > 0)个互不相交的集合T1、T2、......、Tm,其中每一个集合Ti (1 <= i <=m) 又是一棵与树类似的子树。每棵子树的根结点有且只有一个前驱,可以有0个或多个后继。
        c.树是递归定义的。树形结构中,子树之间不能有交集,否则就不是树形结构。

(2)树的基本术语

结点的度:一个结点含有子树的个数称为该结点的度; 如上图:A的度为6
树的度:一棵树中,所有结点度的最大值称为树的度; 如上图:树的度为6
叶子结点或终端结点:度为0的结点称为叶结点; 如上图:B、C、H、I...等节点为叶结点
双亲结点或父结点:若一个结点含有子结点,则这个结点称为其子结点的父结点; 如上图:A是B的父结点
孩子结点或子结点:一个结点含有的子树的根结点称为该结点的子结点; 如上图:B是A的孩子结点
根结点:一棵树中,没有双亲结点的结点;如上图:A
结点的层次:从根开始定义起,根为第1层,根的子结点为第2层,以此类推
树的高度或深度:树中结点的最大层次; 如上图:树的高度为4
非终端结点或分支结点:度不为0的结点; 如上图:D、E、F、G...等节点为分支结点
兄弟结点:具有相同父结点的结点互称为兄弟结点; 如上图:B、C是兄弟结点
堂兄弟结点:双亲在同一层的结点互为堂兄弟;如上图:H、I互为兄弟结点
结点的祖先:从根到该结点所经分支上的所有结点;如上图:A是所有结点的祖先
子孙:以某结点为根的子树中任一结点都称为该结点的子孙。如上图:所有结点都是A的子孙
森林:由m(m>=0)棵互不相交的树组成的集合称为森林

(3)树的存储结构

        树结构相对线性表就比较复杂了,要存储表示起来就比较麻烦了,实际中树有很多种表示方式,如:双亲表示法孩子表示法孩子双亲表示法孩子兄弟表示法等等。

相关博客详解:树的三种表示法:双亲表示法、孩子表示法、孩子兄弟表示法_Huberyxiao的博客-CSDN博客_树的双亲表示法

二、二叉树

(1)二叉树的定义

        一棵二叉树是结点的一个有限结合,该集合需满足两个条件:

a.或者为空

b.或者是由一个根节点加上两棵别称为左子树右子树的二叉树组成

a. 二叉树不存在度大于2的结点
b.
二叉树的子树有左右之分,次序不能颠倒,因此二叉树是有序树

任意的二叉树都是由以下几种情况复合而成的:

(2)两种特殊二叉树

1.满二叉树

        一棵二叉树,如果每层的结点数都达到最大值,则这棵二叉树就是满二叉树。也就是说,如果一棵二叉树的层数为K,且结点总数是2^{k}-1 ,则它就是满二叉树。
        

2.完全二叉树

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

(3)二叉树的性质

1.公式

a. 若规定根结点的层数为1,则一棵非空二叉树的第i层上最多有2^{i-1} (i>0)个结点
b. 若规定只有根结点的二叉树的深度为1,则深度为K的二叉树的最大结点数是 2^{k}-1(k>=0)
c. 对任何一棵二叉树, 如果其叶结点个数为 n_{0}, 度为2的非叶结点个数为 n_{2},则有n_{0}=n_{2}+1
d. 具有n个结点的完全二叉树的深度k= log_{_2}(n+1)上取整
e. 对于具有n个结点的完全二叉树,如果按照从上至下从左至右的顺序对所有节点从0开始编号,则对于序号为i的结点有:
若i>0,双亲序号:\frac{i-1}{2};i=0,i为根结点编号,无双亲结点
2i+1<n,左孩子序号:2i+1,否则无左孩子
2i+2<n,右孩子序号:2i+2,否则无右孩子

2.习题

1. 某满二叉树中共有 399 个结点,其中有 199 个度为 2 的结点,则该二叉树中的叶子结点数为( B)
A 不存在这样的二叉树
B 200
C 198
D 199


2.在具有 2n 个结点的完全二叉树中,叶子结点个数为(A)
A n
B n+1
C n-1
D n/2


3.一个具有767个节点的完全二叉树,其叶子节点个数为(B)
A 383
B 384
C 385
D 386

与第三题同理
4.一棵完全二叉树的节点数为531个,那么这棵树的高度为(B )
A 11
B 10
C 8
D 12

(4)二叉树的存储

        二叉树的存储结构分为:顺序存储和类似于链表的链式存储。
        二叉树的链式存储是通过一个一个的节点引用起来的,常见的表示方式有二叉和三叉表示方式,具体如下:

// 孩子表示法
class Node {
int val; // 数据域
Node left; // 左孩子的引用,常常代表左孩子为根的整棵左子树
Node right; // 右孩子的引用,常常代表右孩子为根的整棵右子树
} 
//孩子双亲表示法
class Node {
int val; // 数据域
Node left; // 左孩子的引用,常常代表左孩子为根的整棵左子树
Node right; // 右孩子的引用,常常代表右孩子为根的整棵右子树
Node parent; // 当前节点的根节点
}

以下代码都是使用如下结点定义:

public class TreeNode {
    public int val;
    public TreeNode left;
    public TreeNode right;
    public TreeNode(int val) {
        this.val = val;     // 这个结点中的值是 val
        this.left = null;   // 这个结点的左孩子是 null:不存在 —— 左子树是空树
        this.right = null;  // 这个结点的有孩子是 null;不存在 —— 右子树是空树
    }
}

 在下面代码中除了利用递归思想,还有另一种方法用到队列。代码实现层面,需要把结点和层数打包在一起,作为队列的元素 。代码中如何体现打包呢?使用一个对象。

static class TreeNodeWithLevel {
        public TreeNode node;
        public int level;

        public TreeNodeWithLevel(TreeNode node, int level) {
            this.node = node;
            this.level = level;
        }

        @Override
        public String toString() {
            return String.format("%s-%d", node.toString(), level);//重写toString方法,使打印明确。
        }
    }

(5)二叉树的基本操作

        遍历(Traversal)是指沿着某条搜索路线,依次对树中每个结点均做一次且仅做一次访问。访问结点所做的操作依赖于具体的应用问题(比如:打印节点内容、节点内容加1)。 遍历是二叉树上最重要的操作之一,是二叉树上进行其它运算之基础。

        在遍历二叉树时,如果没有进行某种约定,每个人都按照自己的方式遍历,得出的结果就比较混乱,如果按照某种规则进行约定,则每个人对于同一棵树的遍历结果肯定是相同的。如果N代表根节点,L代表根节点的左子树,R代表根节点的右子树,则根据遍历根节点的先后次序有以下遍历方式:
NLR:前序遍历(Preorder Traversal 又先序遍历)——访问根结点--->根的左子树--->根的右子树。
LNR:中序遍历(Inorder Traversal)——根的左子树--->根节点--->根的右子树。
LRN:后序遍历(Postorder Traversal)——根的左子树--->根的右子树--->根节点

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

二叉树的遍历分为深度遍历、广度遍历。

深度遍历:前中后序遍历,需要用到栈。隐式用到栈:递归方法。显示用到栈:非递归方法。

广度遍历:层序遍历,需要用到队列。

1.二叉树的遍历之前序遍历

递归:

    public static void preorder(TreeNode root) {
        if (root == null) {
            return;
        }
        System.out.println(root.val);
        preorder(root.left);
        preorder(root.right);
    }

非递归:

public static void preorder非递归(TreeNode root) {
        Deque<TreeNode> stack = new LinkedList<>(); // 使用 Deque 作为栈来使用
        TreeNode cur = root;

        // 循环停止的条件 cur == null && stack.isEmpty()
        // cur == null 的时候,应该处理 栈 中还有结点的右子树了
        // 但 栈 也是 empty,说明所有的右子树都处理
        // 所以可以停止了
        // 反过来,循环继续的条件
        // !(cur == null && stack.isEmpty())
        // 化简 cur != null || stack.isEmpty() == false
        while (cur != null || stack.isEmpty() == false) {
            // 一路朝左走,不管右子树,但需要回溯,所以记录在栈中
            while (cur != null) {
                System.out.println(cur.toString());
                stack.push(cur);
                cur = cur.left;
            }

            // 依次从栈中弹出元素,处理剩下没走的右子树
            TreeNode top = stack.pop();

            // 处理右子树
            cur = top.right;
        }
    }

2.二叉树的遍历之中序遍历

递归:

   public static void inorder(TreeNode root) {
        if (root == null) {
            return;
        }
        inorder(root.left);
        System.out.println(root.val);
        inorder(root.right);
    }

非递归:

// 中序遍历是第二次经过时打印(从左子树回来时打印) -> 从栈里弹出来的时候打印
    public static void inorder非递归(TreeNode root) {
        Deque<TreeNode> stack = new LinkedList<>(); // 使用 Deque 作为栈来使用
        TreeNode cur = root;

        while (cur != null || stack.isEmpty() == false) {
            while (cur != null) {
                // 第一次经过某结点
                stack.push(cur);
                cur = cur.left;
            }

            // 依次从栈中弹出元素,处理剩下没走的右子树
            TreeNode top = stack.pop();
            System.out.println(top);    // 第二次经过某结点

            // 处理右子树
            cur = top.right;
        }
    }

3.二叉树的遍历之后序遍历

递归:

    public static void postorder(TreeNode root) {
        if (root == null) {
            return;
        }
        postorder(root.left);
        postorder(root.right);
        System.out.println(root.val);
    }

非递归:

public static void postorder非递归(TreeNode root) {
        Deque<TreeNode> stack = new LinkedList<>(); // 使用 Deque 作为栈来使用
        TreeNode cur = root;
        TreeNode last = null;   // 记录上次后序遍历经过的结点

        while (cur != null || stack.isEmpty() == false) {
            while (cur != null) {
                // 第一次经过某结点
                stack.push(cur);
                cur = cur.left;
            }

            // 查看栈顶元素
            TreeNode top = stack.peek();
            if (top.right == null) {
                // 右子树为空的情况下,没必要走第三次
                // 换言之:第二次可以看作第三次
                System.out.println(top);    // 后序
                last = top; // last 记录上一次被后序遍历的结点

                stack.pop();
            } else if (top.right == last) {
                // 上一次后序遍历的结点是 top 的右孩子
                // 说明是从 top 的右边来到 top 的
                // 也就是第三次经过 top 结点
                System.out.println(top);    // 后序
                last = top; // last 记录上一次被后序遍历的结点

                stack.pop();
            } else {
                // 说明是从 top 的左边到的 top
                // 第二次经过 top 结点
                // 不后序遍历,而是继续走 top 的右子树
                // 处理右子树
                cur = top.right;
            }
        }
    }

4.二叉树的遍历之层序遍历 

   public static void levelorder(TreeNode root) {
        if (root == null) {
            return;
        }
        // 使用队列而不是栈,没有递归
        // 因为队列中取出元素后需要把结点的左右孩子继续放进去,所以这里队列的元素类型是 TreeNode
        Queue<TreeNode> queue = new LinkedList<>();
        // 首先把根节点放入队列中,此时此刻的层序遍历不会把 null 放到队列中
        // 所以,所以需要考虑 root == null 的情况
        queue.offer(root);

        // 开启循环,直到 queue.isEmpty() == true
        while (queue.isEmpty() == false) {
            // 1. 取出队首元素
            TreeNode node = queue.poll();
            // 断言 node != null
            // 2. 打印结点的值
            System.out.println(node.toString());
            // 3. 依次从左到右把孩子放入队列中,不放 null 进去
            if (node.left != null) {
                queue.offer(node.left);
            }
            if (node.right != null) {
                queue.offer(node.right);
            }
        }
    }

层序遍历打印结点和层数:

  public static void levelOrderButLevel(TreeNode root) {
        if (root == null) {
            return;
        }

        Queue<TreeNodeWithLevel> queue = new LinkedList<>();//见二叉树的存储
        queue.offer(new TreeNodeWithLevel(root, 1));

        while (!queue.isEmpty()) {
            TreeNodeWithLevel tnwl = queue.poll();
            TreeNode node = tnwl.node;
            int level = tnwl.level;
            System.out.println(tnwl);

            if (node.left != null) {
                queue.offer(new TreeNodeWithLevel(node.left, level + 1));
            }

            if (node.right != null) {
                queue.offer(new TreeNodeWithLevel(node.right, level + 1));
            }
        }
    }
public static void levelOrderButLevel2(TreeNode root) {
        if (root == null) {
            return;
        }

        Queue<TreeNode> nodeQueue = new LinkedList<>();
        Queue<Integer> levelQueue = new LinkedList<>();

        nodeQueue.offer(root);
        levelQueue.offer(1);

        // 代码一定维护着 nodeQueue 和 levelQueue 的元素个数一定是一样的
        // 所以判断,只需要判断其中一个队列是 empty 即可
        while (!nodeQueue.isEmpty()) {
            TreeNode node = nodeQueue.poll();
            int level = levelQueue.poll();

            System.out.println(node + "-" + level);

            if (node.left != null) {
                nodeQueue.offer(node.left);
                levelQueue.offer(level + 1);
            }

            if (node.right != null) {
                nodeQueue.offer(node.right);
                levelQueue.offer(level + 1);
            }
        }
    }

下面是层序遍历的升级版:二叉树的层序遍历

 public static List<List<Integer>> levelOrder(TreeNode root) {
        List<List<Integer>> ans = new ArrayList<>();
        if (root == null) {
            return ans;
        }

        // 定义两个队列
        Queue<TreeNode> nodeQueue = new LinkedList<>();
        Queue<Integer> levelQueue = new LinkedList<>();

        // 根结点记为第 0 层
        // 把根结点和它对应的层数放入队列中
        nodeQueue.offer(root);
        levelQueue.offer(0);

        while (!nodeQueue.isEmpty()) {
            TreeNode node = nodeQueue.poll();
            int level = levelQueue.poll();

            // 每个结点的 level 就刚好对应 ans 中的下标,根据这个下标,就可以获取到对应的 List<Integer>,这层的 List
            List<Integer> levelList = ans.get(level);
            // 将元素放入对应的 levelList 中(尾插,保证顺序不变)
            levelList.add(node.val);

            // 层序遍历的模板代码
            if (node.left != null) {
                nodeQueue.offer(node.left);
                levelQueue.offer(level + 1);
            }

            if (node.right != null) {
                nodeQueue.offer(node.right);
                levelQueue.offer(level + 1);
            }
        }

        return ans;
    }

5. 统计二叉树中结点个数

递归:

public static int nodeSize;
    /**
     * 获取树中节点的个数:遍历思路
     */
    void size(TreeNode root) {
        if(root==null){
            return ;
        }
        nodeSize++;
        size(root.left);
        size(root.right);
    }
    /**
     * 获取节点的个数:子问题的思路
     *
     * @param root
     * @return
     */
    int size2(TreeNode root) {
        if(root==null) {
            return 0;
        }
        return size2(root.left)+size2(root.right)+1;
    }

队列:

public static int sizeOf(TreeNode root) {
        if (root == null) {
            // 空树的结点个数
            return 0;
        }

        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);

        int size = 0;
        // 每个结点都会被放入队列中
        // 都会被取出来一次
        // 有多少个结点,这个循环就会持续多少次
        while (!queue.isEmpty()) {
            TreeNode node = queue.poll();
            size++; // 打印替换成 size++
            if (node.left != null) {
                queue.offer(node.left);
            }
            if (node.right != null) {
                queue.offer(node.right);
            }
        }

        return size;
    }

6.统计二叉树中叶子节点的个数

递归:

/*
     获取叶子节点的个数:遍历思路
     */
    public static int leafSize = 0;
 
    void getLeafNodeCount1(TreeNode root) {
        if(root==null){
            return;
        }
        if(root.left==null&&root.right==null){
            leafSize++;
        }
        getLeafNodeCount1(root.left);
        getLeafNodeCount1(root.right);
    }
 /*
     获取叶子节点的个数:子问题
     */
    int getLeafNodeCount2(TreeNode root) {
        if (root==null){
            return 0;
        }
        if(root.left==null&&root.right==null){
            return 1;
        }
        return getLeafNodeCount2(root.left)+getLeafNodeCount2(root.right);
    }

队列:

public static int leafSizeOf(TreeNode root) {
        if (root == null) {
            // 空树的叶子结点个数
            return 0;
        }

        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);

        int size = 0;
        // 每个结点都会被放入队列中
        // 都会被取出来一次
        // 有多少个结点,这个循环就会持续多少次
        while (!queue.isEmpty()) {
            TreeNode node = queue.poll();
            // 只有叶子结点时,才 size++
            if (node.left == null && node.right == null) {
                size++; // 打印替换成 size++
            }
            if (node.left != null) {
                queue.offer(node.left);
            }
            if (node.right != null) {
                queue.offer(node.right);
            }
        }

        return size;
    }

7.统计二叉树的第K层上的结点个数

递归:

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

队列:

public static int getKLevelSize(TreeNode root, int k) {
        if (root == null) {
            return 0;
        }

        Queue<TreeNodeWithLevel> queue = new LinkedList<>();
        queue.offer(new TreeNodeWithLevel(root, 1));

        int size = 0;
        while (!queue.isEmpty()) {
            TreeNodeWithLevel tnwl = queue.poll();
            TreeNode node = tnwl.node;
            int level = tnwl.level;
            if (level == k) {
                // 说明是第 k 层了
                size++;
            }

            if (node.left != null) {
                queue.offer(new TreeNodeWithLevel(node.left, level + 1));
            }

            if (node.right != null) {
                queue.offer(new TreeNodeWithLevel(node.right, level + 1));
            }
        }

        return size;
    }

8.求一棵二叉树的高度

递归:

/*
     获取二叉树的高度
     时间复杂度:O(N)
     */
    int getHeight(TreeNode root) {
        if(root==null){
            return 0;
        }
        int leftHeight=getHeight(root.left);
        int rightHeight=getHeight(root.right);
        return leftHeight>rightHeight?leftHeight+1:rightHeight+1; 
    }

队列:

public static int heightOf(TreeNode root) {
        if (root == null) {
            return 0;
        }

        Queue<TreeNodeWithLevel> queue = new LinkedList<>();
        queue.offer(new TreeNodeWithLevel(root, 1));

        int height = -1;
        while (!queue.isEmpty()) {
            TreeNodeWithLevel tnwl = queue.poll();
            TreeNode node = tnwl.node;
            int level = tnwl.level;

            height = level; //后边的值覆盖前面的值

            if (node.left != null) {
                queue.offer(new TreeNodeWithLevel(node.left, level + 1));
            }

            if (node.right != null) {
                queue.offer(new TreeNodeWithLevel(node.right, level + 1));
            }
        }

        // height 就是最后一个 level,也就是最大的 level
        return height;
    }

9.给定一个值,在二叉树中进行查找,返回包含这个值的结点

   // 检测值为value的元素是否存在
    TreeNode find(TreeNode root, char val) {
        if(root==null){
            return null;
        }
       if(root.val==val){
           return root;
       }
       TreeNode left=find(root.left,val);
       if(left!=null){
           return left;
       }
       TreeNode right=find(root.right, val);
       if(right!=null){
           return right;
       }
        return null;
    }

10.判断是否是完全二叉树

public static boolean isCompleteTree(TreeNode root) {
        if (root == null) {
            // 空树是一种完全二叉树
            return true;
        }

        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);

        // 循环停止的条件,从队列中取出的元素是 null
        while (true) {
            TreeNode node = queue.poll();
            if (node == null) {
                break;
            }

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

        // 检查队列中剩余的元素是否存在 != null 的情况
        // 存在 != null:非完全二叉树
        // 任取 == null:完全二叉树
        // 所以,遍历队列中剩余的元素
        while (!queue.isEmpty()) {
            TreeNode node = queue.poll();
            if (node != null) {
                // 存在 != null:
                return false;
            }
        }

        // 任取 == null:是完全二叉树
        return true;
    }

三、二叉树oj题 

(1)检查两颗树是否相同

100. 相同的树

class Solution {
    public boolean isSameTree(TreeNode p, TreeNode q) {
        if(p==null&&q==null){
            return true;
        }
        if(p==null||q==null){
            return false;
        }
        if(p.val!=q.val){
            return false;
        }else{
            boolean r=isSameTree(p.left,q.left);
            if(r==false){
                return false;
            }else{
                r=isSameTree(p.right,q.right);
                if(r==false){
                    return false;
                }else{
                    return true;
                }
            }
        }
    }
}

(2)另一颗树的子树

572. 另一棵树的子树

class Solution {
    public boolean isSubtree(TreeNode root, TreeNode subRoot) {
        if(subRoot==null){
            return true;
        }
        if(root==null){
            return false;
        }
        if(isSame(root,subRoot)){
            return true;
        }
       return isSubtree(root.left,subRoot)||isSubtree(root.right,subRoot);
    }
     public boolean isSame(TreeNode root, TreeNode subRoot) {
        if(root==null&&subRoot==null){
            return true;
        }
        if(root==null||subRoot==null){
            return false;
        }
        return root.val==subRoot.val
        &&isSame(root.left,subRoot.left)
        &&isSame(root.right,subRoot.right);
    }
}

(3)翻转二叉树

226. 翻转二叉树

class Solution {
    public TreeNode invertTree(TreeNode root) {
        if(root==null){
            return null;
        }
        invertTree(root.left);
        TreeNode tmp=root.left;
        root.left=root.right;
        root.right=tmp;
        
        invertTree(root.right);
        return root;
    }
}

(4)判断一颗二叉树是否是平衡二叉树

110. 平衡二叉树

class Solution {
    public boolean isBalanced(TreeNode root) {
        if(root==null){
            return true;
        }
        if(!isBalanced(root.left)){
            return false;
        }
        if(!isBalanced(root.right)){
            return false;
        }
        if(Math.abs(height(root.left)-height(root.right))>1){
            return false;
        }
        return true;
    }
    public int height(TreeNode root) {
        if(root==null){
            return 0;
        }
        return height(root.left)>height(root.right)?height(root.left)+1:height(root.right)+1;
        
    }
}

(5)对称二叉树

101. 对称二叉树

class Solution {
    public boolean isSymmetric(TreeNode root) {
        if(root==null){
            return true;
        }
        return isSame(root.left,root.right);
    }
    public boolean isSame(TreeNode p,TreeNode q) {
        if(p==null&&q==null){
            return true;
        }
        if(p==null||q==null){
            return false;
        }
        return p.val==q.val&&isSame(p.left,q.right)&&isSame(p.right,q.left);
    }
}

(6)二叉树的构建及遍历

二叉树遍历_牛客题霸_牛客网

import java.util.*;
import java.lang.*;

class TreeNode{
    char val;
    TreeNode left;
    TreeNode right;
    TreeNode(char val){
        this.val=val;
    }
}

// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        while(in.hasNext()){
            String str=in.nextLine();
            List <Character>preorderList=toList(str);
            TreeNode node= createTree(preorderList);
            inorder(node);
        } 
    }
    public static List <Character> toList(String str) {
        List <Character> list=new LinkedList<>();
        for(int i=0;i<str.length();i++){
            list.add(str.charAt(i));
        }
        return list;
    }
    public static TreeNode  createTree(List <Character> proderList) {
        if(proderList.size()==0){
            return null;
        }
        char rootval =proderList.remove(0);
        if(rootval=='#'){
            return null;
        }

        TreeNode root =new TreeNode(rootval);

        TreeNode left = createTree(proderList);
        TreeNode right = createTree(proderList);
        root.left=left;
        root.right=right;
        return root;
    
    }
    public static void inorder(TreeNode root) {
        if(root==null){
            return;
        }
        inorder(root.left);
        System.out.print(root.val+" ");
        inorder(root.right);
    }
    
}

(7)二叉树的分层遍历 

102. 二叉树的层序遍历

class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
        List<List<Integer>> ans=new LinkedList<>();
        Queue<TreeNode> queue=new LinkedList<>();

        if(root==null){
            return ans;
        }
        queue.offer(root);
        while(!queue.isEmpty()){
            List<Integer> level=new LinkedList<>();
            int levelSize=queue.size();
            for(int i=0;i<levelSize;i++){
                TreeNode node=queue.poll();
                level.add(node.val);
                if(node.left!=null){
                    queue.offer(node.left);
                }
                if(node.right!=null){
                    queue.offer(node.right);
                }
            }
            ans.add(level);
        }
        return ans;
    }
}

(8)给定一个二叉树, 找到该树中两个指定节点的最近公共祖先 

236. 二叉树的最近公共祖先

class Solution {
       public boolean contain(TreeNode root, TreeNode target) {
           if(root==null){
               return false;
           }
           if(root==target){
               return true;
           }
           return contain(root.left,target)||contain(root.right,target);
    }
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if(p==root||q==root){
            return root;
        }
    
        boolean r1=contain(root.left,p);
        boolean r2=contain(root.left,q);

        if(r1==true&&r2==true){
            return lowestCommonAncestor(root.left,p,q);
        }
        if(r1==false&&r2==false){
            return lowestCommonAncestor(root.right,p,q);
        }
        return root;
    }
}

(9)根据一棵树的前序遍历与中序遍历构造二叉树

105. 从前序与中序遍历序列构造二叉树

class Solution {
    public List<Integer> arrayToList(List<Integer> list,int[] array) {
        for(int i=0;i<array.length;i++){
            list.add(array[i]);
        }
        return list;
    }
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        List<Integer> inorderList=new LinkedList<>();
        List<Integer> preorderList=new LinkedList<>();
        inorderList=arrayToList(inorderList,inorder);
        preorderList=arrayToList(preorderList,preorder);
        return build(preorderList,inorderList);
    }
public TreeNode build(List <Integer> preorderList, List<Integer> inorderList) {
        if(inorderList.size()==0){
            return null;
        }
        int rootVal=preorderList.get(0);
        TreeNode root=new TreeNode(rootVal);
        
        int leftLength=inorderList.indexOf(rootVal);
        List <Integer> leftpreorderList=preorderList.subList(1,leftLength+1);
        List<Integer> leftinorderList=inorderList.subList(0,leftLength);
        TreeNode letfNode=build(leftpreorderList,leftinorderList);

        List <Integer> rightpreorderList=preorderList.subList(leftLength+1,preorderList.size());
        List <Integer> rightinorderList=inorderList.subList(leftLength+1,inorderList.size());
        TreeNode rightNode=build(rightpreorderList,rightinorderList);

        root.left=letfNode;
        root.right=rightNode;
        return root;
    }
}

(10)根据一棵树的中序遍历与后序遍历构造二叉树

106. 从中序与后序遍历序列构造二叉树

class Solution {
    public List<Integer> arrayToList(List<Integer> list,int[] array) {
        for(int i=0;i<array.length;i++){
            list.add(array[i]);
        }
        return list;
    }
    public TreeNode buildTree(int[] inorder, int[] postorder) {
        List<Integer> inorderList=new LinkedList<>();
        List<Integer> postorderList=new LinkedList<>();

        inorderList=arrayToList(inorderList,inorder);
        postorderList=arrayToList(postorderList,postorder);
        return build(inorderList,postorderList);

    }
       public TreeNode build(List <Integer>inorderList, List<Integer> postorderList) {
        if(inorderList.size()==0){
            return null;
        }
        int rootVal=postorderList.get(postorderList.size()-1);
        TreeNode root=new TreeNode(rootVal);
        
        int leftLength=inorderList.lastIndexOf(rootVal);
        List <Integer> leftinorderList=inorderList.subList(0,leftLength);
        List<Integer> leftpostinorderList=postorderList.subList(0,leftLength);
        TreeNode letfNode=build(leftinorderList,leftpostinorderList);

        List <Integer> rightinorderList=inorderList.subList(leftLength+1,inorderList.size());
        List <Integer> rightpostorderList=postorderList.subList(leftLength,postorderList.size()-1);
        TreeNode rightNode=build(rightinorderList,rightpostorderList);

        root.left=letfNode;
        root.right=rightNode;
        return root;
    }
}

(11)二叉树创建字符串

606. 根据二叉树创建字符串

class Solution {
    public String tree2str(TreeNode root) {
        StringBuilder sb=new StringBuilder();
        preOrder(root,sb);
        String str=sb.toString();
        str=str.substring(1,str.length()-1);
        return str;
    }
    public void preOrder(TreeNode root,StringBuilder sb) {
        if(root==null){
            sb.append("()");
            return;
        }
         sb.append("(");
        if(root.left==null&&root.right!=null){

            sb.append(root.val);
            preOrder(root.left,sb);
            preOrder(root.right,sb);         

        }else if(root.left!=null&&root.right==null){
       
            sb.append(root.val);
            preOrder(root.left,sb);

        }else if(root.left==null&&root.right==null){
            sb.append(root.val);
        }else{

            sb.append(root.val);
            preOrder(root.left,sb);
            preOrder(root.right,sb);   
        }
            sb.append(")");

    }
}

(12)二叉树前序非递归遍历实现 

144. 二叉树的前序遍历

class Solution {
 public List<Integer> preorderTraversal(TreeNode root) {

        List<Integer> ans = new ArrayList<>();

        if (root == null) {
            return ans;
        }

        int rootValue = root.val;
        ans.add(rootValue);
        ans.addAll(preorderTraversal(root.left));
        ans.addAll(preorderTraversal(root.right));
        return ans;
    }
}

(13)二叉树中序非递归遍历实现

94. 二叉树的中序遍历

class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List <Integer> list=new LinkedList<>();
        if(root==null){
            return list;
        }
        List <Integer> listLeft=new ArrayList<>();
        List <Integer> listRight=new ArrayList<>();
        listLeft=inorderTraversal(root.left);
        list.addAll(listLeft);
        list.add(root.val);
        listRight=inorderTraversal(root.right);
        list.addAll(listRight);
        return list;
    }
}

(14)二叉树后序非递归遍历实现

145. 二叉树的后序遍历

class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer>list=new ArrayList<>();
         if(root==null){
            return list;
        }
       
        List<Integer>listLetf=new ArrayList<>();
        List<Integer>listRight=new ArrayList<>();
        listLetf=postorderTraversal(root.left);
        list.addAll(listLetf);
        listRight=postorderTraversal(root.right);
        list.addAll(listRight);
        list.add(root.val);    
        return list;
    }
}

 

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

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

相关文章

CSS中你可能不知道的Selectors特性

CSS中你可能不知道的Selectors特性本文作者为奇舞团前端开发工程师引言最近看了2022年全球CSS调查报告&#xff0c;发现了一些不常见的伪类和伪元素。伪类:has()html<div><h1>H1</h1><h2>H2</h2><p>h1{margin: 0 0 0.25rem 0}</p> &…

设置访问SSH为密钥访问

1.制作密钥对 ssh-keygen输入会问两个问题 设置公私钥名称&#xff08;可以留白&#xff0c;直接回车&#xff09;设置公私钥密码&#xff08;可以留白&#xff0c;直接回车&#xff09; 第一次输入第二次确认 留空确认的话&#xff0c;生成公私钥。共有两个文件 # 私钥 id…

Rxjava源码分析实践(三)【RxJava基本原理分析之订阅流】

本节,我们从Rxjava使用代码入手,去结合自己已有的知识体系,加查阅部分源码验证的方式,来一起探索一下Rxjava实现的基本原理。 为了本文原理分析环节,可以被更多的人理解、学习,所以小编从初学者的角度,从使用入手,一点点的分析了其中的源码细节、思想,建议大家随着本文…

NCMMSC论文介绍 | 探索语音自监督模型的高效融合算法

本文介绍了清华大学语音与音频技术实验室&#xff08;SATLab&#xff09;与上海交通大学跨媒体语言智能实验室&#xff08;X-LANCE&#xff09;合作的NCMMSC录用论文&#xff1a;Exploring Effective Fusion Algorithms for Speech Based Self-Supervised Learning Models。该论…

动态列合并更新

【问题】 I have one query, would be great if anyone can help me out on this. In SQL, I have two tables with same column names. Want to query if there is any difference in the column values and if yes will update the values(in the first table) else if the…

【工具类】后台Mock工具类

目录一、介绍二、使用方法1. Controller层定义接口2. 编写json文件3. 开启AOP4. 调用接口验证三、源码一、介绍 Controller层定义完接口后&#xff0c;不需要写业务逻辑。编写Json文件&#xff0c;调用接口时返回json文件的数据。 优点&#xff1a; 设计阶段即可定义好接口&…

Centos 图形化yum管理工具 - yum Extender

文章目录背景安装开启yum-GUI工具 - yumexyum list installed列出软件包的依赖yum cleam背景 作为一个yum工程师&#xff0c;长期备受yum 命令的煎熬。 难道yum就乜有一个GUI管理界面吗&#xff1f; yum Extender (简称 yumex ) , 是 yum 的图形化操作界面。可以通过 yumex 方…

ActiveMQ高级特性和大厂面试常考重点

目录 一、引入消息队列之后该如何保证其高可用性 二、异步投递Async Sends 三、延迟投递和定时投递 四、ActiveMQ消费重试机制 五、死信队列 六、如何保证消息不被重复消费呢?幂等性问题你谈谈 一、引入消息队列之后该如何保证其高可用性 ActiveMQ集群模式_zoeil的博客-…

【机器学习】KNN 算法介绍

文章目录一、KNN 简介二、KNN 核心思想实例分析&#xff1a;K 值的影响三、KNN 的关键1. 距离计算1. 闵可夫斯基距离2. 曼哈顿距离3. 欧氏距离4. 切比雪夫距离5. 余弦距离总结2. K值选择四、KNN 的改进&#xff1a;KDTree五、KNN 回归算法参考链接一、KNN 简介 KNN 算法&#…

想在微信上使用chatGPT?小程序?公众号?企业微信,最终还是选择了企业微信版本的chatgpt

chatgpt的接口现在都可以正常用了&#xff0c;但是怎么把这个功能放在手机上随用随开呢&#xff1f;微信个人聊天版本小程序版本公众号版本企业微信版本逻辑实现方式微信个人聊天版本 网上很多微信机器人版本的&#xff0c;但是原理是网页版微信&#xff0c;很多账号都不能登陆…

golang指针

指针 区别于C/C中的指针&#xff0c;Go语言中的指针不能进行偏移和运算&#xff0c;是安全指针。 要搞明白Go语言中的指针需要先知道3个概念&#xff1a;指针地址、指针类型和指针取值。 1.1. Go语言中的指针 Go语言中的函数传参都是值拷贝&#xff0c;当我们想要修改某个变…

Linux中如何理解线程?线程ID到底是什么?

朋友们好&#xff0c;这里简要介绍了进程和线程的区别以及对LINUX中线程ID的理解&#xff0c;本人目前理解尚浅&#xff0c;若文中有表述不当的地方还望理解并指正&#xff0c;谢谢大家&#xff01; 文章目录一&#xff1a;进程和线程二&#xff1a;线程ID和进程地址空间布局一…

5 项目部署

5.1 Linux-项目部署 5.1.1 环境 5.1.1.1 开发环境&#xff08;dev&#xff09; 外部用户无法访问&#xff0c;开发人员使用&#xff0c;版本变动很大 平时大家大多是在Windows或者Mac操作系统下去编写代码进行开发. 在开发环境中安装大量的软件&#xff0c;这样会导致环境的稳…

2022 年度盘点 | 更成熟的 AI,更破圈的技术狂欢

By 超神经内容一览&#xff1a;2022 年 AI 领域发展不断提速&#xff0c;新技术成果纷纷落地&#xff0c;模型迭代加速升级。本文总结了 2022 年 AI 领域各大公司的技术成就。关键词&#xff1a;年终盘点 大厂 技术创新2022 年在此起彼伏的咳嗽声中接近尾声&#xff0c;这一…

onCreate、onSaveInstanceState、onRestoreInstance一个参数和两个参数

Android Studio移动应用开发——onCreate、onSaveInstanceState、onRestoreInstance一个参数和两个参数_dear_jing的博客-程序员宅基地 - 程序员宅基地 在做Android生命周期实验过程中&#xff0c;把 Log.i(TAG, "(1) onCreate()") 写到了含有两个参数的函数 onSave…

HTML5 元素拖放

文章目录HTML5 元素拖放概述触发事件实现元素拖放功能dataTransfer元素拖动效果垃圾箱效果HTML5 元素拖放 概述 在HTML5中&#xff0c;我们只需要给元素添加一个draggable属性&#xff0c;然后设置该属性值为true&#xff0c;就能实现元素的拖放。 拖放&#xff0c;指的是“…

【Python】Numpy分布函数总结

文章目录总表均匀分布和三角分布幂分布与正态分布相关的分布与Gamma相关的分布极值分布总表 np.random中提供了一系列的分布函数&#xff0c;用以生成符合某种分布的随机数。下表中&#xff0c;如未作特殊说明&#xff0c;均有一个size参数&#xff0c;用以描述生成数组的尺寸…

【综合笔试题】难度 1.5/5,常规二叉树爆搜题

题目描述 这是 LeetCode 上的 95. 不同的二叉搜索树 II &#xff0c;难度为 中等。 Tag : 「树」、「二叉搜索树」、「BST」、「DFS」、「递归」、「爆搜」 给你一个整数 n&#xff0c;请你生成并返回所有由 n 个节点组成且节点值从 1 到 n 互不相同的不同 二叉搜索树 。可以…

2022出圈的ML研究:爆火的Stable Diffusion、通才智能体Gato,LeCun转推

这些机器学习领域的研究你都读过吗&#xff1f; 2022 年即将步入尾声。在这一年里&#xff0c;机器学习领域涌现出了大量有价值的论文&#xff0c;对机器学习社区产生了深远的影响。 今日&#xff0c;ML & NLP 研究者、Meta AI 技术产品营销经理、DAIR.AI 创始人 Elvis S.…

CSRF漏洞渗透与攻防(一)

目录 前言 什么是CSRF漏洞 CSRF实现流程 CSRF漏洞危害 XSS漏洞危害 CSRF与XSS区别 CSRF分类 GET型&#xff1a; POST型&#xff1a; CSRF漏洞案列模拟 CSRF常用Payload&#xff1a; CSRF漏洞挖掘 检测工具 CSRF漏洞防御 防御思路 我们该如何去防御CSRF漏洞…