【Java 数据结构】二叉树的遍历和基本操作

    • 一、树型结构
      • 1、概念
      • 2、树的表示形式
    • 二、二叉树
      • 1、特点
      • 2、两种特殊的二叉树
      • 3、二叉树的性质
          • 选择题:
      • 4、二叉树的存储
      • 5、二叉树的基本操作
        • 5.1、创建一棵二叉树
        • 5.2、二叉树的遍历
          • LeetCode 144. 二叉树的前序遍历
          • LeetCode 94. 二叉树的中序遍历
          • LeetCode 145. 二叉树的后序遍历
          • 选择题:
      • 5.3二叉树的基本操作
          • 获取树中节点的个数
          • 获取叶子节点的个数
          • 获取第K层节点的个数
          • LeetCode 104. 二叉树的最大深度
          • 检测值为value的元素是否存在
          • LeetCode 958. 二叉树的完全性检验
      • 5.4、二叉树相关oj题
        • LeetCode 100. 检查两颗树是否相同
        • LeetCode 572. 另一棵树的子树
        • LeetCode 110.判断一颗二叉树是否是平衡二叉树
        • LeetCode 101. 对称二叉树
        • 牛客 二叉树的构建及遍历
        • LeetCode 102. 二叉树的层序遍历
        • LeetCode 236.给定一个二叉树, 找到该树中两个指定节点的最近公共祖先
        • 牛客 二叉树搜索树转换成排序双向链表
        • LeetCode 105.根据一棵树的前序遍历与中序遍历构造二叉树
        • LeetCode 106.根据一棵树的中序遍历与后序遍历构造二叉树
        • LeetCode 606. 根据二叉树创建字符串
        • 二叉树前序非递归遍历实现
        • 二叉树中序非递归遍历实现
        • 二叉树后序非递归遍历实现
        • LeetCode 剑指 Offer 27. 二叉树的镜像

一、树型结构

树是一种非线性的数据结构,它是由n(n>=0)个有限结点组成一个具有层次关系的集合。把它叫做树是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。它具有以下的特点:

  • 有一个特殊的结点,称为根结点根结点没有前驱结点
  • 除根结点外,其余结点被分成M(M > 0)个互不相交的集合T1、T2、…、Tm,其中每一个集合 Ti (1 <= i<= m) 又是一棵与树类似的子树。每棵子树的根结点有且只有一个前驱,可以有0个或多个后继
  • 树是递归定义的

注意:树形结构中,子树之间不能有交集,否则就不是树形结构

【Java 数据结构】二叉树的遍历和基本操作_第1张图片

1、概念

【Java 数据结构】二叉树的遍历和基本操作_第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)棵互不相交的树组成的集合称为森林

2、树的表示形式

双亲表示法,孩子表示法、孩子双亲表示法、孩子兄弟表示法等等。
最常用的是孩子兄弟表示法
【Java 数据结构】二叉树的遍历和基本操作_第3张图片

class Node {
	int value; // 树中存储的数据
	Node firstChild; // 第一个孩子引用
	Node nextBrother; // 下一个兄弟引用
}

二、二叉树

1、特点

一棵二叉树是结点的一个有限集合,该集合:
或者为空
或者是由一个根节点加上两棵别称为左子树和右子树的二叉树组成
【Java 数据结构】二叉树的遍历和基本操作_第4张图片

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

注意:对于任意的二叉树都是由以下几种情况复合而成的:
【Java 数据结构】二叉树的遍历和基本操作_第5张图片

2、两种特殊的二叉树

  • 满二叉树: 一棵二叉树,如果每层的结点数都达到最大值,则这棵二叉树就是满二叉树。也就是说,如果一棵二叉树的层数为 K,且结点总数是 2^k - 1,则它就是满二叉树。(等比数列求和)
  • 完全二叉树: 完全二叉树是效率很高的数据结构,完全二叉树是由满二叉树而引出来的。对于深度为K的,有 n 个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从 0 至 n-1 的结点一一对应时称之为完全二叉树。 要注意的是满二叉树是一种特殊的完全二叉树。(依次)

【Java 数据结构】二叉树的遍历和基本操作_第6张图片

