树的相关理解

树的相关问题

二叉树

对于一个二叉树数据结构,其核心操作为遍历

  • 前序遍历的递归及非递归写法。非递归用栈实现,进入压栈,先将左子树全部压栈,然后返回出栈,对于出栈的元素先打印,然后压入右子树,直至栈为空。
  • 中序遍历的递归及非递归写法。非递归用栈实现,进入压栈,先将左子树全部压栈,压栈时打印,然后返回出栈,对于出栈的元素压入右子树,直至栈为空。
  • 后序遍历的递归及非递归写法。非递归用栈实现,进入压栈,先将左子树全部压栈,然后返回出栈,对于右子树为空或者右子树已经被访问过的元素,可以打印,否则将元素及右子树压栈,并标记为前一个元素。这样做的根本目的是需要在第三次经过元素时才能将其打印,如果不重新压栈,那么后面将找不到这个元素;同时,需要确定元素的右子树已经访问过了,因此需要用指针记录前一个访问元素的位置。
  • 层序遍历的非递归写法。非递归用队列实现,先压入根节点,然后在每个循环中弹出一个,压入两个(子树),直至队列为空。

需要注意的是,对于二叉树的前中后三种遍历方式,他们所经过的路径是一样的,每个节点都相当于经过三次,只不过不同的遍历方式,选择打印这个节点的时机不同,而在程序中,我们可见的就是进入该节点时的压栈,以及从左子树返回时的出栈,从右子树返回时如果不多进行压栈操作,则就看不到该节点的第三次状态,这也是后序非递归遍历需要增加push操作的原因

各种遍历方法是树操作的基础,其他的操作可以看作是某种遍历的变形,只是在访问子树的过程中增加了一些其他操作由于在进行其他树操作时候往往是从根节点开始,因此相当于使用了二叉树的前序遍历。

二叉树目前的一些常见题型及相应的操作(主要来自于剑指offer及浙江大学MOOC数据结构视频教程):

  • 重建二叉树。根据二叉树的前序跟中序或者二叉树的前序跟后序序列(一定要有中序)来构造二叉树,需要根据前序或者后序序列找出根节点,然后根据跟节点在中序中的位置划分出左子树及右子树,再返回前序或者后序序列中递归构造。

  • 二叉树的下一个节点。给出一个二叉树的节点,如何找出中序序列中的下一个节点。这个题考的就是对于中序遍历的理解。需要根据不同的节点情况,划分不同的节点类型,构造相应的寻找下一个节点的方式。

  • 树的子结构。首先需要找到递归起始的位置,然后就是对两个树同步遍历的过程。

  • 从上到下打印二叉树。考察的就是层序遍历。

  • 二叉树中和为某一值的路径。思想跟回溯法相同,但由于路径不止一条且需要正向打印,因此需要构造一个可见的,压弹栈顺序和递归时系统栈相同的栈,用来保存并打印路径。

二叉树的题目,大部分从本质上来讲考的还是递归,因此需要对递归有一个到位的理解。对于树的递归,递归思路都是从根节点到左子树及右子树的递归,其变化的主要在于递归的边界条件、返回值以及返回值与上一层调用栈参数间的逻辑关系,理清了这些,树的题目应该不是很难。

二叉搜索树

二叉搜索树是一种排序树,左子树都小于根节点,右子树都大于根节点。涉及到的主要操作为查找插入删除

  • 查找:查找的过程类似于二分查找,可以用递归或者非递归形式来写;
  • 插入:先查找到插入位置,再插入。由于插入的操作会产生新的节点,相当于递归的返回值对于上层参数(结构)产生了影响,因此不能使用尾递归形式,需要用赋值语句接住返回值。
  • 删除操作属于二叉搜索树中相对复杂的问题。对于删除操作,需要分几种情况考虑:
    • 删除节点没有子节点或仅有一个子节点,此时将子节点接到父节点上即可
    • 删除节点有两个子节点。那么需要从左子树中找到最大节点或者右子树中找到最小节点。之后完成两个操作,用这个节点的值替代需要删除的节点,然后从左子树或者右子树中找到这个节点,将其删除。

搜索树的题型,碰到的还比较少:

  • 是否是同一颗二叉搜索树。给两个序列,判断其形成的二叉搜索树是否相同,视频中给出了三种思路:
    • 建两棵树:将两个序列都建立二叉搜索树,然后对两个树进行遍历比较
    • 建一棵树:将基准序列建树,其他序列跟基准序列进行比较。这里比较重要的是比较操作,比较操作的核心就是依次查找序列中的元素在树中的路径,查找过程中遇到该元素之前经过的节点都应该访问过,否则就不是同一颗二叉搜索树
    • 不建树:根据第一个节点的值,将二叉树序列进行划分,要求两棵树划分出来的左子树和右子树序列完全相同,否则就不是同样的二叉搜索树。
  • 判断一个序列是不是某颗二叉搜索树的后序序列。核心思想就是先找到根节点,然后在后序序列中找到比根节点大的序列和小的序列,这两个序列不能出现交叉,否则就不能形成二叉搜索树。然后进行递归判断。

平衡二叉树(AVL树)

首先平衡二叉树是一种二叉搜索树,因此在进行操作的时候一定要确保其仍然保留二叉搜索数的有序性质。

平衡二叉树的操作主要还是插入序列后为了使之重新平衡做出的旋转操作。其根据插入位置与平衡被破坏点的相对关系分为以下四种情况:

  • RR插入与RR旋转:插入位置在被破坏点的右子树的右子树上,对于这种情况,需要把右子树的节点给拎上来;
  • LL插入与LL旋转:插入位置在被破坏点的左子树的左子树上,对于这种情况,需要把左子树的节点给拎上来;
  • LR插入与LR旋转:插入位置在被破坏点的左子树的右子树上,对于这种情况,涉及到被破坏点、左子树节点及插入点三个节点,根据这三个节点的大小关系(左子树最小,插入点中间,被破坏点最大),需要将需要把插入点作为中间节点,左子树节点和被破坏点分别位于左右两边,并将左子树节点和被破坏点的子树按照大小关系接好;
  • RL插入与RL旋转:插入位置在被破坏点的右子树的左子树上,对于这种情况,涉及到被破坏点、右子树节点及插入点三个节点,根据这三个节点的大小关系(右子树最大,插入点中间,被破坏点最小),需要将需要把插入点作为中间节点,右子树节点和被破坏点分别位于右左两边,并将右子树节点和被破坏点的子树按照大小关系接好;

猜你喜欢

转载自blog.csdn.net/LHaoRax/article/details/89315319