08-二叉树

  • 树形结构

在前面章节中介绍到的数据结构,都为线性结构,比如链表,数组,队列等,都属于线性结构,类似于通过一根线串在一起

1569326231986.png

树形结构示意图

二叉树
多叉树

现实生活中的树,如果将现实生活中的树倒过来,就类似于树形结构

  • 生活中经常用到的树形结构

1.公司的组织架构

2.平时的文件夹组织

总结:1.使用树形结构可以大大提高效率

​ 2.树形结构是算法面试的重点

  • 树(Tree)的基本概念

  • 节点,根节点,父节点,子节点,兄弟节点
  • 一棵树可以没有任何节点,称为空树
  • 一棵树可以只有一个节点,也就是只有根节点
  • 子树,左子树,右子树
  • 节点的(degree):子树的个数
  • 树的:所有节点度中的最大值
  • 叶子节点(leaf):度为0的节点
  • 非叶子节点:度不为0的节点
  • 层数(level):根节点在第一层,根节点的子节点在第二层,以此类推(有些书籍也从第0层开始计算)
  • 节点的深度(depth):从根节点到当前节点的唯一路径上的节点总数
  • 节点的高度(height):当从前节点到最远叶子节点的路径上的节点总数
  • 树的深度:所有节点深度中的最大值
  • 树的高度:所有节点高度中的最大值(一般来讲,树的深度与树的高度相等)
多叉树
  • 有序树
  • 树中任意节点的子节点之间有顺序关系(如上面树中,节点2,3,4,5,6是完全按照顺序排列的,如果调换位置,就变成了另外一棵树)
  • 无序树
  • 树种的任意节点的子节点之间没有顺序关系,也称为自由树
  • 森林
  • 由m(m ≥ 0)棵互不相交的树组成的集合
  • 二叉树(Binary Tree)

二叉树的特点

  • 每个节点的度最大为2(最多拥有2棵子树)
  • 左子树和右子树是有顺序的
  • 即使某个节点只有一颗子树,也要区分左右子树

二叉树有以下几种形态

1.空树

2.只有一个节点

3.只有左子树

4.只有右子树

5.右左右子树

🤔二叉树是有序树还是无序树?

  • 二叉树的性质

  • 非空二叉树的第i层,最多有2^(i - 1)个字节点(i ≥ 1)
  • 在高度为h的二叉树上,最多有2^h - 1个节点(h ≥ 1)
  • 对于任何一颗非空的二叉树,如果叶子节点个数为n0,度为2的节点个数为n2,则有:n0 = n2 +1
    • 假设度为1的节点个数为n1,那么二叉树的节点总数为 n = n0 + n1 +n2
    • 二叉树的边数T= n1 + 2 * n2 = n - 1 = n0 + n1 + n2 - 1
  • 真二叉树(Proper Binary Tree)

真二叉树:所有节点的度,要么为0,要么为2

下图不是真二叉树

  • 满二叉树(Full Binary Tree)

满二叉树:所有节点的度都要么为0,要么为2,并且所有的叶子节点都在最后一层

在同样高度的二叉树中,满二叉树的叶子节点数量最多,总节点数量最多

满二叉树一定是真二叉树,真二叉树不一定是满二叉树

假设满二叉树的高度为h(h ≥ 1),那么

第i层的节点数量:2^(i - 1)

叶子节点的数量:2(h - 1)

总节点数量n: n = 2^h - 1 = 2^0 + 2^1 +2^2 + …… + 2^(h - 1)

  • 完全二叉树(Complete Binary Tree)

完全二叉树:叶子节点只会出现在最后2层,并且最后1层的叶子节点都靠左对齐

完全二叉树的记忆方法:所有节点从上往下,从左往右一次排布,就位完全二叉树,如下图

如果所有节点都排满了,就叫做满二叉树

完全二叉树从根节点倒数第二层是一个满二叉树

满二叉树一定是一棵完全二叉树,完全二叉树不一定是满二叉树

  • 完全二叉树的性质

度为1的节点只有左子树

度为1的节点,要么是1个,要么是0个

同样节点数量的二叉树,完全二叉树的高度最小

假设完全二叉树的高度为h(h ≥ 1),那么

至少有2^(h - 1)个节点(2^0 + 2^1 + …… + 2^(h - 2) +1)

最多有2^ - 1个节点(满二叉树)

如果此时,总节点数量为n,那么有结论 2^(h -1) ≤ n < 2^h