3、二叉树的性质

  • ① 若规定根结点的层数为1,则一棵非空二叉树的第i层上最多有 2^(i - 1) (i>0) 个结点
    例:第一层 2^0, 第二层 2^1
  • ② 若规定只有根结点的二叉树的深度为1,则深度为K的二叉树的最大结点数是 2^k - 1 (k>=0)
  • ③ 对任何一棵二叉树, 如果其叶结点个数为 n0, 度为2的非叶结点个数为 n2,则有n0=n2+1,即任何一棵二叉树,叶子节点比度为2的节点多一个

推导:
1.假设二叉树有N个节点。一棵二叉树,要么n0,要么n1度为1的节点,要么n2度为2的节点
N = n0+n1+n21 // ( 1)

2.一个有N个节点的树,应该有N-1条边。
边的总数:
度为0的节点,能产生多少条边?-> 0
度为1的节点,向下只能产生1条边,如果有n1个节点,能产生多少条边? -> n1 条边
度为2的节点,向下只能产生1条边,如果有n2个节点,能产生多少条边? -> 2*n2 条边
N - 1 = 0 + n1 + 2 * n2 // (2)

联合此二表达式:
n0 + n1 + n2 = n1 + 2*n2 + 1
化简得:n0 = n2 + 1

选择题:

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

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

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

【Java 数据结构】二叉树的遍历和基本操作_第7张图片

答案:B A B
  • 具有n个结点的完全二叉树的深度k为 log以2为底(n + 1)的对数向上取整

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

2^k = 532
2^10 = 1024
2^9 = 512
向上取整为10

  • ⑤ 对于具有n个结点的完全二叉树,如果按照从上至下从左至右的顺序对所有节点从0开始编号,则对于序号为 i 的结点有:
    若 i>0,双亲序号:(i-1)/2;i=0,i为根结点编号,无双亲结点
    若 2i+1左孩子序号:2i+1,否则无左孩子
    若 2i+2右孩子序号:2i+2,否则无右孩子

【Java 数据结构】二叉树的遍历和基本操作_第8张图片

4、二叉树的存储

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

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

【Java 数据结构】二叉树的遍历和基本操作_第9张图片
孩子双亲表示法后序在平衡树位置介绍,本文采用孩子表示法来构建二叉树

5、二叉树的基本操作

5.1、创建一棵二叉树

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

// 二叉树的实现
class TreeNode {
    public char val;
    public TreeNode left; // 左孩子的引用
    public TreeNode right; // 右孩子的引用
    public TreeNode(char val) {
        this.val = val;
    }
}

public class BinaryTree {
    // public TreeNoderoot; // 二叉树的根节点

    public TreeNode createTree() {
        TreeNode A = new TreeNode('A');
        TreeNode B = new TreeNode('B');
        TreeNode C = new TreeNode('C');
        TreeNode D = new TreeNode('D');
        TreeNode E = new TreeNode('E');
        TreeNode F = new TreeNode('F');
        TreeNode G = new TreeNode('G');
        TreeNode H = new TreeNode('H');
        A.left = B;
        A.right = C;
        B.left = D;
        B.right = E;
        C.left = F;
        C.right = G;
        E.right = H;
        return A;
    }
}
public class TestDemo {
    public static void main(String[] args) {
        BinaryTree binaryTree = new BinaryTree();
        TreeNode root = binaryTree.createTree();
    }
}

【Java 数据结构】二叉树的遍历和基本操作_第10张图片

5.2、二叉树的遍历

遍历(Traversal)是指沿着某条搜索路线,依次对树中每个结点均做一次且仅做一次访问。访问结点所做的操作依赖于具体的应用问题(比如:打印节点内容、节点内容加1)。
如果N代表根节点,L代表根节点的左子树,R代表根节点的右子树,则根据遍历根节点的先后次序有以下遍历方式:

  • ① NLR:前序遍历 (Preorder Traversal 亦称先序遍历)
    访问根结点 —> 根的左子树 —> 根的右子树。
  • ② LNR:中序遍历 (Inorder Traversal)
    根的左子树 —> 根节点 —> 根的右子树。
  • ③ LRN:后序遍历 (Postorder Traversal)
    根的左子树 —> 根的右子树 —> 根节点。
