放松了一周,也一周都没写题了,接下来,我做题的方式可能会更趋向于成体系的学习,今天就先从树的章节开始学起,主要参考的学习资料还是博客,知乎等网上较为系统的教材,涉及到的参考内容我都会进行标注,并以力扣题目作为导向来学习.
一.树
1. 什么是树
根据维基百科的定义:在计算机科学中,树(英语:tree)是一种抽象数据类型(ADT)或是实现这种抽象数据类型的数据结构,用来模拟具有树状结构性质的数据集合。它是由n(n>0)个有限节点组成一个具有层次关系的集合。把它叫做“树”是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。它具有以下的特点:
- 每个节点都只有有限个子节点或无子节点;
- 没有父节点的节点称为根节点;
- 每一个非根节点有且只有一个父节点;
- 除了根节点外,每个子节点可以分为多个不相交的子树;
- 树里面没有环路(cycle)
2. 树的一些重要的术语
- 节点的度:一个节点含有的子树的个数称为该节点的度;
- 树的度:一棵树中,最大的节点度称为树的度;
- 叶节点或终端节点:度为零的节点;
- 非终端节点或分支节点:度不为零的节点;
- 父亲节点或父节点:若一个节点含有子节点,则这个节点称为其子节点的父节点;
- 孩子节点或子节点:一个节点含有的子树的根节点称为该节点的子节点;
- 兄弟节点:具有相同父节点的节点互称为兄弟节点;
- 节点的层次:从根开始定义起,根为第1层,根的子节点为第2层,以此类推;
- 深度:对于任意节点n,n的深度为从根到n的唯一路径长,根的深度为0;
- 高度:对于任意节点n,n的高度为从n到一片树叶的最长路径长,所有树叶的高度为0; 堂兄弟节点:父节点在同一层的节点互为堂兄弟;
- 节点的祖先:从根到该节点所经分支上的所有节点; 子孙:以某节点为根的子树中任一节点都称为该节点的子孙。
- 森林:由m(m>=0)棵互不相交的树的集合称为森林;
3. 树的存储结构
主要有以下三种存储方式:
(1)双亲表示法 : 该存储方式,对每个结点进行定义的时候,会包含自己的数据域(即该结点的数据)和双亲的指针域(即双亲的地址).
(2)孩子表示法 : 该存储方式,将每个结点的孩子结点排列起来,以但链表作为存储结点,使得n个结点有n个孩子链表,并将n个链表的头指针组成一个线性表,采用顺序存储结构,存放在一个一维数组中.因此线性表有一个数据域(存储当前结点的数据)和一个指针域(存储第一个孩子的地址),对于n个孩子链表中,每个孩子结点有一个child域(存储某个结点在表头数组中的下标)和一个next域(存储指向某结点的下一个孩子结点的指针).
(3)孩子兄弟表示法:该存储方式,每个结点包含三个域,分别为数据域(存储当前结点的数据),firstchild域(第一个孩子结点的存储地址),rightsib域(存储当前结点的右兄弟结点的存储地址).
以上是对树存储方法的介绍,但是整体而言,树主要有以下几种分类:
接下来,按照以上的介绍,分别进行汇总学习.
二 . 二叉树
1.二叉树的定义
二叉树是另一种树形结构,其特点是每个结点至多只有两棵子树( 即二叉树中不存在度大于2的结点),并且二叉树的子树有左右之分,其次序不能任意颠倒。与树相似,二叉树也以递归的形式定义。二叉树是n (n≥0) 个结点的有限集合:或者为空二叉树,即n=0。或者由一个根结点和两个互不相交的被称为根的左子树和右子树组成。左子树和右子树又分别是一棵二叉树。
二叉树是有序树,若将其左、右子树颠倒,则成为另一棵不同的二叉树。即使树中结点只有一棵子树,也要区分它是左子树还是右子树。二叉树的5种基本形态如图所示。
2.二叉树的存储结构
(1)顺序存储结构::使用一组地址连续的存储单元依次自上而下,从左往右存储每个结点的数据,用空值表示空结点.
(2)链式存储方式:使用二叉链表进行存储,每个结点右lchild(存储左孩子的存储地址),数据域(存储当前结点的数据),rchild(存储右孩子的存储地址).
3.二叉树的遍历
不论二叉树的类型,要想去访问一棵树的每个节点,都是共通的,主要有以下四种遍历方式:
(1)先序遍历(根左右)
当二叉树非空时,先访问根结点,之后访问该跟节点的左子树,再之后是右子树,对应力扣的题目:144. 二叉树的前序遍历,树是链式存储方式进行表示的,
按照思路,选用递归的方式去做,直到递归到空结点,实现如下:
(2)中序遍历(左根右)
当二叉树非空时,先访问左结点,之后访问该节点的根结点,再之后是右结点,对应力扣的题目:94. 二叉树的中序遍历,树是链式存储方式进行表示的,
举一反三,那么只需要更改返回数组的位置即可.实现如下:
(3)后序遍历(右根左)
当二叉树非空时,先访问左结点,之后访问该节点的根结点,再之后是右结点,对应力扣的题目:145. 二叉树的后序遍历
只需要将返回数组放在最后就可以了.
(4)层序遍历
层序遍历是需要一层一层去遍历树的结点,主要的遍历过程如下图所示:
对应的力扣题目是:102. 二叉树的层序遍历
实现思路的话,使用队列的思想,先进先出,先将头结点存储在队列内,遍历队列,存储该层的所有结点的值,并将非空遍历到的结点的非空结点存储到队列内,实现结果如下:
在这里需要注意的是,python中队列的使用,主要包含以下几个重要的使用方法:
import collections
#定义
queue = collections.deque() #不指定长度
queue = collections.deque(maxlen=num) #指定长度#进队列
queue.append('a') #单个元素从尾插入
que.appendleft('A') # 单个元素从头插入
queue.extend(['D', 'E']) #可迭代对象
queue.insert(3, 'T') #指定位置插入#出队列
queue.pop() #从最右边出
queue.popleft() #从头出#删除
queue.remove('T') #删除某个元素
queue.clear() #清空整个队列#复制
queue_b = queue.copy()
和传统的先进先出规则有点不同,添加了更多灵活的操作.
4.由遍历序列构造二叉树
由遍历序列可惟一确定一个二叉树的方式有三种:(1)先序序列+中序序列 (2)后序遍历+中序遍历 (3)层序序列+中序序列
以第一种方式:先序遍历+中序遍历进行讲解:先序序列的遍历方式是根左右,中序遍历是左根右,那么先序序列的第一个就是整棵树的根,那么只需要在中序序列内找到根的位置,即可将树分为左子树和右子树,在对每个子树,使用相同的方式确认根,再分,直到遍历结束,整个树的构造就结束了.后序序列还有层次序列和这个方式是相同的,那么为什么说其余三种加上中序遍历就可以惟一确认呢?我的理解是,取决于二叉树的性质根的惟一性,只要能惟一确定根,那么就可惟一确定左子树和右子树,以此类推就可以获得,只是实现的思想不一样,根据这个实现思路,对应于力扣的105. 从前序与中序遍历序列构造二叉树,进行解答
先序遍历根左右,中序遍历左根右,先有先序序列的根在中序序列确认左子树和右子树,再在先序序列迭代去确认左子树和右子树的根,实现过程和结果如下所示:
递归调用该函数,从而实现树的构建.
未完待续…
Reference
树的分类
维基百科-树的定义
树的细节讲解
python中队列的使用细节