数据结构(C语言描述)树

1 树的定义

树是由一个集合及在该集合上定义的一种层次关系构成的
集合中的元素是树的结点,结点间的关系为父子关系。
树结点之间的父子关系建立了树的层次结构。
在这种层次结构中,有一个结点具有特殊地位,这个结点称为该树的根结点,简称为树根

1.1 树的递归定义

下面形式地给出树的递归定义。
(1)单个结点是一棵树,树根就是该结点本身。
(2)设T1,T2,.…,Tn是树,它们的根结点分别为n1,n2,……,nk。用一个新结点n作为n1,n2,…,nk的父亲,则得到一棵新树。
结点n就是新树的根
结点n1,n2,……,nk称为一组兄弟结点,它们都是结点n的儿子结点
还称T1,T2,……,Tk为结点n的子树
为方便起见,将空集合也看成树,称为空树,用“^”来表示。空树中没有结点。
总结:
在这里插入图片描述

树中的结点与表中的元素类似,可以具有任何一种类型。在用图来表示树时,常用一个圆圈表示一个结点,并在圆圈中标一个字母,或一个字符串,或一个数作为该结点的名字,以便与其他节点区别。
树的递归定义刻画了树的固有特性,即一棵树是由若干棵子树构成的

1.2 树结构的基本概念和常用术语