// 前序遍历
void preOrder(Node root);

// 中序遍历
void inOrder(Node root);

// 后序遍历
void postOrde(Node root);

【Java 数据结构】二叉树的遍历和基本操作_第11张图片
递归实现:

class TreeNode {
    public char val;
    public TreeNode left; // 左孩子的引用
    public TreeNode right; // 右孩子的引用
    public TreeNode(char val) {
        this.val = val;
    }
}

public class BinaryTree {

    public TreeNode createTree() {
        TreeNode A = new TreeNode('A');
        TreeNode B = new TreeNode('B');
        TreeNode C = new TreeNode('C');
        TreeNode D = new TreeNode('D');
        TreeNode E = new TreeNode('E');
        TreeNode F = new TreeNode('F');
        TreeNode G = new TreeNode('G');
        TreeNode H = new TreeNode('H');
        A.left = B;
        A.right = C;
        B.left = D;
        B.right = E;
        C.left = F;
        C.right = G;
        E.right = H;
        return A;
    }

    // 前序遍历
    void preOrder(TreeNode root) {
        if(root == null) {
            return;
        }
        System.out.print(root.val + " ");
        preOrder(root.left);
        preOrder(root.right);
    }

    // 中序遍历
    void inOrder(TreeNode root) {
        if(root == null) {
            return;
        }
        inOrder(root.left);
        System.out.print(root.val + " ");
        inOrder(root.right);
    }

    // 后序遍历
    void postOrder(TreeNode root) {
        if(root == null) {
            return;
        }
        postOrder(root.left);
        postOrder(root.right);
        System.out.print(root.val + " ");
    }
public class TestDemo {
    public static void main(String[] args) {
        BinaryTree binaryTree = new BinaryTree();
        TreeNode root = binaryTree.createTree();
        binaryTree.preOrder(root); // 前 A B D E H C F G
        System.out.println();
        binaryTree.inOrder(root); // 中 D B E H A F C G
        System.out.println();
        binaryTree.postOrder(root); // 后 D H E B F G C A
    }
}
LeetCode 144. 二叉树的前序遍历

144. 二叉树的前序遍历

class Solution {
    // 3、返回值接收
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> retlist = new ArrayList<>();
        if(root == null) {
            return retlist;
        }
        retlist.add(root.val);

        List<Integer> leftTree = preorderTraversal(root.left);
        retlist.addAll(leftTree);

        List<Integer> rightTree = preorderTraversal(root.right);
        retlist.addAll(rightTree);

        return retlist;
    }

    // 2、一个retlist
    // List retlist = new ArrayList<>();
    // public List preorderTraversal2(TreeNode root) {
    //     if(root == null) {
    //         return retlist;
    //     }
    //     retlist.add(root.val);
    //     preorderTraversal(root.left);
    //     preorderTraversal(root.right);
    //     return retlist;
    // }

    // 1、pre 方法
    public List<Integer> preorderTraversal1(TreeNode root) {
        List<Integer> list = new ArrayList<>();
        pre(root, list);
        return list;
    }

    public void pre(TreeNode root, List<Integer> list) {
        if(root != null) {
            list.add(root.val);
            pre(root.left, list);
            pre(root.right, list);
        }
    }
}
LeetCode 94. 二叉树的中序遍历

94. 二叉树的中序遍历

class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> retlist = new ArrayList<>();
        if(root == null) {
            return retlist;
        }
        List<Integer> leftTree = inorderTraversal(root.left);
        retlist.addAll(leftTree);

        retlist.add(root.val);

        List<Integer> rightTree = inorderTraversal(root.right);
        retlist.addAll(rightTree);

        return retlist;
    }
}
LeetCode 145. 二叉树的后序遍历

145. 二叉树的后序遍历