对 2^(h -1) ≤ n < 2^h取对数,则有h - 1 ≤ log2n < h,可以得出,h与n之间的关系为 h = log2n (向下取整) +1,即 h = floor(log2n) +1

一棵有n个节点的完全二叉树(n>0)[下图],从上到下,从左到右对节点从1开始进行编号,对任意第i个节点

  • 如果i = 1,它是根节点
  • 如果i > 1 ,它的父节点编号为floor(i / 2)
  • 如果2i ≤ n ,它的左子节点编号为2i
  • 如果2i > n ,它没有左子节点
  • 如果2i + 1 ≤ n ,它的右子节点编号为2i + 1
  • 如果2i + 1> n,它没有右子节点

一棵有n个节点的完全二叉树(n>0)[下图],从上到下,从左到右对节点从0开始进行编号,对任意第i个节点

  • 如果i = 0,它是根节点
  • 如果i > 0 ,它的父节点编号为floor((i - 1) / 2)
  • 如果2i + 1 ≤ n - 1 ,它的左子节点编号为2i + 1
  • 如果2i + 1> n - 1 ,它没有左子节点
  • 如果2i + 2 ≤ n - 1,它的右子节点编号为2i + 2
  • 如果2i + 1> n,它没有右子节点
  • 下面的二叉树不是完全二叉树
  • 面试题

如果一棵完全二叉树有768个节点,求叶子节点的个数

解题思路:

  1. 假设叶子节点个数为n0,度为1的节点个数为n1,度为2的节点个数为n2
  2. 总结点个数 n = n0 +n1 + n2 ,而且由前面的公式知道,n0 = n2 + 1

因此有 n = 2n0 + n1 - 1

由于题目告知二叉树为完全二叉树,那么我们知道度为1的节点数量,为1或者为0,所以

  • 当n1为1时,n = 2n0,n必然是偶数,所以叶子节点是数量n0 = n / 2,非叶子节点个数 n1 +n2 = n / 2
  • 当n1位0时,n = 2n0 - 1,n必然位奇数,所以叶子节点的数量n0 = (n +1) / 2,非叶子节点的数量 n1 + n2 = (n - 1) / 2

所以最终,叶子节点的数量n0 = floor((n +1) / 2) = ceilling(n / 2)

非叶子节点的数量n1 + n2 = floor(n / 2) = ceilling((n - 1) / 2)

因此最终结果为384

  • 国外资料的说法

Full Binary Tree: 完满二叉树

  • 所有非叶子节点的度都为2
  • 也就是我们所说的"真二叉树“

Perfect Binary Tree:完美二叉树

  • 所有非叶子节点的度都为2,且所有的叶子节点都在最后一层
  • 也就是我们所说的"满二叉树“

Complete Binary Tree:完全二叉树

  • 和我们说的一样

以下内容,建议阅读二叉搜索树部分后再来继续阅读

  • 二叉树的遍历

遍历是数据结构中的常见操作

  • 把所有元素都遍历一遍

在前面我们介绍的线性数据结构,它的遍历方法比较简单,一般是

  • 正序遍历
  • 逆序遍历

二叉树遍历中,根据节点访问的顺序不同,常见的遍历方式有4种,分别为

  • 前序遍历(Preorder Traversal)
  • 中序遍历(Inorder Traversal)
  • 后序遍历(Postorder Traversal)
  • 层序遍历(Level Order Traversal)
  • 前序遍历(Preorder Traversal)

例如现有如下的二叉树

访问顺序

  1. 优先访问根节点,然后再前序遍历左子树,前序遍历右子树[访问顺序:下图]
  • 前序遍历的代码实现

结合前面二叉搜索树的代码,我们可以在里面新增一个preorderTraversal(Node<E> node)的方法

通过递归的方式实现代码

public void  preorderTraversal(Node<E> node) {
    if (node == null) {
        return;
    }
    System.out.println(node.element);
    preorderTraversal(node.left);
    preorderTraversal(node.right);
}

另外,读者可以尝试使用其他的方式进行实现。如迭代

  • 中序遍历(Inorder Traversal)

访问顺序

  1. 先中序遍历左子树,然后访问根节点,最后中序遍历右子树

最终的访问逻辑

由于我们当前的二叉树是一棵二叉搜索树,因此我们可以看到,如果我们使用中序遍历的方法进行遍历的话,最红遍历的结果为1,2,3,4,5,6,7,8,9,10,11,12。它们的顺序是从小到大的顺序进行遍历的

如果我们的访问顺序是:

先中序遍历右子树,然后访问根节点,最后中序遍历左子树

我们不难发现,最终的访问结果为12,11,10,9,8,7,6,5,4,3,2,1。它们的顺序是中大到小的顺序进行遍历的

因此:二叉搜索树的中序遍历结果是升序或者降序的

  • 中序遍历的代码实现

我们新增一个inorderTraversal(Node<E> node)的方法

通过递归实现中序遍历的代码

public void inorderTraversal(Node<E> node) {
    if (node == null) return;
    inorderTraversal(node.left);
    System.out.println(node.element);
    inorderTraversal(node.right);
}

同样的,读者可以使用其他方式来实现

  • 后序遍历(Postorder Traversal)

访问顺序

  1. 先后续遍历左子树,然后后续遍历右子树,最后访问根节点

最终的访问逻辑

  • 后续遍历的代码实现

我们新增一个postorderTraversal(Node<E> node)的方法

public void postorderTraversal(Node<E> node) {
    if (node == null) return;
    inorderTraversal(node.left);
    inorderTraversal(node.right);
    System.out.println(node.element);
}
  • 层序遍历(Level Order Traversal)

一层一层的往下进行访问

访问顺序

  1. 从上到下,从左到右依次访问每个节点

也就意味着,我们最终的访问顺序为

实现思路:使用队列

  1. 将根节点入队
  2. 循环执行下面的操作,直到队列为空
  • 将队列头节点A出队,进行访问
  • 将A的左子节点入队
  • 将A的右子节点入队
  • 后续遍历的代码实现

我们新增一个levelorderTraversal(Node<E> node)的方法

层序遍历的实现代码

public void levelorderTraversal() {
    if (root == null) return;
    Queue<Node<E>> queue = new LinkedList<>();
    queue.offer(root);
    while (!queue.isEmpty()) {
        Node<E> node = queue.poll();
        System.out.println(node.element);
        if (node.left != null) {
            queue.offer(node.left);
        }
        if (node.right != null) {
            queue.offer(node.right);
        }
    }
}

🤔如果允许外界遍历二叉树的元素,你会如何设计接口?

因此我们可以设计一个Visitor接口,定义一个方法,让调用者自己定义元素被访问后,需要做的操作

因此4中遍历方法,改造完成后的代码是这样的

public void preOrder(Visitor<E> visitor) {}
private void preOrder(Node<E> node , Visitor<E> visitor) {
    if (node == null || visitor == null) return;
    visitor.visit(node.element);
    preOrder(node.left,visitor);
    preOrder(node.right,visitor);
}

public void inOrder(Visitor<E> visitor) {}
private void inOrder(Node<E> node , Visitor<E> visitor) {
    if (node == null || visitor == null) return;
    preOrder(node.left,visitor);
    visitor.visit(node.element);
    preOrder(node.right,visitor);
}

public void postOrder(Visitor<E> visitor) {}
private void postOrder(Node<E> node ,Visitor<E> visitor) {
    if (node == null || visitor == null) return;
    preOrder(node.left,visitor);
    visitor.visit(node.element);
    preOrder(node.right,visitor);
}

public void levelOrder(Visitor<E> visitor) {
    if (root == null || visitor == null) return;
    Queue<Node<E>> queue = new LinkedList<>();
    queue.offer(root);
    while (!queue.isEmpty()) {
        Node<E> node = queue.poll();
        visitor.visit(node.element);
        if (node.left != null) {
            queue.offer(node.left);
        }
        if (node.right != null) {
            queue.offer(node.right);
        }
    }
}
  • 遍历的应用

前序遍历

树状结构展示(注意左右子树的顺序)

练习-利用前序遍历树状打印二叉树

中序遍历

二叉搜索树的中序遍历按照升序或者降序处理节点

后序遍历

适用于一些先子后父的操作

层序遍历

计算二叉树的高度

判断一棵树是否为完全二叉树

练习1:计算二叉树的高度

建议使用递归和迭代的方式实现。

递归实现方式

public int height(Node<E> node) {
    if (node == null) return 0;
    return 1 + Math.max(height(node.left),height(node.right));
}

使用迭代的方式实现

public int height() {
        if (root == null) return 0;
        int height = 0;//树的高度
        int levelSize = 1;//存储着每一层元素的数量
        Queue<Node<E>> queue = new LinkedList<>();
        queue.offer(root);
        while (!queue.isEmpty()) {
            Node<E> node = queue.poll();
            levelSize--;
            if (node.left != null) {
                queue.offer(node.left);
            }
            if (node.right != null) {
                queue.offer(node.right);
            }
            if (levelSize == 0) { //即将访问下一层
                levelSize = queue.size();
                height++;
            }
        }
        return height(root);
    }