在这里插入图片描述
上图表示一棵树,由结点的有限集T={A,B,C,D,E,F,G,H,IJ}构成,其中A是根结点,T中其余结点,分成3个互不相交的子集T1={B,E,F,I,J},T2={C},T3={D,G,F}。T1,T2和T3是根A的3棵子树,其本身又都是一棵树。
树结构的基本概念和常用术语。
(1)一个结点的儿子结点个数称为该结点的度。一棵树的度是指该树中结点的最大度数。
(2)树中度为零的结点称为叶结点或终端结点。
(3)树中度不为零的结点称为分枝结点或非终端结点。除根结点外的分枝结点统称为内部结点。
如上图,结点A,B和E的度分别为3,2和0。其中,A为根节点,B为内部结点,E为叶结点,树的度为3。
(4)若存在树中的一个结点序列k1,k2,……,kj,使得结点ki是结点ki+1的父结点(1≤i<j),则称该结点序列是树中从结点k1到结点kj与的一条路径或道路。
称这条路径的长度为j-1,它是该路径所经过的边(即连接两个结点的线段)的数目。
例如,上图所示,结点A到结点I有一条路径ABFI,它的长度为3。
树中任结点有一条到其自身的长度为等的路径。
(5)若在树中存在一条从结点k到结点m的路径,则称结点k是结点m的祖先,也称结点m是结点k的子孙或后裔。例如,上图所示,结点F的祖先有A,B和F自己,而它的子孙包括它自己和I,J。注意,任一结点既是它自己的祖先,也是它自己的子孙
(6)将树中一个结点的非自身的祖先和子孙分别称为该结点的真祖先和真子孙。在一棵树中,树根是唯一没有真祖先的结点,叶结点是没有真子孙的结点。子树是树中某一结点及其所有真子孙组成的一棵树。(类似集合中真子集和子集的关系
(7)树中一个结点的高度是指从该结点到各叶结点的最长路径的长度。树的高度是指根结点的高度,例如,如上图的结点B,C和D的高度分别为2,0和1,而树的高度与结点A的高度相同,为3。
(8)从树根到任一结点n有唯一的一条路径,称这条路径的长度为结点n的深度或层数。根结点的深度为0,其余结点的深度为其父结点的深度加1。深度相同的结点属于同一层。例如,上图中,结点A的深度为0;结点B,C和D的深度为1;结点E,F,G,H的深度为2;结点I和J的深度为3。树的第0层只有一个根结点A,在树的第1层的结点有B,C和D,在树的第2层的结点有E,F,G和H,在树的第3层的结点有I和J。
(9)树的定义在某些结点之间确定了父子关系,这种关系又延拓为祖先和子孙关系
但是树中的许多结点之间仍然没有这种关系。
例如,兄弟结点之间就没有祖先或子孙关系。
若在树的每一组兄弟结点之间定义一个从左到右的次序,则得到一棵有序树。否则称为无序树
设结点n的所有儿子按其从左到右的次序排列为n1,n2,……,nk。则称n1是n的最左儿子,简称为左儿子,并称n2(ni)是n1(ni-1)的右邻兄弟,或简称为右兄弟(i=2,3……k)。
在这里插入图片描述

上图中的两棵树作为无序树是相同的,但作为有序树是不同的,因为结点a的两个儿子在两棵树的左右次序是不同的。
兄弟结点之间左右次序关系还可以延拓:若a和b是兄弟,并且a在b的左边,则规定a的任一子孙都在b的任一子孙的左边
(10)森林是m(m≥0)棵互不相交的树的集合。如果删去一棵树的树根,留下的子树就构成了一个森林。当删去的是一棵有序树的树根时,留下的子树也是有序的,这些树组成一个树表。在这种情况下,称这些树组成的森林为有序森林或果园
在这里插入图片描述

2 树的遍历

2.1 三种遍历方式

树的遍历是树的一种重要的运算。
遍历是指对树中所有结点的系统的访问,即依次对树中每个结点访问一次且仅访问一次
树的3种最重要的遍历方式分别称为前序遍历、中序遍历和后序遍历
以这3种方式遍历一棵树时,如果按访问结点的先后次序将结点排列起来,就分别得到树中所有结点的前序列表、中序列表和后序列表
相应的结点次序就分别称为结点的前序,中序和后序
在这里插入图片描述
3种遍历方式可定义如下。
(1)如果T是一棵空树,那么对T进行前序遍历、中序遍历和后序遍历都是空操作,得到的列表为空表。
(2)如果T是一棵单结点树,那么对T进行前序遍历、中序遍历和后序遍历都只访问这个单结点。这个结点本身就是得到的相应的列表。
在这里插入图片描述
(3)否则,设树T如上图,它以n为树根,树根的子树从左到右依次为T1,T2,……,Tk。
①对T进行前序遍历是先访问树根n,然后依次前序遍历T1,T2,……,Tk,即前序遍历T1,然后前序遍历T2,最后前序遍历Tk。
②对T进行中序遍历是先中序遍历T1,然后访问树根n,接着依次对T2,……,Tk进行中序遍历。
③对T进行后序遍历是先依次对T1,T2,……,Tk进行后序遍历,最后访问树根n。
在这里插入图片描述
例如,对上图中的树进行前序遍历、中序遍历和后序遍历得到的列表如下。
前序列表:A B E F I J C D G H
中序列表:E B I F J A C G D H
后序列表:E I J F B C G H D A
在这里插入图片描述

2.1 树的构造规则

(1)每个叶结点的标号是一个运算对象,且称这个运算对象为该叶结点所代表的表达式。例如,叶结点n4,所代表的表达式为a。
(2)每一个内部结点n的标号是一个运算符。若结点n的标号是一个二元运算符:
且n的左儿子代表的表达式为E1,其右儿子代表的表达式为E2,则n所代表的表达式为(E1):(E2)。其中的括号在不必要时可省略。
在这里插入图片描述
结点n2的标号是+,其左、右儿子所代表的表达式分别为a和b,因此n2代表(a)+(b),或简记为a+b。结点n1的标号是*,其左、右儿子n2和n3分别代表a+b和a+c,结点n1代表的表达式(a+b) * (a+c)。
上图树的前缀标号表为*+ab+ac。

3 树的表示法

3.1 父结点数组表示法(双亲表示法)

设T是一棵树,其中结点的名称分别为1,2,……,n。用一个一维数组存储每个结点的父结点。寻找一个结点的父结点只需要o(1)时间。该树为:
在这里插入图片描述

转化为数组:
在这里插入图片描述
B,C的parent的0是从A数组下标0获得的
D,E的parent的1是从B数组下标1获得的
F,G,H的parent的4是从E数组下标获得的
I,J的parent的2是从C数组下标的2获得的

3.2 儿子链表表示法

对树的每个结点建立一个儿子结点表。用链表来实现儿子结点表。
例如:树为
在这里插入图片描述
用儿子链表表示法

在这里插入图片描述
每个表头指针指向一个以数中结点为元素的链表。header[i]所指的表由结点i的所有儿子构成。

3.3 左儿子右兄弟表示法(二叉树表示法)

又称二叉链表表示法。以二叉链表作为树的存储结构。链表中结点的两个链域分别指向该结点的最左儿子和右邻兄弟。
例如:
树为
在这里插入图片描述
用二叉树表示法为:
在这里插入图片描述

4 二叉树

4.1 基本概念

二叉树T是有限个结点的集合,它或者是空集,或者用n,n1和n2分别表示T,u(1)和u(2)的结点数,子树u(1)和u(2)有时分别称为T的第一和第二子树。
二叉树的根可以有空的左子树或空的右子树,或左、右子树均为空。因此,二叉树有5种基本形态。

在这里插入图片描述

在二叉树中,每个结点至多有两个儿子,并且有左、右之分。因此任一结点的儿子有4种情况:没有儿子,只有一个左儿子,只有一个右儿子,有一个左儿子且有一个右儿子。
在二叉树中,即使是一个儿子也有左右之分。
下图是两棵不同的二叉树。
在这里插入图片描述
但是不能等同于下图的普通数。
在这里插入图片描述
若将这3棵树看成有序树,则它们就是相同的。
总结:二叉树不是树的特殊情况。

4.2 性质

(1)高度为h≥0的二叉树至少有h+1个结点。
(2)高度不超过h的二叉树至多有2^h+1 -1个结点。
(3)含有n≥1个结点的二叉树的高度至多为n-1。
(4) 含有n≥1个结点的二叉树的高度至少为[logn], 因此其高度为O(logn) 。
例如:
有3个结点的不同二叉树,一共有5种情况。
在这里插入图片描述

4.3 满二叉树

满二叉树的特点是每一层上的结点数都达到最大值,即具有最多结点的二叉树。
满二叉树中不存在读数为1的结点。
每个分支结点均有2棵高度相同的子树,且叶结点都在最下面一层上。
在这里插入图片描述

4.4 近似满二叉树

在满二叉树的最下层上,从最右结点开始连续往左删除若干个结点后得到道德二叉树是一棵近似满二叉树。
在近似满二叉树中,若某个节点没有左儿子,则它一定没有右儿子。
在这里插入图片描述

4.5 非近似满二叉树

要与近似满二叉树区分。
在这里插入图片描述
上图中F结点没有左儿子而有右儿子,因此它不是近似满二叉树,是非近似满二叉树。

5 二叉树的运算

(1)Binarylnit():创建一棵空二叉树。
(2) BinaryEmpty(T):判断一棵二叉树T是否为空。
(3) Root(T) :返回二叉树T的根结点标号。
(4) MakeTree(x,T,L,R) :以x为根结点元素,分别以L和R为左、右子树构建一棵新的二叉树T。
(5) Break Tree(T,L,R) :函数MakeTree的逆运算将二叉树T拆分为根结点元素,左子树L和右子树尺等三部分。
(6) PreOrder(visit,t) :前序遍历二叉树。
(7) InOrder(visit,t) :中序遍历二叉树。
(8) PostOrder(visit,t) :后序遍历二叉树。
(9) PreOut(T) :二叉树前序列表。
(10)InOut(T) :二叉树中序列表。
(11)PostOut(T) :二叉树后序列表。
(12)Delete(t) :删除二叉树。
(13)Height(f) :二叉树的高度。
(14)Size(t) :二叉树的结点数。

6 二叉树的实现

6.1 二叉树的顺序存储结构

二叉树的顺序存储结构方法试将二叉树的所有结点,按照一定的次序,存储到一片连续的存储单元中。因此,必须将结点排成一个适当的线性序列
在一棵具有n个结点的近似满二叉树中,从树根起,自上而下,逐层从左到右给所有结点编号,就能得到一个足以反映整个二叉树结构的线性序列。其中,每个结点的编号就作为结点的名称。
在这里插入图片描述

将数组下标作为结点名称(编号)就可将二叉树中所有结点的标号存储在一个一维数组中。上图的二叉树的顺序存储结构如下图所示。
在这里插入图片描述
近似满二叉树的顺序存储结构

近似满二叉树中,除最下层外,各层都充满了结点。每一层的结点个数恰好是上一层结点个数的二倍。所以,从一个节点的编号可以推出该结点父、左、右儿子、兄弟等结点的编号。
对i结点i有如下关系:
(1)仅当i=1时,结点i为根结点。
(2)当i≥1时,结点i的父结点为[i/2]。
(3)结点i的左儿子结点为2i。
(4)结点i的右儿子结点为2i+1。

例如:只有3个结点的右单枝树,如下图。
在这里插入图片描述
相应的顺序存储结构如下图。
在这里插入图片描述

6.2 二叉树的结点度表示

将二叉树中所有节点依其后序列表排列,并在每个结点中附加一个0~3之间的整数,以表示结点的状态。该整数为0时,表示相应的结点为一叶结点;为1时,表示相应节点只有一个左儿子;为2时,表示相应节点只有一个右儿子;为3时,表示相应结点有2个儿子。
例如:
二叉树
在这里插入图片描述
用二叉树的结点表示为
在这里插入图片描述

6.3 用指针的节点度表示

由于二叉树的每个结点最多有两个儿子,因此实现二叉树的方法是指针。
用指针实现二叉树时,对于每个结点除存储节点标号等信息外,还应设置指向结点左、右儿子结点的指针。

6.3.1 二叉树结点结构定义

编写程序:

//二叉树结点类型
typedef struct btonde *btlink;//二叉树结点指针类型
typedef btnode{
    
    //二叉树结点结构
	TreeItem element;//二叉树结点标号(元素)
	btlink left;//左子树
	btlink right;//右子树
}Btnode;

btlink NewNode()
{
    
    //建立新的树结点
	return (btlink)malloc(sizeof(Btonde));
]

分析:
其中,element存储结点标号;left和right分别是指向其左子树和右子树的指针。函数NewNode()创建一个新的树结点。

6.3.2 用指针实现二叉树结构定义

编写程序:

typedef struct binarytree *BinaryTree;//二叉树类型
typedef struct binarytree{
    
    //二叉树结构
	btlink root;//树根
}BTree;

分析:
其中,root是指向树根的指针。

6.3.3 创建一棵空树

函数BinaryInit()将root置为空指针,创建一棵空树。
编写程序:

BinaryInit() BinaryInit()
{
    
    
	BinaryTree T=(BinaryTree )malloc(sizeof *T);
	T->root=0;
	return T;
} 

例如:
二叉树
在这里插入图片描述
用指针实现
在这里插入图片描述
其中^表示空

6.4 二叉树的基本运算

6.4.1 函数Root(T)

int BinaryEmpty(BinaryTree T)
{
    
    
	return T->root ==0;
}
TreeItem Root(BinaryTree T)
{
    
    //前提树非空
	if(BinaryEmpty(T)) exit(1);
	return T->root ->element;
}

分析:
函数BinaryEmpty(T)检测T的根节点root是否为空指针。
函数Root(T)f返回T的根结点标号。

6.4.2 函数MakeTree(x,T,L,R)

在这里插入图片描述
函数MakTree(x, T, L,R)以x为根结点元素,分别以L和R为左、右子树构建一棵新的二叉树T。

6.4.3 函数BreakTree(T,L,R)

在这里插入图片描述
在这里插入图片描述
BreakTree(T,L,R)执行函数MakeTree(x,T,L,R)的逆运算,将二叉树T拆分为根节点元素element、左子树L和右子树R等3部分。

6.4.4 二叉树的遍历

递归的遍历
用PreOrder()函数、InOrder()函数、PostOrder()函数分别实现对二叉树的前序遍历、中序遍历和后序遍历。
在这里插入图片描述
这3个函数都以二叉树结点t、结点访问函数visit作为参数,对以结点t为根的子树递归地进行相应的遍历操作。

非递归的遍历
用栈模拟递归,用非递归的遍历、例如,非递归前序遍历算法如下。
在这里插入图片描述

层序遍历

6.4.5 输出二叉树的列表

函数PreOut,InOut,PostOut和LevelOut通过对二叉树的根结点调用结点元素输出函数outnode来实现对整个二叉树结点的前序列表、中序列表、后序列表和层序列表。
在这里插入图片描述

6.4.6 函数Height()

输出二叉树的高度。
在这里插入图片描述
在这里插入图片描述

7 线索二叉树

在n个结点二叉树中含有n+1个空指针,可以利用这些空指针存放指向结点在某种遍历次序下的前驱和后继结点的指针。这种附加的指针称为”线索“,加上了线索的二叉树称为线索二叉树。
为了区分一个结点的指针是指向其儿子结点的指针,还是指向其前驱或后继结点的线索,可在每个结点中增加两个线索标志
线索二叉树结点类型定义如下。
在这里插入图片描述
其中,leftThread为左线索标志,rightThread为右线索标志。
他们的含义是:
当leftThread的值为0时,left是指向左儿子结点的指针。
当leftThread的值为1时,left是指向前驱结点的左线索。
当rightThread的值为0时,right是指向右儿子结点的指针。
当rightThread的值为1时,right是指向后继结点的右线索。
例如下图为一棵中序线索二叉树
在这里插入图片描述
中序遍历
D B H E J I K A C G
其指针表示图如下。
在这里插入图片描述
增加一个头结点,其left指针指向二叉树的根结点A,其right指针指向中序遍历的最后一个结点G。
头结点的左儿子结点指针指向A结点,
A结点的左儿子结点指针指向B结点,
A结点的右儿子结点指针指向C结点,
B结点的左儿子结点指针指向D结点,
B结点的右儿子结点指针指向E结点,
E结点的左儿子结点指针指向H结点,
E结点的右儿子结点指针指向I结点,
I结点的左儿子结点指针指向J结点,
I结点的右儿子结点指针指向K结点,
C结点的左儿子节点指针指向F结点,
C结点的右儿子结点指针指向G结点。

当D结点的rightThread为1,right是指向后继B结点的右线索,
当H结点的leftThread为1,left是指向前驱结点B的左线索,
当H结点的rightThread为1,right是指向后继结点E的右线索,
当J结点的leftThread为1,left是指向前驱结点E的左线索,
当J结点的rightThread为1,right是指向后继结点I的右线索,
当K结点的leftThread为1,left是指向前驱结点I的左线索,
当K结点的rightThread为1,right是指向后继结点A的右线索,
当F结点的leftThread为1,left是指向前驱结点A的左线索,
当F结点的rightThread为1,right是指向后继结点C的右线索,
当G结点的leftThread为1,left是指向前驱结点C的左线索,
当G结点的rightThread为1,right是指向头结点的右线索。
总结:
对于找前驱和后继结点两种运算而言,线索二叉树优于非线索二叉树。但线索二叉树也有缺点:在进行结点插入和删除运算时,线索二叉树比非线索二叉树的时间开销大。其原因在于在线索二叉树中进行结点插入和删除时,除修改相应指针外,还要修改相应的线索。

8 二叉搜索树

8.1 字典

字典中元素有一个线性序,且支持涉及线性序的一些集合运算。
字典是以有序集为基础的抽象数据类型,它支持以下运算。
(1) Membar(x,S) , 成员运算。
(2) Insert(z,S) , 插入运算:将元素x插入集合S。
(3) Delete(z,S) , 删除运算:将元素x从当前集合S中去。
(4)Predecessor(z,S) , 前驱运算:返回集合S中小于x的最大元素。
(5) Successor(x,S) , 后继运算:返回集合S中大于x的最小元素。
(6) Range(x,y,S),区间查询运算:返回集合S中界于x和y之间的所有元素组成的集合。
(7) Min(S) , 最小元运算:返回当前集合S中依线性序最小的元素。
用数组实现字典的一个明显缺陷是插入和删除运算的效率较低。导致其在最坏情况下的计算时间为O(n)。

8.2 实现二叉搜索树

为了利用数组和链表二者的优点,引入二叉搜索数,并用它来实现字典。
二叉搜索数的性质:
存储与每个结点中元素x大于其左子树任一结点所存储的元素,小于其右子树任一结点中所存储的元素。
例如:
整数集合{5,7,10,12,14,15,18}
若按照中序列出二叉搜索树结点中所存储的元素,则恰好是集合中的所有元素从小到大的排列。
在这里插入图片描述
用二叉搜索数实现字典时,增加了指向当前结点的父结点的指针parent,为了便于实现算法对树结点的操作。
实现二叉搜索树的程序:
在这里插入图片描述
在这里插入图片描述
函数NewBSonde(x)产生一个存储元素x的新二叉搜素树结点。
在这里插入图片描述
对于整数集可以定义有序集元素TreeItem如下。
在这里插入图片描述
对二叉树结点进行扩充后,二叉搜索树结构bstree定义如下。
在这里插入图片描述

8.3 查询二叉搜索数的元素

二叉搜索树表示的字典T中搜索一个元素x。
在这里插入图片描述
成员查询函数BSMember(x,T)只要返回BSSearch(x,T)搜索的结果。
在这里插入图片描述

8.4 新增二叉搜索数的元素

在二叉搜索树T插入一个元素x的运算BSInsert(x,T)可实现如下。
在这里插入图片描述
在这里插入图片描述
仅当x不在集合时插入元素x。实现这一功能的算法BSInsertVisit(x,visit(u),T)如下。
在这里插入图片描述

8.5 删除二叉搜索数的元素

从二又搜索树T中删除一个元素x稍复杂。
首先必须找到存储元素x的结点。
如果这个结点是一个叶结点,只要删除这个叶结点就行了。
如果不是叶结点,就不能简单地删除,因为这样将破坏树的连通性。
如果要删除的结点p只有一个儿子结点,如下图中存储元素14的结点,用p的儿子结点代替它就行了。
在这里插入图片描述

如果p有两个儿子结点,如下图中存储元素10的结点,为了保持二义搜索树的性质,即按中序遍历树结点将从小到大排列出所有结点中的元素,可以用p的前驱结点或后继结点来代替它如在下图中删去元素10,应该先刑去元素7,并用7代替10的位置。
在这里插入图片描述

这样删去一个元素后得到的树仍是一棵二叉搜索树。
下面的算法BSDelete(x,T) 实现了上面所讨论的删除元素的过程。
在这里插入图片描述

8.6 小结

(1)用二叉搜索数实现字典时,BSMember,BSInsert,BSDelete等运算的平均时间为O(logn)。
(2)实现运算Predecesoor和Successor的算法类似BSSearch算法。
即在最坏情况下需要O(n)计算时间,在平均情况下,需要O(logn)计算时间。
(3)Range运算,最坏情况下用O(r+n)计算时间,在平均情况下,需要o(r+logn)计算时间。
(4)二叉搜索数的效率取决于它的高度。在最坏情况下,搜索过程所用的时间为O(h),平均情况下,需要O(logn)计算时间。h为二叉树搜索树的高度。

9 线段树

9.1 线段树的基本概念

线段树结构是表示线段的一个集合数据结构。
用于解决与矩形有关的计算几何问题。其主要目的是有效地组织线段集S={I1,I2,…,In},使其能够高效支持关于线段集S的几何运算。
表示线段集S的线段集是一棵平衡二叉树,它将实直线按照有序实数集x[1…N]为分割点划分一系列的初等线段。每个初等线段对应与线段树的一个叶结点。
线段树结点类型可定义如下。
在这里插入图片描述
用一个结点数组tree来存储线段树结点如下。
在这里插入图片描述
表示标准线段的线段树结构T(l,r)可以递归构造如下。
在这里插入图片描述
一般情况下,线段的结构intv定义如下。
在这里插入图片描述
下面的算法inst在线段树结点pos处插入单个线段。
在这里插入图片描述
下面的算法只是简单的count值增1。
在这里插入图片描述
在这里插入图片描述
用插入单个线段的算法inst,可以将线段集iset中的所有线段插入线段树T(L,N)中如下。
在这里插入图片描述
算法erase在线段树结点pos处删除一个线段。
在这里插入图片描述
在这里插入图片描述

9.2 基本几何问题

(1)线段穿刺问题:对于给定线段集S和一个查询点q,找出S中含查询点q的所有线段。
(2)线段集的并集问题:对于给定线段集S,计算S中所有线段的并集。
(3)线段集最大团问题:对于给定线段集S,计算s的最大团。S的团是s中交集非空的线段集。S的最大团是S的团中线段数最多的团。
(4)矩形集并集而积问题:对于给定的边平行于坐标轴的矩形集S,计算S中所有矩形的并集的面积。
(5)矩形集并集固长问题:对于给定的边平行于坐标轴的矩形集S,计算S中所有矩形的并集的周长。
(6)矩形集最大团问题:对于给定矩形集S,计算S的最大团。S的团是S中交集非空的矩形集。S的最大团是S的团中矩形数最多的团。
对于给定线段集S,建立了与其相应的线段树T,并将线段集S插入线段树T后就能高效解决上述基本几何问题。例如,对于线段穿刺问题,只要在线段树T中对查询点进行一次从根结点到叶结点的搜索即可。
在这里插入图片描述
在这里插入图片描述
对于线段集的并集问题,根据线段树中存储的count信息计算以tree[pos]为根结点的子树中线段集的并。
在这里插入图片描述
增加uni域后,结点的结构改变如下。
在这里插入图片描述
其中,域clq在线段集最大团问题中用到。

结点结构改变后,在插入线段时也要更新相应信息。
在这里插入图片描述
在这里插入图片描述
线段插入算法也需要相应更新如下。
在这里插入图片描述
线段删除算法也需要相应更新如下。
在这里插入图片描述
在这里插入图片描述
这些问题都可以在O(NlogN)时间内得到解决。

10 序列树

10.1 序列树的基本概念

序列树结构是表示序列的一个数据结构,用于解决与序列有关的计算问题。
其主要目的是有效地组织一个序列,使其能够高效支持对于此序列中的子序列运算。
序列树支持以下给定序列和子序列的基本运算。
(1) build(l,r,pos) , 初始化序列树运算。
(2) insert(k,v) , 插入运算:将序列树中x[k] 的值更改为v。
(3) add(k,v) , 增值运算:将序列树中x[k] 的值加上v。
(4) modify(l,r,v) , 子序列插入运算:将序列树中x[l…r] 的值均改为v。
(5) increase(l,r,v) , 子序列增值运算:将序列树中x[l…r] 的值均加上v。
(6) querysum(l,r) , 子序列求和运算:返回x[l…r] 中元素之和。
(7) querymin(l,r) , 子序列最小元运算:返回x[l.r] 中最小元素。
(8) querymax(l,r) ,子序列最大元运算:返回xfL.r] 中最大元素。
序列树的基本思想源于线段树。