class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> retlist = new ArrayList<>();
        if(root == null) {
            return retlist;
        }
        List<Integer> leftTree = postorderTraversal(root.left);
        retlist.addAll(leftTree);

        List<Integer> rightTree = postorderTraversal(root.right);
        retlist.addAll(rightTree);

        retlist.add(root.val);

        return retlist;
    }
}
  • ④ 层序遍历
    除了先序遍历、中序遍历、后序遍历外,还可以对二叉树进行层序遍历。设二叉树的根节点所在层数为1,层序遍历就是从所在二叉树的根节点出发,首先访问第一层的树根节点,然后从左到右访问第2层上的节点,接着是第三层的节点,以此类推,自上而下,自左至右逐层访问树的结点的过程就是层序遍历。
选择题:

1.某完全二叉树按层次输出(同一层从左到右)的序列为 ABCDEFGH 。该完全二叉树的前序序列为()
A: ABDHECFG
B: ABCDEFGH
C: HDBEAFCG
D: HDEBFGCA

2.二叉树的先序遍历和中序遍历如下:先序遍历:EFHIGJK;中序遍历:HFIEJKG.则二叉树根结点为()
A: E
B: F
C: G
D: H

3.设一课二叉树的中序遍历序列:badce,后序遍历序列:bdeca,则二叉树前序遍历序列为()
A: adbce B: decab C: debac D: abcde

4.某二叉树的后序遍历序列与中序遍历序列相同,均为 ABCDEF ,则按层次输出(同一层从左到右)的序列为()
A: FEDCBA
B: CBAFED
C: DEFCBA
D: ABCDEF

解释:

【Java 数据结构】二叉树的遍历和基本操作_第12张图片

答案:

1.A   2.A   3.D   4.A

5.3二叉树的基本操作

获取树中节点的个数
	/**
     * 2、子问题思路
     * @param root
     * @return root.left + root.right + 1
     */
    int size(TreeNode root) {
        if(root == null) {
            return 0;
        }
        return size(root.left) + size(root.right) + 1;
    }

    /**
     * 1、遍历思路:遍历二叉树,此次root不是空,计数器++
     * @param root
     * @return
     */
    int count = 0;
    int size1(TreeNode root) {
        if(root == null) {
            return 0;
        }
        count++;
        size1(root.left);
        size1(root.right);
        return count;
    }
获取叶子节点的个数
	/**
     * 2、子问题思路:左叶子 + 右叶子
     * @param root
     * @return
     */
    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);
    }

    /**
     *  1、遍历思路:遍历到子叶子,计数器++
     * @param root
     * @return
     */
    int leafCount = 0;
    int getLeafNodeCount(TreeNode root) {
        if(root == null) {
            return 0;
        }
        // 左右都空 是叶子
        if(root.left == null && root.right == null) {
            leafCount++;
        }
        getLeafNodeCount(root.left);
        getLeafNodeCount(root.right);
        return leafCount;
    }
获取第K层节点的个数
    int getKLevelNodeCount(TreeNode root, int k) {
        if(root == null || k <= 0) {
            return 0;
        }
        if(k == 1) { // 第一层
            return 1;
        }
        return getKLevelNodeCount(root.left, k - 1) + getKLevelNodeCount(root.right, k - 1);
    }
LeetCode 104. 二叉树的最大深度

104. 二叉树的最大深度

class Solution {
    // 广度优先搜索 层序遍历
    // 每次拓展下一层的时候,将队列里的所有节点都拿出来进行拓展,
    // 保证每次拓展完的时候队列里存放的是当前层的所有节点
    public int maxDepth(TreeNode root) {
        if(root == null) return 0;
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        int ans = 0; // 拓展的次数
        while(!queue.isEmpty()) {
            int size = queue.size(); // 每一层全部取出
            while(size != 0) {
                TreeNode cur = queue.poll();
                if(cur.left != null) {
                    queue.offer(cur.left);
                }
                if(cur.right != null) {
                    queue.offer(cur.right);
                }
                size--;
            }
            ans++;
        }
        return ans;
    }