练习2:判断一棵树是否为完全二叉树

解题思路

  1. 如果树为空,返回false
  2. 如果树不为空,开始层序遍历二叉树(使用队列)
    • 如果node.left != null && node.right != null ,将node.left, node.right按顺序入队
    • 如果node.left == null && node.right != null,返回false
    • 如果node.left != null && node.right == null 或者node.left == null && node.right == null
      • 那么后序遍历的节点应该都为叶子节点,才是完全二叉树
      • 否则返回false

解题源码

public boolean isComplete() {
    if (root == null) return false;
    Queue<Node<E>> queue = new LinkedList<>();
    queue.offer(root);
    boolean leaf = false;
    while (!queue.isEmpty()) {
        Node<E> node = queue.poll();
        if (leaf && !node.isLeaf()) return false;
        if (node.hasTwoChildren()) {
            queue.offer(node.left);
            queue.offer(node.right);
        } else if (node.left == null && node.right != null) {
            return false;
        } else {
            //意味着后遍历的节点,都必须是叶子节点
            leaf = true;
        }
    }
    return true;
}

练习3:翻转二叉树 题目来自[Leetcode]

将所有节点的左右子树都交换

由于使用了多种解题方法,因此解题源码在这里

  • 根据遍历结果重构二叉树

即:根据遍历的结果,推导出该结果是哪种二叉树

通过以下结果可以保证重构出唯一的一棵二叉树

  • 前序遍历结果 + 中序遍历结果
  • 后序遍历结果 + 中序遍历结果

为什么根据前序遍历和中序遍历的结果,就能推导出一棵二叉树的结果

假设下图为前序遍历的结果示意图,其中红色为根节点

其中left(左子树)和right(右子树)结果,同样也是采用前序遍历

下图为中序遍历的结果示意图,其中红色为根节点

其中left(左子树)和right(右子树)结果,同样也是采用中序遍历

通过以上两个结果,我们可以知道

  1. 根据前序遍历的结果,我们可以知道结果中的第一个节点为根节点
  2. 找到根节点后,根据中序遍历的结果,可以找到左子树和右子树的边界,根节点左边的为左子树,根节点右边的为右子树,也就知道了哪些元素属于左子树,哪些元素属于右子树
  3. 通过中序遍历的结果,已经知道了哪些元素属于左子树,哪些元素属于右子树,因此,我们在前序遍历中,就知道了哪些元素为左子树,哪些元素属于右子树
  4. 已经知道了哪些节点属于哪个子树,因此前序遍历的左子树中的第一个元素,为左子树的根节点
  5. 通过前序遍历中找到的左子树根节点,可以在中序遍历的左子树中,找到左子树的左子树和右子树,因为中序遍历的特点是通过根节点,将左右区分开来
  6. 通过这种拆分,又可以拆分为一个规模更小的遍历结果,以此类推,谁是谁的左节点,谁是谁的右节点,谁是谁的父节点,都能拆分出来

因此通过后序遍历的结果 + 中序遍历的结果,也是同理

通过这个结论,可以尝试以下练习

通过前序遍历结果 + 中序遍历结果重构二叉树

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

利用前面的结论,我们知道

  1. 当前二叉树的根节点为 4


  2. 通过中序遍历的结果知道,该二叉树的左子树为 1 2 3 ,右子树为 5 6

  3. 在通过前序遍历,就可以知道,左子树的根节点为 2,右子树的根节点为 6
  4. 由于已经知道了2为左子树的根节点,那么通过中序遍历的结果,2位1 3 的父节点,1为2的左子树,3为2的右子树
  5. 前面知道6位右子树的根节点,那么通过中序遍历的结果,6为 5的父节点,5在6的左边,因此5为6的左子树

🤔根据前序遍历的结果 + 后序遍历的结果,能重构出唯一的一棵二叉树吗?

  • 如果是一个真二叉树,它的结果是唯一的
  • 否则结果不唯一

因为有以下的情况

如果两个的遍历结果如上图所示,并且其中一个子树为空的话,就没法知道该结果为左子树的结果还是右子树的结果

如果二叉树的左右节点都不为空,遍历结果如下图

