什么是树?
树形结构的概念
树是一种非线性的数据结构,它是由n(n>=0)个优先节点组成一个具有层次关系的集合。把它叫作树,是因为它看起来像一棵树,也就是说它是根朝上,而叶朝下。 它具有以下特点: 1、有一个特殊的结点,称为根结点,根节点没有前驱结点 2、除根结点外,其余结点被分成 M (M > 0) 个互不相交的集合 T1、 T2、…、 Tm,其中每一个集合 Ti(1 <= i <= m)又是一棵与树类似的子树。每棵子树的根结点有且只有一个前驱,可以有0个或多个后继。 3、 树是递归定义的。(这个等到 具体讲二叉树的时候会讲到的) 注意:树形结构中,子树之间不能有交集,否则就不是树形结构。
?
重要概念
结点的度:一个结点含有子树的个数;如上图:A的度为6。 ? 树的度:一棵树中,所有结点度的最大值称为树的度;如上图:树的度为6。 ? 叶子结点或终端结点:度为0的节点成为叶节点;如上图:B、C、H、I、P、Q、K、L、M、N都是叶子结点。
? 双亲结点或父结点:若一个结点含有子结点,则这个结点称为其子结点的父结点/双亲节点;如上图:A 是 B 的父结点。还有很多就不说了。
? 孩子结点或子结点:一个结点 含有的子树的根结点 称为 该节点 的 子结点;如上图: B 是 A 的孩子结点
? 根结点:一棵树中,没有双亲结点的结点;如上图:A ? 结点的层次:从根开始定义起,根为第一层,根的子结点为第2层,依次类推。 ?
树的高度或深度:树中最大深度 ,即为高度;如上图:树的高度为 4,最大深度也为 4。但两者是不同的定西。 只有:当深度最大时, 深度才能说是高度。
? 非终端结点或分支结点: 度不为0的节点;如上图:D E F G J 都是分支结点。【简单来说:除了叶子结点【度为0的结点】,其它结点都是分支结点/非终端结点】 ? 兄弟结点:具有相同父结点 的 节点 互称为兄弟结点;如上图:B C 互为兄弟结点。 ?
堂兄弟结点:双亲/父 在同一层的结点互为堂兄弟; 如上图:H I 互为堂兄弟结点。 ?
结点的祖先:从该节点出发,可以经过所有分支上的节点;如上图:A 是 所有节点的祖先。 ? 子孙:以某个结点为根的子树中任一结点都称为该结点的子孙。如上图:所有结点都是A节点的子孙。 ? 森林: 由 m(m >= 0)棵 互不相交的树组成的集合称为森林
?
树的表示形式
树结构相对线性表就比较复杂了,要存储表示起来就比较麻烦了,实际中树有很多种表示方式. 如:双亲表示法、孩子表示法、孩子双亲表示法、孩子兄弟表示法等等。 我们这里就简单的了解其中最常用的孩子兄弟表示法。 这里,我们在额外的讲一下 双亲表示法,就是 孩子兄弟法,加了一个存储空间,用来存储自身的父结点
?
树的应用
文件系统管理(目录 和 文件) 试想一下:我们的电脑常有空文件夹,点进去,就没有东西点了。对不对?那这空文件夹是不是就可以看作是叶子结点!【没有子结点,或者“度”为零嘛。】 ? 再来举一个例子:
?
二叉树
概念
一棵二叉树是结点的一个有限集合,该集合: 1、可能为 空 2、可能是由一个根结点加上两棵别称为 左子树 和 右子树 的 二叉树组成。 二叉树 跟我们前面讲的树的区别就在于:二叉树 的 每个结点,最多只能有 两个 “孩子”/子树.。最少 零个。 也就是说:一棵树,如果是二叉树,那么它的每棵子树都是 二叉树【都有左子树 和 右子树】。
?
总结
1. 二叉树不存在度大于2 的结点 2.二叉树的子树有左右之分,次序不能颠倒,因此二叉树为有序树。 注意:对于任意的二叉树都是由以下几种情况复合而成的 上图的这四棵树都是二叉树
?
两种特殊的二叉树
1、满二叉树:一棵二叉树,如果每层的结点数都达到最大值,则这棵二叉树就是满二叉树。也就是说:如果一棵二叉树的层数为K,且结点总数是 2的k次方-1,则它就是满二叉树。 ? 2、完全二叉树:完全二叉树是效率很高的数据结构,完全二叉树是由满二叉树引申出来的。对于深度为K的,有n个节点的二叉树,当其每一个结点都与深度为k的满二叉树中编号从 0 值 n -1 的结点 一一对应时称之为完全二叉树。值得注意的是 满二叉树 是一种特殊的 完全二叉树。 【简单来说: 给二叉树编号,根结点为0,然后下一层次 从左往右,依次编号。中间不能有间断,依次类推】
?
二叉树性质
性质1、 若规定根结点的层数为1,则一棵非空二叉树的第i层上,最多有 2的 (i - 1)次方。【这个我们在前面推导满二叉树的时候,已经出来了 2的(k-1)次方】 性质2、若规定只有根结点的二叉树的深度为1,则深度为k的二叉树的最大结点数是 2的K次方减一(k>=0)。【就是满二叉树】 性质3、对于任何一棵二叉树,如果其 叶结点个数 为 n0,度为2的非叶结点个数为 n2,则有 n0 = n2 + 1。【也就是说:叶结点的个数 总是比 非叶结点的个数多一个】 ? 得出结论:任何一棵二叉树的叶子结点 比 度为2的节点 多一个。
来看三个练习题
? 性质4、具有n个结点的完全二叉树的深度 k 为 log以为2底的(n+1)上取整 再来看个练习题
? 性质5. 对于具有n个结点的完全二叉树,如果按照从上至下从左至右的顺序对所有节点从0开始编号,则对于序号为i 的结点有: 若 i > 0,双亲序号:(i-1)/2;i=0,i为根结点编号,无双亲结点 若2i+1<n,左孩子序号:2i+1,否则无左孩子 若2i+2<n,右孩子序号:2i+2,否则无右孩子
?
二叉树存储
二叉树的存储结构分为:顺序存储 和 类似于链表的链式存储。 这里,我们讲链式存储。 二叉树的链式存储是通过一个一个的节点引用起来的。常见的表示方式有二叉 和 三叉表示方式。 【二叉 : 孩子表示法;三叉 :孩子双亲表示法】
?
模拟实现二叉树
提前说明:二叉树的构建是一个非常复杂的过程,因为目前作者对二叉树的理解,还不是很深。所以,我们先会创建一个二叉树,但是这种创建方式,很LOW, 只是为了应付前期使用,比较简单,不是正确的常用创建方式。
?
准备工作
构造二叉树节点 - (孩子表示法)
?
构建一棵这样的二叉树
?
debug【调试效果图】
?
二叉树最重要的功能 - 遍历二叉树 - (一级标题 更显重要。)
学习二叉树结构,最简单的方式就是遍历。所谓遍历(Traversal)是指沿着某条搜索路线,依次对树中每个结 点均做一次且仅做一次访问。访问结点所做的操作依赖于具体的应用问题【打印节点值,求节点个数等】。所有的二叉树相关的题目,基本上都是需要通过某种遍历去解题的。
?
1. NLR:前序遍历(Preorder Traversal 亦称先序遍历)——访问根结点—>根的左子树—>根的右子树
?
2. LNR:中序遍历(Inorder Traversal)——根的左子树—>根节点—>根的右子树
它的遍历顺序 就是 左子树 》》 根结点 》》 右子树
?
3. LRN:后序遍历(Postorder Traversal)——根的左子树—>根的右子树—>根节点
遍历顺序: 左子树 》》 右子树 》》 根结点
?
小结
前中后,无论是哪一种遍历,都是按照下面图的路线,来走的,只是打印的顺序不同而已,
?
练习
写出下面二叉树的 前中后排序的 序列 前序遍历:A B D E H C F G 中序遍历:D B H E A F C G 后序遍历:D H E B F G C A
?
程序实现 - 前中后序遍历
接着模拟实现二叉树,完成其遍历功能
?
前序遍历
?
拓展
二叉树的题 天生就是用递归来写的。【90%】 剩余 10%,使用非递归来解决问题。【只针对部分题】 其实所有的递归 都是 跟栈有关系的。
?
中序遍历
与前序同理,就是打印顺序不同.
public void inorderTraversal(BTNode root){
if(root == null){
return;
}
inorderTraversal(root.left);
System.out.print(root.val + " ");
inorderTraversal(root.right);
}
?
后序遍历
与前序同理,就是打印顺序不同.
public void postorderTraversal(BTNode root){
if(root == null){
return;
}
postorderTraversal(root.left);
postorderTraversal(root.right);
System.out.print(root.val + " ");
}
?
这题,你可以直接将我们刚才的程序,复制粘贴。 唯一需要注意的就是 不过也很好解决。new一个 List 的嘛。反正它只用来存储前序遍历的结果序列。 其次,就是把输出语句 换成 add 添加 语句,将遍历的结果存入
?
代码如下
class Solution {
List<Integer> list;
public List<Integer> preorderTraversal(TreeNode root) {
list = new ArrayList<>();
preorder(root);
return list;
}
public void preorder(TreeNode root){
if(root == null){
return;
}
list.add(root.val);
preorder(root.left);
preorder(root.right);
}
}
?
做法 完全一样,改变一下,add 添加数据的代码位置就行了
代码如下:
class Solution {
List<Integer> list;
public List<Integer> inorderTraversal(TreeNode root) {
list = new ArrayList<>();
inorder(root);
return list;
}
public void inorder(TreeNode root){
if(root == null){
return;
}
inorder(root.left);
list.add(root.val);
inorder(root.right);
}
}
?
我就直接上代码。。。。
class Solution {
List<Integer> list;
public List<Integer> postorderTraversal(TreeNode root) {
list = new ArrayList<>();
postorder(root);
return list;
}
public void postorder(TreeNode root){
if(root == null){
return;
}
postorder(root.left);
postorder(root.right);
list.add(root.val);
}
}
?
将二叉树 整体分为 根结点,左子树 和 右子树 三部分,将其按照前序的遍历顺序进行添加。
代码如下
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> list = new ArrayList<>();
if(root == null){
return list;
}
list.add(root.val);
List<Integer> treeLeft = preorderTraversal(root.left);
list.addAll(treeLeft);
List<Integer> treeRight = preorderTraversal(root.right);
list.addAll(treeRight);
return list;
}
}
?
代码分析
4. 层序遍历
除了先序遍历、中序遍历、后序遍历外,还可以对二叉树进行层序遍历。设二叉树的根节点所在 层数为1,层序遍历就是从所在二叉树的根节点出发,首先访问第一层的树根节点,然后从左到右访问第2层 上的节点,接着是第三层的节点,以此类推,自上而下,自左至右逐层访问树的结点的过程就是层序遍历
?
练习题
?
现在开始实现二叉树所具有的基本功能
获取二叉树的节点个数
?
获取叶子结点个数
?
获取第K层节点的个数 - 子问题思路
?
获取二叉树的高度 - 子问题思路
?
检测值为value的元素是否存在
我们都知道在数组当中 查找一个数据:遍历数组,依次查找。 如果是链表中,是不是也是需要从头节点开始遍历比较,确认是不是存在的。 而现在是二叉树,是不是也需要遍历二叉树紫红的节点,确定某一个节点的值,是不是我们要找的。 最简单就是运用前序遍历来实现。先判断根结点值是否与 value值相等,其次 左子树,最后是右子树。
public BTNode find(BTNode root,char value){
if(root == null){
return null;
}
if(root.val == value){
return root;
}
BTNode node1 = find(root.left,value);
BTNode node2 = find(root.right,value);
return node1 == null ? node2:node1;
}
?
判断一棵树是不是完全二叉树
?
拓展: 队列中 入队为元素为 null,队列也是有大小的。
?
与面试相关的二叉树OJ题型
?
题目解析
注意我框选的题干部分【如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的。】 它的意思: 两棵二叉树的节点位置 与 节点值 都必须一模一样。 两个条件中,只要有一个条件不符合。那么,这棵二叉树就不说是相同的树。
?
解题思维
利用遍历思想 去完成这道题:就是 使用同一种遍历方式,来遍历两棵二叉树节点,如果两棵树的节点值,有一组不相等就返回false。 但是!我们需要注意几个特殊情况! 情况1: 两棵二叉树为 空树【两棵二叉树的根结点为 null】 情况2: 一棵二叉树为null,另一棵不为null。
?
代码如下
class Solution {
public boolean isSameTree(TreeNode p, TreeNode q) {
if(p == null && q == null){
return true;
}
if( (p != null && q == null) || (p == null && q != null) ){
return false;
}
if(p.val != q.val){
return false;
}
return isSameTree(p.left,q.left) && isSameTree(p.right,q.right);
}
}
?
?
题目解析
简单来说: 在 根结点为 root 的 二叉树中,寻找有没有 一棵 与 根结点为subRoot的二叉树 相同的树。
?
解题思维
既然是寻找相同的树,那么,肯定是需要一个方法来判断 相同的树。【上一题的代码,就可以直接复制过来,作为一个判断相同树的方法来使用】 subRoot 为 root 的子树,无非就是以下几种情况: 1、root 与 subRoot 是一棵相同的树 2、 subRoot 是 root 的 左子树 ,或者说 是 左子树的一部分, 3、 subRoot 是 root 的 右子树 ,或者说 是 右子树的一部分, ? 注意:以上这些操作,都可以交由我们的判断相同的方法来操作。但这不意味着我们可以什么都不用做!
?
代码如下
class Solution {
public boolean isSameTree(TreeNode p, TreeNode q) {
if(p == null && q == null){
return true;
}
if( (p != null && q == null) || (p == null && q != null) ){
return false;
}
if(p.val != q.val){
return false;
}
return isSameTree(p.left,q.left) && isSameTree(p.right,q.right);
}
public boolean isSubtree(TreeNode root, TreeNode subRoot) {
if(root == null){
return subRoot!=null ? false:true;
}
if(isSameTree(root,subRoot)){
return true;
}
if(isSubtree(root.left,subRoot)){
return true;
}
if(isSubtree(root.right,subRoot)){
return true;
}
return false;
}
}
?
这题就是我们前面实现的 求二叉树高度功能。【高度 等价于 二叉树的最大深度】 这里我就直接上代码了。
class Solution {
public int maxDepth(TreeNode root) {
if(root == null){
return 0;
}
return Math.max(maxDepth(root.left),maxDepth(root.right)) + 1;
}
}
?
?
题目解析
题目的意思: 让我们去计算 这棵二叉树的左右子树的高度。其高度差不超过 1。 唯一需要注意的是:平衡二叉树 不仅仅是 左右子树 是这么个情况,还需要把左右子树 看作一个 二叉树,而且也必须是一个平衡二叉树。 也就是说:平衡二叉树 的 左右子树高度差 不超过 1,且如果将左右子树看作 是 一棵单独二叉树 ,也是一棵平衡二叉树。
?
代码如下
时间复杂度 O(N)
空间复杂度 O(log2N)
class Solution {
public boolean isBalanced(TreeNode root) {
if(root == null){
return true;
}
int leftHeight = getHeight(root.left);
int rightHeight = getHeight(root.right);
return Math.abs(leftHeight - rightHeight) <= 1 && isBalanced(root.left) && isBalanced(root.right);
}
public int getHeight(TreeNode root){
if(root == null){
return 0;
}
return Math.max(getHeight(root.left),getHeight(root.right)) + 1;
}
}
?
进阶
?
代码如下
class Solution {
public boolean isBalanced(TreeNode root) {
if(root == null){
return true;
}
return getHeight(root)> -1;
}
public int getHeight(TreeNode root){
if(root == null){
return 0;
}
int L = getHeight(root.left);
int R = getHeight(root.right);
if(L>=0 && R>=0 && Math.abs(L-R)<=1){
return Math.max(L,R) + 1;
}else{
return -1;
}
}
}
?
?
题目解析
简单来说:以根结点为分界线,将二叉树的左右子树分割开来,并以此线 作为对称轴。 对称简单来说:将这上图看作一张纸,沿着 对称轴 折叠,你会发现 左右子树的节点完美重合【位置和值都是】
?
解题思维
简单来说:以二叉树的根结点 1 为 分割线, 去判断 左右子树 节点的值是否对称。 对称图如下: 但是有几个特殊情况需要注意! 1、二叉树为空树,根结点为null。【空树也是树,也算对称树】 2、二叉树只有一个根结点,也算对称二叉树【左右两个空子树】 3、 二叉树的左右子树中:有一个为空子树【非对称子树】
?
代码如下:
class Solution {
public boolean isSymmetric(TreeNode root) {
if(root == null){
return true;
}
return isSymmetricChild(root.left,root.right);
}
public boolean isSymmetricChild(TreeNode L,TreeNode R){
if(L == null && R == null){
return true;
}
if( (L == null && R != null) || (L != null && R == null) ){
return false;
}
if(L.val == R.val){
return isSymmetricChild(L.left,R.right) &&isSymmetricChild(L.right,R.left);
}
return false;
}
}
?
?
题目解析
简单来说:根据所给出 二叉树 前序遍历序列,构造出一棵二叉树。对其进行 中序遍历,输出结果。
?
解题思维
首先,我们先根据 它所给的 前序遍历序列 和 关键条件【# 代表 空树】 ,根据示例的 数据,来 推导构造出一棵二叉树 难点:是推导出来了,也画出来了,但是代码具体如何实现? 答案:既然 题目 给的是前序遍历的结果,我们也需要用前序遍历的方式来构建。
?
解题步骤
程序框架
?
creationTree 方法 【构造二叉树】
思路: 创建一个静态变量 i ,用来遍历字符串,辅助我们去构造 一个 二叉树。 不要抱有太大的异或,你就跟着我的思路一步步来。稳得很! 因为我们不知道这个字符串具体输入什么样的数据。所以重点就在于判断! 判断 我们拿到的字符串数据。
?
代码 如下
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 void main(String[] args){
Scanner sc = new Scanner(System.in);
while(sc.hasNextLine()){
String str = sc.nextLine();
TreeNode root = creationTree(str);
inorderTraversal(root);
}
sc.close();
}
public static int i = 0;
public static TreeNode creationTree(String str){
TreeNode root = null;
char ch = str.charAt(i);
if( ch != '#'){
root = new TreeNode(ch);
i++;
root.left = creationTree(str);
root.right = creationTree(str);
}else{
i++;
}
return root;
}
public static void inorderTraversal(TreeNode root){
if(root == null){
return;
}
inorderTraversal(root.left);
System.out.print(root.val + " ");
inorderTraversal(root.right);
}
}
?;
?
解题思维
这里我们需要 了解/实现 一下 层序遍历的代码。 思维: 跟前面判断 完全二叉树的方法一致:用队列实现。出队的时候,随便打印一下,不同的是 如果左右子树为空树,就不用入队了。【没有值可以打印】
代码如下【不是题目的最终答案 - 过渡】
?
代码 如下 - 这是真货
因为题目很gou!要求的 返回值,很那啥。 把 每一层的节点数据,先存入一个 线性表 中。 然后,根据层次顺序,存入 另一个线性表中。【你可以理解为 是 一个 二维数组】 但是,有一个问题:我们前面写的 层序遍历,是没有分层的。 只要出队的元素不为null,我就打印一下。 来下面的代码,看看我是怎么处理的
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> result = new ArrayList<>();
if(root == null){
return result;
}
Queue<TreeNode> q = new LinkedList<>();
q.offer(root);
while(!q.isEmpty()){
int size = q.size();
List<Integer> list = new ArrayList<>();
while(size > 0){
TreeNode tmp = q.poll();
list.add(tmp.val);
size--;
if(tmp.left != null){
q.offer(tmp.left);
}
if(tmp.right != null){
q.offer(tmp.right);
}
}
result.add(list);
}
return result;
}
}
?
代码 解析
?;
?;
题目解析
给我们一棵二叉树,和 这棵二叉树中的两个节点,要求我们找到 这两个节点的 公共祖先【父亲节点,注意父亲节点也可以是 二个节点中的一个。也就是说另一个节点是孩子结点】
?;
解题思维
?;
思维一:假设题目给定的是一棵二叉搜索树
?;
代码如下 - 【二叉搜索树得出的某些结论,同样适用于本题】
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(root == null){
return root;
}
if(root == q || root == p){
return root == q ? q : p;
}
TreeNode leftNode = lowestCommonAncestor(root.left,p,q);
TreeNode rightNode = lowestCommonAncestor(root.right,p,q);
if(leftNode != null && rightNode != null){
return root;
}
return leftNode == null ? rightNode : leftNode;
}
}
?;
代码附图
如果根结点不为公共节点,就去 左右子树中 去找。 如果 根结点 就是 p 和 q 的一员,直接返回根结点。 需要注意的是:有可能 p 和 q 都在 左子树中;或者,都在右子树中。 且,可能存在 p 是 q 的 祖先节点的情况,或者, q 是 p 的 祖先节点情况。;
?
思维二 - 假设 这个二叉树使用的是孩子双亲表示法来来表示
?
代码如下
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(root == null){
return root;
}
Stack<TreeNode> stackq = new Stack<>();
Stack<TreeNode> stackp = new Stack<>();
getPath(root,p,stackp);
getPath(root,q,stackq);
int sizep = stackp.size();
int sizeq = stackq.size();
int difference = sizep - sizeq;
if(difference < 0 ){
difference = sizeq - sizep;
while(difference>0){
stackq.pop();
difference--;
}
while(!stackq.isEmpty() && !stackp.isEmpty() ){
TreeNode nodeq = stackq.pop();
TreeNode nodep = stackp.pop();
if(nodeq == nodep){
return nodep;
}
}
}else{
while(difference>0){
stackp.pop();
difference--;
}
while(!stackq.isEmpty() && !stackp.isEmpty() ){
TreeNode nodeq = stackq.pop();
TreeNode nodep = stackp.pop();
if(nodeq == nodep){
return nodep;
}
}
}
return null;
}
public boolean getPath(TreeNode root,TreeNode node,Stack<TreeNode> stack){
if(root == null || node == null){
return false;
}
stack.push(root);
if(root == node){
return true;
}
boolean flag =getPath(root.left,node,stack);
if(flag){
return true;
}
flag =getPath(root.right,node,stack);
if(flag){
return true;
}
stack.pop();
return false;
}
}
?
?
题目解析
将一颗搜索二叉树,进行有序排序【升序】。按照升序的顺序,创建一个链表【双向】。
?
解题思维
首先通过上面那题思维一,我们得知 搜索二叉树,如果对其进行中序遍历,其结果就是有序的。 最后一个问题: 将其结果转换成一个双向链表 【难点】
?
代码如下
public class Solution {
public TreeNode Convert(TreeNode pRootOfTree) {
if(pRootOfTree == null){
return null;
}
inorderTraversal(pRootOfTree);
TreeNode head = pRootOfTree;
while(head.left != null){
head = head.left;
}
return head;
}
public TreeNode prev ;
public void inorderTraversal(TreeNode root){
if(root == null){
return;
}
inorderTraversal(root.left);
root.left = prev;
if(prev != null){
prev.right = root;
}
prev = root;
inorderTraversal(root.right);
}
}
?
附图
?
这题的意思,就不用我讲了。跟前面的那道选择题一样【根据 两种遍历方法(前中 或 中后)确定 一棵唯一的二叉树】,不同的是 选择要求返回 另外一种遍历的结果,而这题要我们返回 这棵二叉树的根结点。相同的是 根据两种遍历方式 确定 / 构造 唯一 的 二叉树。
?
解题思维
根据上面的附图,从而引申出我的解题逻辑。
?
代码如下
class Solution {
public TreeNode buildTree(int[] preorder, int[] inorder) {
if(preorder == null || inorder == null){
return null;
}
return creationTree(preorder,inorder,0,inorder.length - 1);
}
public int rootIndex;
public TreeNode creationTree(int[] preorder,int[] inorder,int begin,int end){
if(begin > end){
return null;
}
TreeNode root = new TreeNode(preorder[rootIndex]);
int inorderIndex = findIndex(inorder,begin,end,root.val);
if(inorderIndex == -1){
return null;
}
rootIndex++;
root.left = creationTree(preorder,inorder,begin,inorderIndex-1);
root.right = creationTree(preorder,inorder,inorderIndex+1,end);
return root;
}
private int findIndex(int[] inorder,int begin, int end,int key){
for(int i = begin;i <= end;i++){
if(key == inorder[i]){
return i;
}
}
return -1;
}
}
?
这个就很简单了,就是上面那题改一些地方就行了。 preorder 换成 postorder,rootIndex 要赋值 【postorder.length - 1】 因为 后序遍历: 左 》 右 》根,也就是说:倒数第一个元素 是 根结点,倒数第二个 是 右子树的根结点。【也就是说,先创建 根结点,其次是 右子树,最后左子树】
?
代码如下
class Solution {
public int rootIndex;
public TreeNode buildTree(int[] inorder,int[] postorder) {
if(postorder == null || inorder == null){
return null;
}
rootIndex = postorder.length - 1 ;
return creationTree(postorder,inorder,0,inorder.length - 1);
}
public TreeNode creationTree(int[] postorder,int[] inorder,int begin,int end){
if(begin > end){
return null;
}
TreeNode root = new TreeNode(postorder[rootIndex]);
int inorderIndex = findIndex(inorder,begin,end,root.val);
if(inorderIndex == -1){
return null;
}
rootIndex--;
root.right = creationTree(postorder,inorder,inorderIndex+1,end);
root.left = creationTree(postorder,inorder,begin,inorderIndex-1);
return root;
}
private int findIndex(int[] inorder,int begin, int end,int key){
for(int i = begin;i <= end;i++){
if(key == inorder[i]){
return i;
}
}
return -1;
}
}
?
?
题目解析
题目大概的意思:将一个二叉树转换成一个由括号和整数组成的字符串。
?
代码如下
class Solution {
public String tree2str(TreeNode root) {
if(root == null){
return "()";
}
StringBuilder sb = new StringBuilder();
preOrderChange(root,sb);
return sb.toString();
}
public void preOrderChange( TreeNode root, StringBuilder sb ){
if(root == null){
return;
}
sb.append(root.val);
if(root.left != null){
sb.append("(");
preOrderChange(root.left,sb);
sb.append(")");
}else{
if(root.right == null){
return ;
}else{
sb.append("()");
}
}
if( root.right == null){
return;
}else{
sb.append("(");
preOrderChange(root.right,sb);
sb.append(")");
}
}
}
?
拓展题
?
?
解题思维 —— 借助栈
?
代码如下
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> list = new ArrayList();
Stack<TreeNode> stack = new Stack<>();
TreeNode cur = root;
while(cur != null || !stack.isEmpty()){
while(cur!= null){
list.add(cur.val);
stack.push(cur);
cur = cur.left;
}
TreeNode tmp = stack.pop();
cur = tmp.right;
}
return list;
}
}
?
有了前序之见。那么,中序将不存在问题。无非就是 list.add 语句换个位置。
?
代码如下
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> list = new ArrayList<>();
Stack<TreeNode> stack = new Stack<>();
TreeNode cur = root;
while(cur != null || !stack.isEmpty() ){
while(cur!=null){
stack.push(cur);
cur = cur.left;
}
TreeNode tmp = stack.pop();
list.add(tmp.val);
cur = tmp.right;
}
return list;
}
}
?
?
代码如下
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> list = new ArrayList<>();
Stack<TreeNode> stack = new Stack<>();
TreeNode cur = root;
TreeNode prev = null;
while(cur != null || !stack.isEmpty()){
while(cur!=null){
stack.push(cur);
cur = cur.left;
}
TreeNode tmp = stack.peek();
if(tmp.right == null || prev == tmp.right){
stack.pop();
list.add(tmp.val);
prev = tmp;
}else{
cur = tmp.right;
}
}
return list;
}
}
?
附图
?
本文结束
|