10.2 序列树的实现

在一般情况下,序列树的结点类型可定义如下。
在这里插入图片描述
由于序列树是一棵平衡二叉树,所以可以用一个结点堆数组tree来存储序列树结点如下。
在这里插入图片描述
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
表示标准子序列的序列结构T(l,r)可以递归地结构如下。
在这里插入图片描述
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
数组元素x[l],它所对应的结点是序列树的叶结点。此时由read将序列元素读入。
在这里插入图片描述
在这里插入图片描述
其中,change根据读入值更新结点信息。
在这里插入图片描述
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
由update根据左右子树信息来更新当前结点的信息。
在这里插入图片描述
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
当N=10时,算法build构造序列树T(1,10)的例子。
在这里插入图片描述
构造了序列树后,可以用下面的算法inst在序列树中更改x[k]的值。
在这里插入图片描述
在这里插入图片描述
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
实现单点更新和增值算法如下。
在这里插入图片描述
其中,insert执行单点更改;add执行单点增值。
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
参数传递的思想是在实现算法时通过参数来传递left和right的信息。例如,在实现算法inst时,增加参数l和r来传递此信息如下。
在这里插入图片描述
参数l和r就以参数形式给出了结点pos所相应的标准子序列的左右端点。
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
标准子序列的左右端点的算法可描述如下。
在这里插入图片描述
其中,seqh即logM。
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
构造了序列树结构T(l,N)后,用下面的算法可计算出给定子序列在以结点pos为根的子树中的和、最小值与最大值。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
给定子序列的和、最小值和最大值就是以结点1为根的序列树中的和、最小值和最大值。
在这里插入图片描述
在这里插入图片描述
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
实现子序列在结点pos处插入运算的延迟更新。算法要将子序列[lft,rht]中所有元素的值都改变为c。
在这里插入图片描述
由update根据左右子树的信息更新当前结点信息。
在这里插入图片描述

在这里插入图片描述
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

实现子序列在结点pos出增值运算的延迟更新。
在这里插入图片描述
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
子序列插入运算modify和子序列增值运算increase是在序列树的根结点处实施插入和增值运算。
在这里插入图片描述
在这里插入图片描述
由于结点中存在延迟更新信息,所以在查询算法querysum,querymin和querymax访问当前结点时需要由push来完成延迟修改信息下推。

总结

(1)树的一般操作和常用表示树的数据结构。
(2)树的前序遍历、中序遍历和后序遍历。
(3)树的父结点数组表示、树的儿子链表表示法和左儿子右兄弟表示法。
(4)二叉树的概念。
(5)实现二叉树常见的方法有二叉树的顺序存储结构、二叉树的结点度表示法和用指针实现二叉树的方法。
(6)二叉树搜索树是残差有序集的高效数据结构。
(7)二叉搜索树实现关于有序集的抽象数据类型字典的方法。
(8)线段树和序列树结构是表示线段和序列的数据结构。

猜你喜欢

转载自blog.csdn.net/qq_45059457/article/details/115078633