    // 时间复杂度:O(n)
    public int maxDepth1(TreeNode root) {
        if(root == null) {
            return 0;
        }
        return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1;
        /*int leftHeight = getHeight(root.left);
        int rightHeight = getHeight(root.right);
        return leftHeight > rightHeight ? leftHeight + 1: rightHeight + 1;*/
    }
}
检测值为value的元素是否存在
	TreeNode find(TreeNode root, int val) {
        if(root == null) return null;
        // 先判断根
        if(root.val == val) return root;
        // 找左
        TreeNode ret = find(root.left, val);
        if(ret != null) {
            return ret;
        }
        // 找右
        ret = find(root.right, val);
        if(ret != null) {
            return ret;
        }
        return null;
    }
	public static void main(String[] args) {
        BinaryTree binaryTree = new BinaryTree();
        TreeNode root = binaryTree.createTree();
        // test
        try {
            TreeNode ret = binaryTree.find(root, 'E');
            System.out.println(ret.val);
        }catch (NullPointerException e) {
            e.printStackTrace();
            System.out.println("没有该节点");
        }

    }
LeetCode 958. 二叉树的完全性检验

958. 二叉树的完全性检验

	public boolean isCompleteTree(TreeNode root) {
        if(root == null) {
            return true;
        }
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        while(!queue.isEmpty()) {
            // 不是空 入左右
            TreeNode cur = queue.poll(); // 弹出判断
            if(cur != null) {
                queue.offer(cur.left);
                queue.offer(cur.right);
            } else {
                break;
            }
        }
        // 判断队列 有不为空的元素 就不是完全二叉树
        while(!queue.isEmpty()) {
            if(queue.poll() != null) {
                return false;
            }
        }
        return true;
    }

5.4、二叉树相关oj题

LeetCode 100. 检查两颗树是否相同

100. 相同的树

class Solution {
    // 时间复杂度:O(min(m, n)) -> p,q节点的个数
    public boolean isSameTree(TreeNode p, TreeNode q) {
        // 一个为空 一个不为空
        if(p == null && q != null || q == null && p != null) {
            return false;
        }
        // 都为空
        if(p == null && q == null) {
            return true;
        }
        // 或都不为空->判断val
        if(p.val != q.val) {
            return false;
        }
        return isSameTree(p.left, q.left) && isSameTree(p.right, q.right);
    }
}
LeetCode 572. 另一棵树的子树

572. 另一棵树的子树

class Solution {
    // // 时间复杂度:O(m * n)
    // 先判断是不是相同的数
    // 再判断是不是root的左子树或右子树
    public boolean isSubtree(TreeNode root, TreeNode subRoot) {
        // 判断isSubtree的 判断根是不是子树 如果没有就会空指针异常
        if(root == null || subRoot == null) {
            return false;
        }
        if(isSameTree(root, subRoot)) {
            return true;
        }
        // 判断subRoot 是不是root 的左子树或右子树
        if(isSubtree(root.left, subRoot)) {
            return true;
        }
        if(isSubtree(root.right, subRoot)) {
            return true;
        }
        return false;
    }

    // 检查两颗树是否相同
    private boolean isSameTree(TreeNode p, TreeNode q) {
        // 一个为空 一个不为空
        if(p == null && q != null || q == null && p != null) {
            return false;
        }
        // 都为空
        if(p == null && q == null) {
            return true;
        }
        // 或都不为空->判断val
        if(p.val != q.val) {
            return false;
        }
        return isSameTree(p.left, q.left) && isSameTree(p.right, q.right);
    }
}
LeetCode 110.判断一颗二叉树是否是平衡二叉树

110. 平衡二叉树

class Solution {
    // 时间复杂度:O(n)
    public boolean isBalanced(TreeNode root) {
        if(root == null) return true;

        return height(root) >= 0;
    }

    public int height(TreeNode root) {
        if(root == null) return 0;
        int leftHeight = height(root.left);
        int rightHeight = height(root.right);
        
        if(leftHeight >= 0 && rightHeight >= 0 && Math.abs(leftHeight - rightHeight) <= 1) {
            return Math.max(leftHeight, rightHeight) + 1;
        } else {
            // 不平衡 返回-1
            return -1;
        }
    }

    // 时间复杂度:O(n^2)
    // 判断高度差
    // 判断左右 每棵子树都要是平衡二叉树
    /* public boolean isBalanced(TreeNode root) {
        if(root == null) {
            return true;
        }
        int left = height(root.left);
        int right = height(root.right);
        // 绝对值
        return Math.abs(left - right) <= 1 && isBalanced(root.left) && isBalanced(root.right);
    }

    public int height(TreeNode root) {
        if(root == null) {
            return 0;
        }
        return Math.max(height(root.left), height(root.right)) + 1;
    } */
}
LeetCode 101. 对称二叉树

