自考02331数据结构重点总结
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
自考02331数据结构重点总结(最终修订)
第一章概论
1.瑞士计算机科学家沃思提出:算法+数据结构=程序。
算法是对数据运算的描述,而数据结构包括逻辑结构和存储结构。
由此可见,程序设计的实质是针对实际问题选择一种好的数据结构和设计一个好的算法,而好的算法在很大程度上取决于描述实际问题的数据结构。
2.数据是信息的载体。
数据元素是数据的基本单位。
一个数据元素可以由若干个数据项组成,数据项是具有独立含义的最小标识单位。
数据对象是具有相同性质的数据元素的集合。
3.数据结构指的是数据元素之间的相互关系,即数据的组织形式。
数据结构一般包括以下三方面内容:数据的逻辑结构、数据的存储结构、数据的运算
①数据的逻辑结构是从逻辑关系上描述数据,与数据元素的存储结构无关,是独立于计算机的。
数据的逻辑结构分类:线性结构和非线性结构。
线性表是一个典型的线性结构。
栈、队列、串等都是线性结构。
数组、广义表、树和图等数据结构都是非线性结构。
②数据元素及其关系在计算机内的存储方式,称为数据的存储结构(物理结构)。
数据的存储结构是逻辑结构用计算机语言的实现,它依赖于计算机语言。
③数据的运算。
最常用的检索、插入、删除、更新、排序等。
4.数据的四种基本存储方法:顺序存储、链接存储、索引存储、散列存储
(1)顺序存储:通常借助程序设计语言的数组描述。
(2)链接存储:通常借助于程序语言的指针来描述。
(3)索引存储:索引表由若干索引项组成。
关键字是能唯一标识一个元素的一个或多个数据项的组合。
(4)散列存储:该方法的基本思想是:根据元素的关键字直接计算出该元素的存储地址。
5.算法必须满足5个准则:输入,0个或多个数据作为输入;输出,产生一个或多个输出;有穷性,算法执行有限步后结束;确定性,每一条指令的含义都明确;可行性,算法是可行的。
算法与程序的区别:程序必须依赖于计算机程序语言,而一个算法可用自然语言、计算机程序语言、数学语言或约定的符号语言来描述。
目前常用的描述算法语言有两类:类Pascal和类C。
6.评价算法的优劣:算法的"正确性"是首先要考虑的。
此外,主要考虑如下三点:
①执行算法所耗费的时间,即时间复杂性;
②执行算法所耗费的存储空间,主要是辅助空间,即空间复杂性;
③算法应易于理解、易于编程,易于调试等,即可读性和可操作性。
以上几点最主要的是时间复杂性,时间复杂度常用渐进时间复杂度表示。
7.算法求解问题的输入量称为问题的规模,用一个正整数n表示。
8.常见的时间复杂度按数量级递增排列依次为:常数阶0(1)、对数阶0(log2n)、线性阶0(n)、线性对数阶0(nlog2n)、平方阶0(n2)立方阶0(n3)、…、k次方阶0(n k)、指数阶0(2n)和阶乘阶0(n!)。
9.一个算法的空间复杂度S(n)定义为该算法所耗费的存储空间,它是问题规模n的函数,它包括存储算法本身所占的存储空间、算法的输入输出数据所占的存储空间和算法在运行过程中临时占用的存储空间。
第二章线性表
1.数据的运算是定义在逻辑结构上的,而运算的具体实现是在存储结构上进行的。
2.只要确定了线性表存储的起始位置,线性表中任意一个元素都可随机存取,所以顺序表是一种随机存取结构。
3.常见的线性表的基本运算:
(1)置空表InitList(L)构造一个空的线性表L。
(2)求表长ListLength(L)求线性表L中的结点个数,即求表长。
(3)GetNode(L,i)取线性表L中的第i个元素。
(4)LocateNode(L,x)在L中查找第一个值为x 的元素,并返回该元素在L中的位置。
若L中没有元素的值为x ,则返回0值。
(5)InsertList(L,i,x)在线性表L的第i个元素之前插入一个值为x 的新元素,表L的长度加1。
(6)DeleteList(L,i)删除线性表L的第i个元素,删除后表L的长度减1。
4.顺序存储方法:把线性表的数据元素按逻辑次序依次存放在一组地址连续的存储单元里的方法。
顺序表(Sequential List):用顺序存储方法存储的线性表称为顺序表。
顺序表是一种随机存取结构,顺序表的特点是逻辑上相邻的结点其物理位置亦相邻。
顺序表中结点a i的存储地址: LOC(a i)= LOC(a1)+(i-1)*c 1≤i≤n,
5.顺序表上实现的基本运算:
(1)插入:该算法的平均时间复杂度是O(n),即在顺序表上进行插入运算,平均要移动一半结点(n/2)。
在第i个位置插入一个结点的移动次数为n-i+1
(2)删除:顺序表上做删除运算,平均要移动表中约一半的结点(n-1)/2,平均时间复杂度也是O(n)。
删除第i个结点移动次数为n-i
6.采用链式存储结构可以避免频繁移动大量元素。
一个单链表可由头指针唯一确定,因此单链表可以用头指针的名字来命名。
①生成结点变量的标准函数 p=( ListNode *)malloc(sizeof(ListNode));立单链表:
(1)头插法建表:算法: p=(ListNode *)malloc(sizeof(ListNode));①链表上的查找:(带头结点)
(1)按结点序号查找:序号为0的是头结点。
算法:p=head;j=0;入运算:插入运算是将值为x的新结点插入到表的第i个结点的位置上,即插入到a i-1与a i之间。
s=(ListNode *)malloc(sizeof(ListNode)); ② s->data=x;③ s->next=p->next;④ p->next=s;⑤
算法的时间主要耗费在查找结点上,故时间复杂度亦为O(n)。
10.删除运算
r=p->next;②指向被删除的前一个结点。
链表上实现的插入和删除运算,无须移动结点,仅需修改指针。
11.单循环链表—在单链表中,将终端结点的指针域NULL改为指向表头结点或开始结点即可。
判断空链表的条件是head==head->next;
12.仅设尾指针的单循环链表:用尾指针rear表示的单循环链表对开始结点a1和终端结点a n查找时间都是O(1)。
而表的操作常常是在表的首尾位置上进行,因此,实用中多采用尾指针表示单循环链表。
判断空链表的条件为rear==rear->next;
13.循环链表:循环链表的特点是无须增加存储量,仅对表的链接方式稍作改变,即可使得表处理更加方便灵活。
若在尾指针表示的单循环链表上实现,则只需修改指针,无须遍历,其执行时间是O(1)。
具体算法:
LinkList Connect(LinkList A,LinkList B) {向链表:双(向)链表中有两条方向不同的链,即每个结点中除next域存放后继结点地址外,还增加一个指向其直接前趋的指针域prior。
①双链表由头指针head惟一确定的。
②带头结点的双链表的某些运算变得方便。
③将头结点和尾结点链接起来,为双(向)循环链表。
15.双向链表的前插和删除本结点操作
①双链表的前插操作
void DInsertBefore(DListNode *p,DataType x){称为后进先出(Last In First Out)的线性表,简称为LIFO表。
栈是运算受限的线性表,顺序栈也是用数组表示的。
进栈操作:进栈时,需要将S->top加1,①S->top==StackSize-1表示栈满
②"上溢"现象--当栈满时,再做进栈运算产生空间溢出的现象。
退栈操作:退栈时,需将S->top减1,①S->top<0表示空栈
②"下溢"现象--当栈空时,做退栈运算产生的溢出现象。
下溢是正常现象,常用作程序控制转移的条件。
空栈时栈顶指针不能是0,只能是-1。
两个栈共享同一存储空间: 当程序中同时使用两个栈时,可以将两个栈的栈底分别设在顺序存储空间的两端,让两个栈顶各自向中间延伸。
当一个栈中的元素较多而栈使用的空间超过共享空间的一半时,只要另一个栈的元素不多,那么前者就可以占用后者的部分存储空间。
当Top1=Top2-1时,栈满
2.为了克服顺序存储分配固定空间所产生的溢出和空间浪费问题。
可采用链式存储结构来存储栈。
链栈是没有附加头结点的运算受限的单链表。
栈顶指针就是链表的头指针。
链栈中的结点是动态分配的,所以可以不考虑上溢,无须定义StackFull运算
栈的一个重要应用是实现递归,直接调用自己或间接调用自己的函数。
3. 队列(Queue)是只允许在一端进行插入,而在另一端进行删除的运算受限的线性表。
允许删除的一端称为队头(Front),允许插入的一端称为队尾(Rear),当队列中没有元素时称为空队列,队列亦称作先进先出(First In First Out)的线性表,简称为FIFO表。
队列的顺序存储结构称为顺序队列,顺序队列实际上是一个受限的线性表。
顺序队列的基本操作
①入队时:将新元素插入rear所指的位置,然后将rear加1。
②出队时:删去front所指的元素,然后将front加1并返回被删元素。
当头尾指针相等时,队列为空。
在非空队列里,头指针始终指向队头元素,而队尾指针始终指向队尾元素的下一位置。
而栈顶指针指向栈顶元素。
4.循环队列:为充分利用数组空间,克服上溢,可将数组空间想象为一个环状空间,并称这种环状数组表示的队列为循环队列。
循环队列中进行出队、入队操作时,头尾指针仍要加1,朝前移动。
只不过当头尾指针指向向量上界(QueueSize-1)时,其加1操作的结果是指向向量的下界0。
这种循环意义下的加1操作可以描述为:
①方法一:
if(i+1==QueueSize) i=0;环队列的基本运算:
①置队空: Q->front=Q->rear=0;
②判队空: return Q->rear==Q->front;
③判队满: return (Q->rear+1)%QueueSize==Q->front;
④入队Q->data[Q->rear]=x; 计算机来处理计算算术表达式问题,首先要解决的问题是如何将人们习惯书写的中缀表达式转换成后缀表达式。
第四章多维数组和广义表
1.数组的顺序存储方式:一般采用顺序存储方法表示数组。
(1)行优先顺序 a11,a12,…,a1n,a21,a22,…,a2n,……,a m1,a m2,…,a mn
(2)列优先顺序 a11,a21,…,a m1,a12,a22,…,a m2,……,a1n,a2n,…,a mn
Pascal和C语言是按行优先顺序存储的,而Fortran语言是按列优先顺序存储的。
按行优先顺序存储的二维数组A mn地址计算公式
LOC(a ij)=LOC(a11)+[(i-1)×n+j-1]×d (注:此公式下界为1,如下界为0,则公式变为[i×n+j])
按列优先顺序存储的二维数组A mn地址计算公式
LOC(a ij)=LOC(a11)+[(j-1)×m+i-1]×d(注:此公式下界为1,如下界为0,则公式变为[j×m+i])
按行优先顺序存储的三维数组A mnp地址计算公式
LOC(a ijk)=LOC(a111)+[(i-1)×n×p+(j-1)×p+k-1]×d(注:此公式下界为1,如下界为0,则公式变为[i×n×p+j×p+k])
2.为了节省存储空间,可以对矩阵中有许多值相同或值为零的元素的矩阵,采用压缩存储。
特殊矩阵是指相同值的元素或零元素在矩阵中的分布有一定的规律。
常见的有对称矩阵、三角矩阵。
(1)对称矩阵在一个n阶方阵A中,若元素满足下述性质: a ij=a ji0≤i,j≤n-1称为n阶对称矩阵,它的元素是关于主对角线对称的,所以只需要存储矩阵上三角或下三角元素即可,让两个对称的元素共享一个存储空间。
矩阵元素a ij和数组元素sa【k】之间的关系是
k=i×(i+1)/2+j i≥j0≤k<n(n+1)/2-1 k=j×(j+1)/2+i i<j 0≤k<n(n+1)/2-1
对称矩阵的地址计算公式:LOC(a ij)=LOC(sa[0])+[I×(I+1)/2+J]×d,其中I=max(i,j),J=min(i,j)
(2)三角矩阵:以主对角线划分,三角矩阵有上三角和下三角两种。
上三角矩阵是指它的下三角(不包括主角线)
中的元素均为常数c或零;下三角矩阵的主对角线上方均为常数c或零。
一般情况,三角矩阵的常数c均为零。
三角矩阵的压缩存储:三角矩阵中的重复元素c可共享一个存储空间,其余的元素正好有n×(n+1)/2个,因此,三角矩阵可压缩存储在一维数组sa[n(n+1)/2+1]中,其中c存放在数组的最后一个元素中。
①上三角矩阵中a ij和sa[k]之间的对应关系
k=i×(2n-i+1)/2+j-i 当i≤j k=n×(n+1)/2 当i>j
②下三角矩阵中a ij和sa[k]之间的对应关系
k=i×(i+1)/2+j 当i≥j k=n×(n+1)/2 当i<j
三角矩阵的压缩存储结构是随机存取结构。
3.稀疏矩阵:设矩阵A mn中有s个非零元素,若s远远小于矩阵元素的总数,则称A为稀疏矩阵。
为了节省存储单元,可用压缩存储方法只存储非零元素。
由于非零元素的分布一般是没有规律的,因此在存储非零元素的同时,还必须存储非零元素所在的行、列位置,所以可用三元组(i,j,a ij)来确定非零元素。
稀疏矩阵进行压缩存储通常有两类方法:顺序存储(三元组表)和链式存储(十字链表)。
稀疏矩阵的压缩存储会失去随机存取功能。
4.广义表是线性表的推广,又称列表。
广义表是n(n≥0)个元素a1,a2,…,a i,…,a n的有限序列。
其中a i或者是原子或者是一个广义表。
①广义表通常用圆括号括起来,用逗号分隔其中的元素。
②为了区分原子和广义表,书写时用大写字母表示广义表,用小写字母表示原子。
③若广义表Ls非空(n≥1),则a l是LS的表头,其余元素组成的表(a1,a2,…,a n)称为Ls的表尾。
④广义表具有递归和共享的性质
广义表的深度:一个表展开后所含括号的层数称为广义表的深度。
19.广义表是一种多层次的线性结构,实际上这就是一种树形结构。
广义表的两个特殊的基本运算:取表头head(Ls)和取表尾tail(Ls).任何一个非空广义表的表头可以是原子,也可以是子表,而其表尾必定是子表。
head=(a,b)=a,tail(a,b)=(b) 对非空表A和(y),也可继续分解。
注意:广义表()和(())不同。
前者是长度为0的空表,对其不能做求表头和表尾的运算;而后者是长度为l的由空表作元素的广义表,可以分解得到的表头和表尾均是空表()。
广义表是一种有层次的非线性结构,通常采用链式存储结构,每个元素用一个结点表示,结点由3个域构成,其中一个是tag标志位,用来区分结点是原子还是子表,当tag为零时结点是子表,第二个域为slink,用以存放子表的地址;当tag为1时结点是原子,第二个域为data,用以存放元素值。
第五章树和二叉树
1.树的表示法:最常用的是树形图表示法;还有3种嵌套集合、凹形、广义表。
树结构的基本术语
(1)结点的度(Degree)
树中的一个结点拥有的子树数称为该结点的度(Degree)。
一棵树的度是指该树中结点的最大度数。
度为零的结点称为叶子(Leaf)或终端结点。
度不为零的结点称分支结点或非终端结点。
除根结点之外的分支结点统称为内部结点。
根结点又称为开始结点。
(2)①路径(path)若树中存在一个结点序列k1,k2,…,k i,使得k i是k i+1的双亲(1≤i<j),则称该结点序列是从k l到k j的一条路径(Path)。
一个结点的祖先是从根结点到该结点路径上所经过的所有结点,而一个结点的子孙则是以该结点为根的子树中的所有结点。
结点的层数(Level)从根起算:根的层数为1,其余结点的层数等于其双亲结点的层数加1。
双亲在同一层的结点互为堂兄弟。
树中结点的最大层数称为树的高度(Height)或深度(Depth)。
若将树中每个结点的各子树看成是从左到右有次序的(即不能互换),则称该树为有序树(OrderedTree);否则称为无序树(UnoderedTree)。
若不特别指明,一般讨论的树都是有序树。
森林(Forest)是m(m≥0)棵互不相交的树的集合。
树和森林的概念相近。
删去一棵树的根,就得到一个森林;反之,加上一个结点作树根,森林就变为一棵树。
3.二叉树与度数为2的有序树不同:在有序树中,虽然一个结点的孩子之间是有左右次序的,但是若该结点只有一个孩子,就无须区分其左右次序。
而在二叉树中,即使是一个孩子也有左右之分。
二叉树的性质:
性质1二叉树第i层上的结点数目最多为2i-1(i≥1)。
例如5层的二叉树,第5层上的结点数目最多为24=16
性质2深度为k的二叉树至多有2k-1个结点(k≥1)。
例如深度为5的二叉树,至多有25-1=31个结点
性质3在任意-棵二叉树中,若终端结点的个数为n0,度为2的结点数为n2,则n o=n2+1。
例如一棵深度为4的二叉树(a),其终端结点数n0为8,度为2的结点树为7,则8=7+1,n o=n2+1成立
(b)其终端结点数n0为6,度为2的结点树为5,则6=5+1,n o=n2+1成立
满二叉树:一棵深度为k且有2k-1个结点的二又树称为满二叉树。
满二叉树的特点:
(1)每一层上的结点数都达到最大值。
即对给定的高度,它是具有最多结点数的二叉树。
(2)满二叉树中不存在度数为1的结点,每个分支结点均有两棵高度相同的子树,且树叶都在最下一层上。
完全二叉树:若一棵深度为k的二叉树,其前k-1层是一棵满二叉树,而最下面一层上的结点都集中在该层最左边的若干位置上,则此二叉树称为完全二叉树。
特点:
(1)满二叉树是完全二叉树,完全二叉树不一定是满二叉树。
(2)在满二叉树的最下一层上,从最右边开始连续删去若干结点后得到的二叉树仍然是一棵完全二叉树。
(3)在完全二叉树中,若某个结点没有左孩子,则它一定没有右孩子,即该结点必是叶结点。
性质4 具有n个结点的完全二叉树的深度为。
⌊logn⌋+1 或⌈log(n+1)⌉
例,具有100个结点的完全二叉树的深度为:⌊lg100⌋+1=7,26=64 27=128所以⌊lg100⌋=6 ,⌈lg(100+1)⌉=7 4.完全二叉树的编号特点:完全二叉树中除最下面一层外,各层都充满了结点。
每一层的结点个数恰好是上一层结
点个数的2倍。
从一个结点的编号就可推得其双亲,左、右孩子等结点的编号。
编号从0开始
①若i=0,则q i为根结点,无双亲;否则,q i的双亲编号为⌊(i-1)/2⌋。
②若2i+1<n,则q i的左孩子的编号是2i+1;否则,q i无左孩子,即q i必定是叶子。
③若2i+2<n,则q i的右孩子的编号是2i+2;否则,q i无右孩子。
对于完全二叉树而言,使用顺序存储结构既简单又节省存储空间。
但对于一般二叉树来说,采用顺序存储时,为了使用结点在数组中的相对位置来表示结点之间的逻辑关系,就必须增加一些虚结点使其成为完全二叉树的形式。
5.链式存储结构: 二叉树的每个结点最多有两个孩子。
用链接方式存储二叉树时,每个结点除了存储结点本身的数据外,还应设置两个指针域lchild和rchild,分别指向该结点的左孩子和右孩子。
结点的结构为:
二叉链表是一种常用的二叉树存储结构。
建立二叉链表方法:a、按广义表方法,靠近左括号的结点是在左子树上,而逗号右边结点是在右子树上。
b、按完全二叉树的层次顺序建立结点。
具有n个结点的二叉链表中,共有2n个指针域。
其中有n-1个用来指示结点的左、右孩子,其余的n+1个为空。
二叉树遍历算法中的递归终止条件是二叉树为空。
中序遍历的递归算法定义:(1)遍历左子树; (2)访问根结点; (3)遍历右子树。
先序遍历的递归算法定义:(1)访问根结点; (2)遍历左子树; (3)遍历右子树。
后序遍历得递归算法定义:(1)遍历左子树; (2)遍历右子树; (3)访问根结点。
递归工作栈中包括两项:一项是递归调用的语句编号,另一项则是指向根结点的指针。
已知一棵二叉树的前序和中序遍历序列或中序和后序遍历序列,可唯一确定一棵二叉树。
具体方法如下:首先根据前序或后序遍历序列确定二叉树的各子树的的根,然后根据中序遍历序列确定各子树根的左右子树。
6.线索二叉树:n个结点的二叉链表必定存在n+1个空指针域,可以利用这些空指针域,存放指向结点在某种遍历次序下的前趋和后继结点的指针,这种指向前驱和后继结点的指针称为"线索",这种加上线索的二叉链表称为线索链表,相应的二叉树称为线索二叉树(Threaded BinaryTree)。
线索链表的结点结构:
其中:ltag和rtag是增加的两个标志域,用来区分结点的左、右指针域是指向其左、右孩子的指针,还是指向其前趋或后继的线索。
图中的实线表示指针,虚线表示线索。
线索二叉树中,一个结点是叶结点的充要条件为:左、右标志均是1。
7.二叉树的线索化:把对一棵二叉线索链表结构中所有结点的空指针域按照某种遍历次序加线索的过程称为线索化。
和中序遍历算法一样,递归过程中对每结点仅做一次访问。
因此对于n个结点的二叉树,线索化的算法时间复杂度为O(n)。
8.树、森林到二叉树的转换:树中每个结点最多只有一个最左边的孩子(长子)和一个右邻的兄弟。
将树转换成二叉树:①在所有兄弟结点之间加一道连线;②对每个结点,除了保留与其长子的连线外,去掉该结点与其它孩子的连线。
由于树根没有兄弟,故树转化为二叉树后,二叉树的根结点的右子树必为空。
将一个森林转换为二叉树:
将森林中的每棵树转化成二叉树,然后再将二叉树的根节点看做兄弟连在一起,形成一棵二叉树
9.二叉树到树、森林的转换:
方式是:若二叉树中结点x是双亲y的左孩子,则把x的右孩子,右孩子的右孩子,…,都与y用连线连起来,最后去掉所有双亲到右孩子的连线。
10.树的存储结构:
1.双亲表示法:双亲链表表示法利用树中每个结点的双亲唯一性,在存储结点信息的同时,为每个结点附设一个指向其双亲的指针parent,惟一地表示任何-棵树。
(1)双亲链表表示法的实现
分析:E和F所在结点的双亲域是1,它们的双亲结点在向量中的位置是1,即B是它们的双亲。
注意:①根无双亲,其parent域为-1。
②双亲链表表示法中指针parent向上链接,适合求指定结点的双亲或祖先(包括根);求指定结点的孩子或其它后代时,可能要遍历整个数组。
2.孩子链表法:孩子链表表示法是为树中每个结点设置一个孩子链表,并将这些结点及相应的孩子链表的头指针存放在一个向量中。
注意:①孩子结点的数据域仅存放了它们在向量空间的序号。
②与双亲链表表示法相反,孩子链表表示便于实现涉及孩子及其子孙的运算,但不便于实现与双亲有关的运算。
③将双亲链表表示法和孩子链表表示法结合起来,可形成双亲孩子链表表示法。
3.孩子兄弟表示法:在存储结点信息的同时,附加两个分别指向该结点最左孩子和右邻兄弟的指针域,即可得树的孩子兄弟链表表示。
注意:
这种存储结构的最大优点是:它和二叉树的二叉链表表示完全一样。
可利用二叉树的算法来实现对树的操作。
11.树的遍历:
一般都只给出两种次序遍历树的方法:前序(先根次序)遍历和后序(后根次序)遍历。
①前序遍历一棵树等价于前序遍历该树对应的二叉树
②后序遍历一棵树等价于中序遍历该树对应的二叉树。
对下面(a)图中所示的森林进行前序遍历和后序遍历,则得到该森林的前序序列和后序序列分别为ABCDEFIGJH和BDCAIFJGHE。
而(b)图所示二叉树的前序序列和中序序列也分别为ABCDEFIGJH和BDCAIFJGHE。
①前序遍历森林等同于前序遍历该森林对应的二叉树
②后序遍历森林等同于中序遍历该森林对应的二叉树
12.从根结点到某结点之间的路径长度与该结点上权的乘积称为该结点的带权路径长度,树种所有叶子结点的带权路径长度之和称为树的带权路径长度。
带权路径长度WPL最小的二叉树称为哈夫曼树或最优二叉树。
哈夫曼树不一定是二叉树。
哈夫曼树又称为最优树,是一类带权路径长度最短的树。
完全二叉树就是这种路径长度最短的二叉树。
①只有叶结点上的权值均相同时,完全二叉树一定是最优二叉树,否则完全二叉树不一定是最优二叉树。
②最优二叉树中,权越大的叶子离根越近。
③最优二叉树的形态不唯一,WPL最小。
13.哈夫曼算法:
基本思想是:(1)根据给定的n个权值w l,w2,…,w n构成n棵二叉树的森林F={T1,T2,…,T n},其中每棵二叉树T i中都只有一个权值为w i的根结点,其左右子树均空。
(2)在森林F中选出两棵根结点权值最小的树(当这样的树不止两棵树时,可以从中任选两棵),将这两棵树合并成一棵新树,为了保证新树仍是二叉树,需要增加一个新结点作为新树的根,并将所选的两棵树的根分别作为新根的左右孩子(谁左,谁右无关紧要),将这两个孩子的权值之和作为新树根的权值。
(3)对新的森林F重复(2),直到森林F中只剩下一棵树为止。
这棵树便是哈夫曼树。
注意:①初始森林中的n棵二叉树,每棵树有一个孤立的结点,它们既是根,又是叶子
② n个叶子的哈夫曼树要经过n-1次合并,产生n-1个新结点。
最终求得的哈夫曼树中共有2n-1个结点。
③哈夫曼树是严格的二叉树,没有度数为1的分支结点。
14.哈夫曼编码:
数据压缩过程称为编码,反之,解压缩的过程称为解码。
设计一种长短不等的编码,则必须保证任一字符的编码都不是另一个字符编码的前缀,这种编码称为前缀编码。
可以利用二叉树来设计二进制的前缀编码,其左分支表示字符0,右分支表示字符1,则以根结点到叶结点路径上的分支字符组成的串作为该叶节点的字符编码。
因此设计电文总长最短的二进制前缀编码,就是以n种字符出现的频率作为权构造一棵哈夫曼树,由哈夫曼树求得的编码就是哈夫曼编码。
译码过程是从树根结点出发,逐个读入电文中的二进制码。
第六章图
1.图G由两个集合构成,顶点集合和边集合,也可以图G只有顶点而没有边。
用尖括号表示图的有向边<v i,v j>,有向边又称为弧,起点称为弧尾,终点称为弧头。
无向图的顶点对用圆括号表示(v i,v j)。
在无向图中,称v i和v j相邻接,在有向图中称顶点v i邻接到v j,顶点v j邻接于v i
在无向图中,n的取值范围是0-n(n-1)/2,将具有n(n-1)/2条边的无向图称为无向完全图。
在有向图中,n的取值范围是0-n(n-1),将具有n(n-1)条边的有向图称为有向完全图。
无向图中,顶点的度定义为以该顶点为一个端点的边的数目,有向图的度等于出度和入度之和。
在无向图中,任意两顶点都有路径,则称两顶点连通。
若图G中的任意两个顶点都连通,称G为连通图。
无向图的极大连通子图称为连通分量,显然,任何连通图的连通分量只有一个,即其自身,而非连通的无向图有多个连通分量。
在有向图中,图G中任意两顶点连通,称为强连通图,极大连通子图称为强连通分量。
若在一个图的每条边上标上某种数值,该数值称为该边的权。
边上带权的图称为带权图,带权的连通图称为网络。
2.图的存储结构:邻接矩阵和邻接表表示法。
图的顶点编号从0开始。
邻接矩阵表示法:<v i,v j>或(v i,v j)是边,则值为1,不是边则值为0。
无向图的邻接矩阵是按主对角线对称的。
若G是带权图,只要把1换成相应边上的权值即可,0的位置上可以不动或将其换成无穷大表示。
无向图的邻接矩阵表示法可以仅存储主对角线以下的元素,时间复杂度为O(n2)邻接表表示法:邻接表是图的一种链式存储结构。
将无向图的邻接表称为边表,将有向图的邻接表称为出边表,将邻接表的表头向量称为顶点表。
若无向图有n个顶点和e条边,则它的邻接表共有n个头结点和2e个表结点。
建立邻接表的时间复杂度是O(n+e)。
图的邻接表表示不是唯一的,这是因为在每个顶点的邻接表中,各边结点的链接次序可以是任意的,其具体链接次序与边的输入次序和生成算法有关。
3.图的遍历:遍历图的算法是求解图的连通性、图的拓扑排序等算法的基础。
图的遍历常用的是深度优先搜索遍历和广度优先搜索遍历两种方法。
深度优先搜索遍历(DFS)类似于前序(先根)遍历。
按访问顶点的先后次序得到的顶点序列称为图的深度优先遍历序。