我们可以通过该结果得到以下信息

  1. 首先我们可以马上确认根节点,前序遍历的结果,最前面的节点为根节点,根节点的下一个节点为左子树的根节点
  2. 通过前序遍历中找到的左子树根节点,可以在后序遍历的左子树结果中找到,并且在后序遍历结果中,左子树根节点在左子树结果中的最后一位
  3. 在后序遍历中,通过左子树根节点的位置,就可以知道左子树和右子树的范围,然后再通过上面的结论,就可以重构出二叉树
  • 前驱节点(predecessor)

前驱节点:中序遍历时的前一个节点

  • 如果是二叉搜索树,前驱节点就是前一个比它小的节点

例如有下列的二叉树

它的中序遍历结果为

节点8的前驱节点为7

因此有以下情况:

情况1

如果左子树不为空 node.left != null

例如上图二叉树中的6 13 8均符合这种情况

前驱节点predecessor = node.left.right.right.right...

终止条件为:right 为null

情况2:

node.left == null && node.parent != null

例如上图二叉树中的7 11 9 1 均符合这种情况

前驱节点predecessor = node.parent.parent.parent....

终止条件为:node的parent在右子树当中

情况3:

node.left == null && node.parent == null

这种情况的假设上图二叉树的左子树不存在,即满足以上条件

在这种情况下,是没有前驱节点的

因此查找前驱节点的实现代码是这样的

private Node<E> predecessor(Node<E> node) {
    if (node == null) return null;
    Node<E> p = node.left;
    if (node.left != null) {
        //从左子树中找前驱
        while (p.right != null) {
            p = p.right;
        }
        return p;
    }
    //从父节点,祖父节点中寻找前驱节点
    while (node.parent != null && node == node.parent.left) {
        node = node.parent;
    }
    //node.parent == null
    //node == node.parent.right
    return node.parent;
}
  • 后继节点(successor)

后继节点:中序遍历是的后一个节点

如果是二叉搜索树,后继节点就是后一个比它大的节点

因此,如果有以下的一棵二叉树

通过中序遍历后的结果为

同样的,也有以下几种情况

情况1:

node.right != null

例如上图二叉树中的1 8 4均符合这种情况

successor = node.right.left.left.left....

终止条件:left == null

情况2:

node.right == null && node.parent != null

例如上图二叉树中的7 6 3 11均符合这种情况

successor = node.parent.parent.parent....

终止条件:node在parent的左子树中

情况3:

node.right = null && node.parent == null

那就没有前驱节点

例如:没有右子树的根节点

因此查找后继节点的代码是这样的

private Node<E> successor(Node<E> node) {
    if (node == null) return null;
    Node<E> p = node.right;
    if (p != null) {
        //从右子树中找前驱
        while (p.left != null) {
            p = p.left;
        }
        return p;
    }
    //从父节点,祖父节点中寻找前驱节点
    while (node.parent != null && node == node.parent.right) {
        node = node.parent;
    }
    //node.parent == null
    //node == node.parent.right
    return node.parent;
}

GitHub地址
本节完!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 151,511评论 1 330
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 64,495评论 1 273
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 101,595评论 0 225
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 42,558评论 0 190
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 50,715评论 3 270
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 39,672评论 1 192
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,112评论 2 291
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,837评论 0 181
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,417评论 0 228
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,928评论 2 232
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,316评论 1 242
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,773评论 2 234
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,253评论 3 220
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,827评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,440评论 0 180
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 34,523评论 2 249
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 34,583评论 2 249

推荐阅读更多精彩内容

  • 树的基本概念 节点,根节点,父节点,子节点,兄弟节点都是属于树的范涛; 一棵树可以没有任何节点,称为空树; 一棵树...
    coder_feng阅读 982评论 0 0
  • 1. 树的概念 一个树由节点组成,这些节点包含根节点,父节点,子节点,兄弟节点;没有任何一个节点的树称为空树;如果...
    HChase阅读 5,818评论 0 34
  • 树(Tree)的基本概念 节点、根结点、父节点、子节点、兄弟节点 一棵树可以没有任何节点,称为空树 一棵树可以只有...
    Peter杰阅读 757评论 0 2
  • 介绍二叉树之前先说下树状结构,不同于线性结构只有前后两个方向,树状结构可以有多个方向。 树的基本概念 节点、根节点...
    YY_Lee阅读 656评论 0 0
  • 编程中我们会遇到多少挫折?表放弃,沙漠尽头必是绿洲。 学习二叉树的意义 由于二叉树的知识更倾向于理论,所以我们在实...
    神经骚栋阅读 6,169评论 5 57