101. 对称二叉树

class Solution {
    public boolean isSymmetric(TreeNode root) {
        if(root == null) return true;
        return isSymmetricChild(root.left, root.right);
    }

    public boolean isSymmetricChild(TreeNode leftTree, TreeNode rightTree) {
        // 判断根
        if(leftTree == null && rightTree != null) return false;
        if(leftTree != null && rightTree == null) return false;
        if(leftTree == null && rightTree == null) return true;
        
        if(leftTree.val != rightTree.val) return false;

        // 判断根的左右
        return isSymmetricChild(leftTree.left, rightTree.right) && 
            isSymmetricChild(leftTree.right, rightTree.left);
    }
}
牛客 二叉树的构建及遍历

KY11 二叉树遍历

import java.util.*;

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

public class Main {
    
    // 创建二叉树
    public static int i = 0;
    public static TreeNode createTree(String str) {
        TreeNode root = null;
        if(str.charAt(i) != '#') {
            root = new TreeNode(str.charAt(i));
            i++;
            root.left = createTree(str);
            root.right = createTree(str);
        } else {
            i++;
        }
        return root; // 跳过#后返回left或right
    }
    
    // 中序遍历
    public static void inorder(TreeNode root) {
        if(root == null) return;
        inorder(root.left);
        System.out.print(root.val + " ");
        inorder(root.right);
    }
    
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        while(in.hasNextLine()) {
            String str = in.nextLine();
            TreeNode root = createTree(str);
            inorder(root);
            System.out.println();
        }
    }
}
LeetCode 102. 二叉树的层序遍历

102. 二叉树的层序遍历

// 不加入List的层序遍历
public void levelOrder(TreeNode root) {
        Queue<TreeNode> queue = new LinkedList<>();
        if(root == null) return;
        queue.offer(root);
        // 队列不为空 弹出打印 加入不为空的左和右
        while(!queue.isEmpty()) {
            TreeNode cur = queue.poll();
            System.out.print(cur.val + " ");
            if(cur.left != null) {
                queue.offer(cur.left);
            }
            if(cur.right != null) {
                queue.offer(cur.right);
            }
        }
    }
class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
        List<List<Integer>> ret = new ArrayList<>();
        if(root == null) return ret;

        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        
        while(!queue.isEmpty()) {
            int size = queue.size(); // 当前层有多少节点
            List<Integer> list = new ArrayList<>();
            while (size != 0) {
                TreeNode cur = queue.poll(); // 弹出
                list.add(cur.val); // 加入list
                if(cur.left != null) { // 左
                    queue.offer(cur.left);
                }
                if(cur.right != null) { // 右
                    queue.offer(cur.right);
                }
                size--;
            }
            ret.add(list); // 每一层
        }
        
        return ret;
    }
}
LeetCode 236.给定一个二叉树, 找到该树中两个指定节点的最近公共祖先

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

class Solution {
    /* 2、思路二:
    假设是孩子双亲表示法 可以转变为链表求交点 但是没有parent双亲节点
    所以用栈:
    1. 用两个栈存储路径
    2. 求栈的大小
    3. 栈中多的元素 出差值个元素
    4. 同出栈 栈顶元素相同 就是公共祖先
    */
    // root:根节点   node:指定节点   stack:存放根结点到指定节点的路径
    public boolean getPath(TreeNode root, TreeNode node, Stack stack) {
        if(root == null || node == null) return false;
        stack.push(root);

        if(root == node) return true;

        boolean flg = getPath(root.left, node, stack);
        if(flg) return true;
        flg = getPath(root.right, node, stack);
        if(flg) return true;

        stack.pop(); // 当前root不在路径下 弹出 返回false
        return false;
    }

    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if(root == null) return null;
        // 获取路径
        Stack<TreeNode> stack1 = new Stack<>();
        getPath(root, p, stack1);
        Stack<TreeNode> stack2 = new Stack<>();
        getPath(root, q, stack2);

        int size1 = stack1.size();
        int size2 = stack2.size();
        // 出差值个元素
        if(size1 > size2) {
            int size = size1 - size2; // 出第一个栈的元素
            while(size != 0) {
                stack1.pop();
                size--;
            }
        }
        if(size1 < size2) {
            int size = size2 - size1; // 出第二个栈的元素
            while(size != 0) {
                stack2.pop();
                size--;
            }
        }
        // 判断栈顶元素
        while(!stack1.isEmpty() && !stack2.isEmpty()) {
            if(stack1.peek() == stack2.peek()) { // 判断地址
                return stack1.pop();
            } else {
                stack1.pop();
                stack2.pop();
            }
        }
        return null;
    }

    // LCA 问题
    // 1、思路一:
    /* 如果是二叉搜索树:中序遍历大小是有序的,左<根<右
    root.val == p || root.val == q 公共祖先是root
    p.val < root.val && q.val < root.val 说明p和q都在root的左子树中 最近公共祖先在root左树中
    p.val > root.val && q.val > root.val 说明p和q都在root的右子树中 最近公共祖先在root右树中
    p.val > root.val && q.val < root.val 分别在左子树和右子树中 最近公共祖先就是root
    */
    public TreeNode lowestCommonAncestor1(TreeNode root, TreeNode p, TreeNode q) {
        if(root ==  null) return null;
        if(root == p || root == q) {
            return root;
        }
        TreeNode leftT = lowestCommonAncestor(root.left, p, q);
        TreeNode rightT = lowestCommonAncestor(root.right, p, q);
        
        if(leftT != null && rightT != null) {
            return root;
        } else if(leftT != null) {
            return leftT;
        } else {
            return rightT;
        }
    }
}
牛客 二叉树搜索树转换成排序双向链表

JZ36 二叉搜索树与双向链表

public class Solution {
    public TreeNode prev;
    public void inorder(TreeNode pCur) {
        if(pCur == null) return;
        inorder(pCur.left);
        
        pCur.left = prev;
        if(prev != null) {
            prev.right = pCur;
        }
        prev = pCur;
        
        inorder(pCur.right);
    }
    
    public TreeNode Convert(TreeNode pRootOfTree) {
        if(pRootOfTree == null) return null;
        inorder(pRootOfTree);
        
        TreeNode head = pRootOfTree;
        while(head.left != null) {
            head = head.left;
        }
        return head;
    }
}
LeetCode 105.根据一棵树的前序遍历与中序遍历构造二叉树

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

class Solution {
    // 通过前序与中序遍历创建二叉树
    public int preIndex = 0;
    public TreeNode createTreeByPandI(int[] preorder, int[] inorder, int inbegin, int inend) { 
        if(inbegin > inend) {
            // 此时没有左树 或右树
            return null;
        }

        TreeNode root = new TreeNode(preorder[preIndex]);

        // 找更根在中序遍历中的位置
        int rootIndex = findIndexOfI(inorder, inbegin, inend, preorder[preIndex]);
        if(rootIndex == -1) return null;
        
        preIndex++;
        root.left = createTreeByPandI(preorder, inorder, inbegin, rootIndex-1); // 创建当前root左树
        root.right = createTreeByPandI(preorder, inorder, rootIndex+1, inend); // 右树

        return root;
    }

    // 在中序遍历中 在inbegin-inend范围内 找key
    public int findIndexOfI(int[] inorder, int inbegin, int inend, int key) {
        for(int i = inbegin; i <= inend; i++) {
            if(inorder[i] == key) {
                return i;
            }
        }
        return -1;
    }

    public TreeNode buildTree(int[] preorder, int[] inorder) {
        if(preorder == null || inorder == null) return null;

        return createTreeByPandI(preorder, inorder, 0, inorder.length - 1);
    }
}
LeetCode 106.根据一棵树的中序遍历与后序遍历构造二叉树

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

class Solution {
    public int postIndex = 0;
    public TreeNode createTreeByPandI(int[] inorder, int[] postorder, int inbegin, int inend) { 
        if(inbegin > inend) {
            // 此时没有左树 或右树
            return null;
        }

        TreeNode root = new TreeNode(postorder[postIndex]);

        // 找根在中序遍历中的位置
        int rootIndex = findIndexOfI(inorder, inbegin, inend, postorder[postIndex]);
        if(rootIndex == -1) return null;
        
        postIndex--; // 后序遍历 根在后 往前减

        // 分别创建 右子树 和 左子树
        root.right = createTreeByPandI(inorder, postorder, rootIndex+1, inend);
        root.left = createTreeByPandI(inorder, postorder, inbegin, rootIndex-1);

        return root;
    }

    // 在中序遍历中 在inbegin-inend范围内 找key
    public int findIndexOfI(int[] inorder, int inbegin, int inend, int key) {
        for(int i = inbegin; i <= inend; i++) {
            if(inorder[i] == key) {
                return i;
            }
        }
        return -1;
    }

    public TreeNode buildTree(int[] inorder, int[] postorder) {
        if(inorder == null || postorder == null) return null;

        postIndex = postorder.length - 1;

        return createTreeByPandI(inorder, postorder, 0, inorder.length-1);
    }
}
LeetCode 606. 根据二叉树创建字符串

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

class Solution {
    public void TreeToString(TreeNode t, StringBuilder sb) {
        if(t == null) return;
        sb.append(t.val);
        if(t.left != null) {
            sb.append("(");
            TreeToString(t.left, sb);
            sb.append(")");
        } else {
            if(t.right == null) {
                return;
            } else {
                sb.append("()"); // 左为空 右不为空
            }
        }
        if(t.right == null) {
            return;
        } else {
            sb.append("(");
            TreeToString(t.right, sb);
            sb.append(")");
        }
    }

    public String tree2str(TreeNode root) {
        if(root == null) return null;
        StringBuilder sb = new StringBuilder();
        TreeToString(root, sb);
        return sb.toString();
    }
}
二叉树前序非递归遍历实现

144. 二叉树的前序遍历

	public List<Integer> preorderTraversal(TreeNode root) {
       List<Integer> ret = new ArrayList<>();
        
        Stack<TreeNode> stack = new Stack<>();
        TreeNode  cur = root;
        
        while(cur != null || !stack.empty()) {
            while(cur != null) {
                stack.push(cur);
                // System.out.print(cur.val + " ");
                ret.add(cur.val);
                cur = cur.left;
            }
            TreeNode top = stack.pop();
            cur = top.right;
        }
        return ret;
    }
二叉树中序非递归遍历实现

94. 二叉树的中序遍历

	public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> ret = new ArrayList<>();

        Stack<TreeNode> stack = new Stack<>();
        TreeNode  cur = root;

        while(cur != null || !stack.empty()) {
            while(cur != null) {
                stack.push(cur);
                
                cur = cur.left;
            }
            TreeNode top = stack.pop();
            // System.out.print(cur.val + " ");
            ret.add(top.val);
            cur = top.right;
        }
        return ret;
    }
二叉树后序非递归遍历实现

145. 二叉树的后序遍历

	public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> ret = new ArrayList<>();
        Stack<TreeNode> stack = new Stack<>();
        TreeNode  cur = root;
        TreeNode prev = null;
        
        while(cur != null || !stack.empty()) {
            while(cur != null) {
                stack.push(cur);

                cur = cur.left;
            }
            
            TreeNode top = stack.peek();
            // 如果当前节点的右子树 遍历过 直接弹出
            if(top.right == null || top.right == prev) {
                stack.pop();
                // System.out.print(top.val + " ");
                ret.add(top.val);
                prev = top; // 最近一次打印的节点
            } else {
                cur = top.right;
            }
        }
        return ret;
    }
LeetCode 剑指 Offer 27. 二叉树的镜像

剑指 Offer 27. 二叉树的镜像

class Solution {
    public TreeNode mirrorTree(TreeNode root) {
        if(root == null) {
            return null;
        }
        // 交换根节点左右子树
        TreeNode tmp = root.left;
        root.left = root.right;
        root.right = tmp;
        
        mirrorTree(root.left);
        mirrorTree(root.right);
        
        return root;
    }
}

你可能感兴趣的:(数据结构,数据结构,java,算法,二叉树,leetcode)