北航面试之数据结构知识要点

来源:互联网 发布:知乎 打怪手游 编辑:程序博客网 时间:2024/04/30 17:55

数据结构复习重点归纳

一、 数据结构的章节结构及重点构成

数据结构学科的章节划分基本上为:概论,线性表,栈和队列,串,多维数组和广义表,树和二叉树,图,查找,内排,外排,文件,动态存储分配。

对于绝大多数的学校而言,“外排,文件,动态存储分配”三章基本上是不考的,在大多数高校的计算机本科教学过程中,这三章也是基本上不作讲授的。所以,大家在这三章上可以不必花费过多的精力,只要知道基本的概念即可。
按照以上我们给出的章节以及对后三章的介绍,数据结构的章节比重大致为:(考研内容分析)
概论
:内容很少,概念简单,分数大多只有几分,有的学校甚至不考。
线性表:基础章节,必考内容之一。考题多数为基本概念题,名校考题中,鲜有大型算法设计题。如果有,也是与其它章节内容相结合。
栈和队列:基础章节,容易出基本概念题,必考内容之一。而栈常与其它章节配合考查,也常与递归等概念相联系进行考查。
 :基础章节,概念较为简单。专门针对于此章的大型算法设计题很少,较常见的是根据KMP进行算法分析。
多维数组及广义表 :基础章节,基于数组的算法题也是常见的,分数比例波动较大,是出题的“可选单元”或“侯补单元”。一般如果要出题,多数不会作为大题出。数组常与“查找,排序”等章节结合来作为大题考查。
树和二叉树 :重点难点章节,各校必考章节。各校在此章出题的不同之处在于,是否在本章中出一到两道大的算法设计题。通过对多所学校的试卷分析,绝大多数学校在本章都曾有过出大型算法设计题的历史。
 :重点难点章节,名校尤爱考。如果作为重点来考,则多出现于分析与设计题型当中,可与树一章共同构成算法设计大题的题型设计。
查找 :重点难点章节,概念较多,联系较为紧密,容易混淆。出题时可以作为分析型题目给出,在基本概念型题目中也较为常见。算法设计型题中可以数组结合来考查,也可以与树一章结合来考查。
排序 :与查找一章类似,本章同属于重点难点章节,且概念更多,联系更为紧密,概念之间更容易混淆。在基本概念的考查中,尤爱考各种排序算法的优劣比较此类的题。算法设计大题中,如果作为出题,那么常与数组结合来考查。

 

 

二、 数据结构各章节重点勾划:
0章 概述

本章主要起到总领作用,为读者进行数据结构的学习进行了一些先期铺垫。大家主要注意以下几点:数据结构的基本概念,时间和空间复杂度的概念及度量方法,算法设计时的注意事项。数据结构发展历史、地位以及对这门课的理解。
第一章 线性表
作为线性结构的开篇章节,线性表一章在线性结构的学习乃至整个数据结构学科的学习中,其作用都是不可低估的。在这一章,第一次系统性地引入链式存储的概念,链式存储概念将是整个数据结构学科的重中之重,无论哪一章都涉及到了这个概念。

总体来说,线性表一章可供考查的重要考点有以下几个方面:

1.线性表的相关基本概念,如:前驱、后继、表长、空表、首元结点,头结点,头指针等概念。
2.线性表的结构特点,主要是指:除第一及最后一个元素外,每个结点都只有一个前趋和只有一个后继。
3.线性表的顺序存储方式及其在具体语言环境下的两种不同实现:表空间的静态分配和动态分配。静态链表与顺序表的相似及不同之处。
4.线性表的链式存储方式及以下几种常用链表的特点和运算:单链表、循环链表,双向链表,双向循环链表。其中,单链表的归并算法、循环链表的归并算法、双向链表及双向循环链表的插入和删除算法等都是较为常见的考查方式。
在链表的小题型中,经常考到一些诸如:判表空的题。在不同的链表中,其判表空的方式是不一样的,请大家注意。
5.线性表的顺序存储及链式存储情况下,其不同的优缺点比较,即其各自适用的场合。单链表中设置头指针、循环链表中设置尾指针而不设置头指针以及索引存储结构的各自好处。

第二章 栈与队列
栈与队列,是很多学习DS的同学遇到第一只拦路虎,很多人从这一章开始坐晕车,一直晕到结束。所以,理解栈与队列,是走向DS高手的一条必由之路。

学习此章前,你可以问一下自己是不是已经知道了以下几点:

1.栈、队列的定义及其相关数据结构的概念,包括:顺序栈,链栈,共享栈,循环队列,链队等。栈与队列存取数据(请注意包括:存和取两部分)的特点。
2.递归算法。栈与递归的关系,以及借助栈将递归转向于非递归的经典算法:hanoi问题,二叉树的递归和非递归遍历问题,

 3.栈的应用:数值表达式的求解,括号的配对等的原理,数值转换问题。
4.循环队列中判队空、队满条件,循环队列中入队与出队算法。

如果你已经对上面的几点了如指掌,栈与队列一章可以不看书了。注意,我说的是可以不看书,并不是可以不作题哦。
第五章 树与二叉树
从对线性结构的研究过度到对树形结构的研究,是数据结构课程学习的一次跃变,此次跃变完成的好坏,将直接关系到你到实际的考试中是否可以拿到高分,而这所有的一切,将最终影响你的专业课总分。所以,树这一章的重要性,已经不说自明了。

总体来说,树一章的知识点包括:
二叉树的概念、性质和存储结构,二叉树遍历的三种算法(递归与非递归),在三种基本遍历算法的基础上实现二叉树的其它算法,最优二叉树的概念、构成和应用,树的概念和存储形式,树与森林的遍历算法及其与二叉树遍历算法的联系,树与森林和二叉树的转换。

下面我们来看考试中对以上知识的主要考查方法:
1.二叉树的概念、性质和存储结构
考查方法可有:直接考查二叉树的定义,让你说明二叉树与普通双分支树的区别;考查满二叉树和完全二叉树的性质,普通二叉树的五个性质:第i层的最多结点数,深度为k的二叉树的最多结点数,n0=n2+1的性质,n个结点的完全二叉树的深度,二叉树的顺序存储和二叉链表存储的各自优缺点及适用场合。
2.二叉树的三种遍历算法
这一知识点掌握的好坏,将直接关系到树一章的算法能否理解,进而关系到树一章的算法设计题能否顺利完成。二叉树的遍历算法有三种:先序,中序和后序。其划分的依据是视其每个算法中对根结点数据的访问顺序而定。不仅要熟练掌握三种遍历的递归算法,理解其执行的实际步骤,并且应该熟练掌握三种遍历的非递归算法。

3.最优二叉树(哈夫曼树):
最优二叉树是为了解决特定问题引出的特殊二叉树结构,它的前提是给二叉树的每条边赋予了权值,这样形成的二叉树按权相加之和是最小的。最优二叉树一节,直接考查算法源码的很少,一般是给你一组数据,要求你建立基于这组数据的最优二叉树,并求出其最小权值之和,此类题目不难,属送分题。
4. 二叉树、树与森林:
  相互转换
树一章,处处是重点,道道是考题,大家务必个个过关。

第七章 查找

应该说,查找和排序两章是前面我们所学的知识的综合运用,用到了树、也用到了链表等知识,对这些数据结构某一方面的运用就构成了查找和排序。

现实生活中,search几乎无处不在,特别是现在的网络时代,万事离不开search,小到文档内文字的搜索,大到INTERNET上的搜索,search占据了我们上网的大部分时间。

在复习这一章的知识时,你需要先弄清楚以下几个概念:
关键字、主关键字、次关键字的含义;静态查找与动态查找的含义及区别;平均查找长度ASL的概念及在各种查找算法中的计算方法和计算结果。

DS的教材中,一般将search分为三类:1st,在顺序表上的查找;2nd,在树表上的查找;3rd,在哈希表上的查找。下面详细介绍其考查知识点及考查方式:

1.线性表上的查找:
主要分为三种线性结构:顺序表,有序顺序表,索引顺序表。对于第一种,我们采用传统查找方法,逐个比较。对于及有序顺序表我们采用二分查找法。对于第三种索引结构,我们采用索引查找算法。考生需要注意这三种表下的ASL值以及三种算法的实现。其中,二分查找还要特别注意适用条件以及其递归实现方法。
2.树表上的查找:()

二叉排序树

3.基本哈希表的查找算法:
哈希一词,是外来词,译自“hash”一词,意为:散列或杂凑的意思。哈希表查找的基本思想是:根据当前待查找数据的特征,以记录关键字为自变量,设计一个function,该函数对关键字进行转换后,其解释结果为待查的地址。基于哈希表的考查点有:哈希函数的设计,冲突解决方法的选择及冲突处理过程的描述。

第八章 内部排序
内排是DS课程中最后一个重要的章节,建立在此章之上的考题可以有多种类型:填空,选择,判断乃至大型算法题。但是,归结到一点,就是考查你对书本上的各种排序算法及其思想以及其优缺点和性能指标(时间复杂度)能否了如指掌。

这一章,我们对重点的规纳将跟以上各章不同。我们将从以下几个侧面来对排序一章进行不同的归纳,以期能更全面的理解排序一章的总体结构及各种算法。

从排序算法的种类来分,本章主要阐述了以下几种排序方法:插入、选择、交换、归并、计数等五种排序方法。
算法思想。
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

第一章 概 

1.数据:信息的载体,能被计算机识别、存储和加工处理。

2.数据元素:数据的基本单位,可由若干个数据项组成,数据项是具有独立含义的最小标识单位。

3.数据结构:数据之间的相互关系,即数据的组织形式。
它包括:1)数据的逻辑结构,从逻辑关系上描述数据,与数据存储无关,独立于计算机;
2)数据的存储结构,是逻辑结构用计算机语言的实现,依赖于计算机语言。
3)数据的运算,定义在逻辑结构上,每种逻辑结构都有一个运算集合。常用的运算:检索/插入/删除/更新/排序。

4.数据的逻辑结构可以看作是从具体问题抽象出来的数学模型。数据的存储结构是逻辑结构用计算机语言的实现。

5.数据类型:一个值的集合及在值上定义的一组操作的总称。分为:原子类型和结构类型。

6.抽象数据类型:抽象数据的组织和与之相关的操作。优点:将数据和操作封装在一起实现了信息隐藏。

7. 抽象数据类型ADT:是在概念层上描述问题;类:是在实现层上描述问题;在应用层上操作对象(类的实例)解决问题。

8.数据的逻辑结构,简称为数据结构,有:
1)线性结构,若结构是非空集则仅有一个开始和终端结点,并且所有结点最多只有一个直接前趋和后继。
2)非线性结构,一个结点可能有多个直接前趋和后继。

9.数据的存储结构有:
1)顺序存储,把逻辑相邻的结点存储在物理上相邻的存储单元内。
2)链接存储,结点间的逻辑关系由附加指针字段表示。
3)索引存储,存储结点信息的同时,建立附加索引表,有稠密索引和稀疏索引。
4)散列存储,按结点的关键字直接计算出存储地址。

10.评价算法的好坏是:算法是正确的;执行算法所耗的时间;执行算法的存储空间(辅助存储空间);易于理解、编码、调试。

11.算法的时间复杂度T(n):是该算法的时间耗费,是求解问题规模n的函数。记为O(n)
时间复杂度按数量级递增排列依次为:常数阶O(1)、对数阶O(log2n)、线性阶O(n)、线性对数阶O(nlog2n)、平方阶O(n^2)、立方阶O(n^3)……k次方阶O(n^k)、指数阶O(2^n)13.算法的空间复杂度S(n):是该算法的空间耗费,是求解问题规模n的函数。

12.算法衡量:是用时间复杂度和空间复杂度来衡量的,它们合称算法的复杂度。
13. 算法中语句的频度不仅与问题规模有关,还与输入实例中各元素的取值相关。
 
第 二 章   线 性 表
 
1.线性表:是由n(n≥0)个数据元素组成的有限序列。

2.线性表的基本运算有:
1InitList(L),构造空表,即表的初始化;
2ListLength(L),求表的结点个数,即表长;
3GetNode(L,i),取表中第i个结点,要求1≤i≤ListLength(L)
4LocateNode(L,x)查找L中值为x的结点并返回结点在L中的位置,有多个x则返回首个,没有则返回特殊值表示查找失败。
5InsertList(L,x,i)在表的第i个位置插入值为x的新结点,要求1≤i≤ListLength(L)+1
6DeleteList(L,i)删除表的第i个位置的结点,要求1≤i≤ListLength(L)

3.顺序表:把线性表的结点按逻辑次序存放在一组地址连续的存储单元里。

4.顺序表结点的存储地址计算公式:Loc(ai)=Loc(a1)+(i-1)*C1≤i≤n

5.顺序表上的基本运算
6.单链表:只有一个链域的链表称单链表。
在结点中存储结点值和结点的后继结点的地址,data  next  data是数据域,next是指针域
1)建立单链表。时间复杂度为O(n)
加头结点的优点:1)链表第一个位置的操作无需特殊处理;2)将空表和非空表的处理统一。
2)查找运算。时间复杂度为O(n)
7.循环链表:是一种首尾相连的链表。特点是无需增加存储量,仅对表的链接方式修改使表的处理灵活方便。
8.空循环链表仅由一个自成循环的头结点表示。
9.很多时候表的操作是在表的首尾位置上进行,此时头指针表示的单循环链表就显的不够方便,改用尾指针rear来表示单循环链表。

10.在结点中增加一个指针域,prior|data|next。形成的链表中有两条不同方向的链称为双链表。
11.顺序表和链表的比较
1) 基于空间的考虑:顺序表的存储空间是静态分配的,链表的存储空间是动态分配的。顺序表的存储密度比链表大。因此,在线性表长度变化不大,易于事先确定时,宜采用顺序表作为存储结构。
2) 基于时间的考虑:顺序表是随机存取结构,若线性表的操作主要是查找,很少有插入、删除操作时,宜用顺序表结构。对频繁进行插入、删除操作的线性表宜采用链表。若操作主要发生在表的首尾时采用尾指针表示的单循环链表。

12.存储密度=(结点数据本身所占的存储量)/(整个结点结构所占的存储总量)
存储密度:顺序表=1,链表<1
第 三 章   栈 和 队 列
1.栈是限制仅在表的一端进行插入和删除运算的线性表又称为后进先出表(LIFO表)。插入、删除端称为栈顶,另一端称栈底。表中无元素称空栈。

2.栈的基本运算有:
1initstack(s),构造一个空栈;
2stackempty(s),判栈空;
3stackfull(s),判栈满;
4push(s,x),进栈;
5pop (s),退栈;
6stacktop(s),取栈顶元素。

3.顺序栈:栈的顺序存储结构称顺序栈。

4.当栈满时,做进栈运算必定产生空间溢出,称上溢。 当栈空时,做退栈运算必定产生空间溢出,称下溢。上溢是一种错误应设法避免,下溢常用作程序控制转移的条件。

5.在顺序栈上的基本运算:
1) 置空栈。
Void initstack(seqstack *s)
{
 s->top=-1;
}
2)判栈空。
int stackempty(seqstack *s)
{
 return s->top==-1;
}
3)判栈满。
int stackfull(seqstack *s)
{
 return s->top==stacksize-1;
}
4)进栈。
Void push(seqstack *s,datatype x)
{
 if(stackfull(s))
  error(“stack overflow”);
 s->data[++s->top]=x;
}
5)退栈。
Datatype pop(seqstack *s)
{
 if(stackempty(s))
  error(“stack underflow”);
 return S->data[s->top--];
}
6)取栈顶元素。
Dtatatype stacktop(seqstack *s)
{
 if(stackempty(s))
  error(“stack underflow”);
 return S->data[s->top];
}

6.链栈:栈的链式存储结构称链栈。栈顶指针是链表的头指针。

7.链栈上的基本运算:
1) 建栈。
Void initstack(linkstack *s)
{
 s->top=NULL;
}
2)判栈空。
Int stackempty (linkstack *s)
{
 return s->top==NULL;
}
3) 进栈。
Void push(linkstack *s,datatype x)
{
 stacknode *p=(stacknode *)malloc(sizeof(stacknode));
 p->data=x;
 p->next=s->top;
 s->top=p;
}
4) 退栈。
Datatype pop(linksatck *s)
{
 datatype x;
 stacknode *p=s->top;
 if(stackempty(s))
  error(“stack underflow”);
 x=p->data;
 s->top=p->next;
 free(p);
 return x;
}
5) 取栈顶元素。
Datatype stacktop(linkstack *s)
{
 if(stackempty(s))
  error(“stack is empty”);
 return s->top->data;
}
 
8.队列是一种运算受限的线性表,允许删除的一端称队首,允许插入的一端称队尾。队列又称为先进先出线性表,FIFO表。

9.队列的基本运算:
1initqueue(q),置空队;
2queueempty(q),判队空;
3queuefull(q),判队满;
4enqueue(q,x),入队;
5dequeue(q),出队;
6queuefront(q),返回队头元素。

10.顺序队列:队列的顺序存储结构称顺序队列。设置frontrear指针表示队头和队尾元素在向量空间的位置。

11.顺序队列中存在假上溢现象,由于入队和出队操作使头尾指针只增不减导致被删元素的空间无法利用,队尾指针超过向量空间的上界而不能入队。

12.为克服假上溢现象,将向量空间想象为首尾相连的循环向量,存储在其中的队列称循环队列。i=(i+1)%queuesize

13.循环队列的边界条件处理:由于无法用front==rear来判断队列的
解决的方法有:
1) 另设一个布尔变量以区别队列的空和满;
2) 少用一个元素,在入队前测试rear在循环意义下加1是否等于front
3) 使用一个记数器记录元素总数。 
第 六 章  

1.树:是n个结点的有限集TT为空时称空树,否则满足:
1)有且仅有一个特定的称为根的结点;
2)其余结点可分为m个互不相交的子集,每个子集本身是一棵树,并称为根的子树。
2.树的表示方法:1)树形表示法;2)嵌套集合表示法;3)凹入表表示法;4)广义表表示法;
3.一个结点拥有的子树数称为该结点的度;一棵树的度是指树中结点最大的度数。
4.度为零的结点称叶子或终端结点;度不为零的结点称分支结点或非终端结点
5.根结点称开始结点,根结点外的分支结点称内部结点;
6.树中某结点的子树根称该结点的孩子;该结点称为孩子的双亲;
7.树中存在一个结点序列K1K2…Kn,使KiKi+1的双亲,则称该结点序列为K1Kn的路径或道路;
8.树中结点KKs间存在一条路径,则称KKs的祖先,KsK的子孙;
9.结点的层数从根算起,若根的层数为1,则其余结点层数是其双亲结点层数加1;双亲在同一层的结点互为堂兄弟;树中结点最大层数称为树的高度或深度;
10.树中每个结点的各个子树从左到右有次序的称有序树,否则称无序树;
11.森林是m棵互不相交的树的集合。
12.二叉树:是n个结点的有限集,它或为空集,或由一个根结点及两棵互不相交的、分别称为该根的左子树和右子树的二叉树组成。
13.二叉树不是树的特殊情况,这是两种不同的数据结构;它与无序树和度为2的有序树不同。
14.二叉树的性质:
1) 二叉树第i层上的结点数最多为2^(i-1)
2) 深度为k的二叉树至多有2^k-1个结点;
3) 在任意二叉树中,叶子数为n0,度为2的结点数为n2,则n0=n2+1
15.满二叉树是一棵深度为k的且有2^k-1个结点的二叉树;
16.完全二叉树是至多在最下两层上结点的度数可以小于2,并且最下层的结点集中在该层最左的位置的二叉树;
17.具有N个结点的完全二叉树的深度为log2N取整加1
18.二叉树的存储结构
1)顺序存储结构:把一棵有n个结点的完全二叉树,从树根起自上而下、从左到右对所有结点编号,然后依次存储在一个向量b[0~n]中,b[1~n]存放结点,b[0]存放结点总数。
2)链式存储结构
结点的结构为:lchild|data|rchild ;相应的类型说明:
typedef char data;
typedef struct node{
 datatype data;
 structnode *lchild , *rchild;
}bintnode;
typedef bintnode * bintree;
19.在二叉树中所有类型为bintnode的结点和一个指向开始结点的bintree类型的头指针构成二叉树的链式存储结构称二叉链表。
20.二叉链表由根指针唯一确定。在n个结点的二叉链表中有2n个指针域,其中n+1个为空。
21.二叉树的遍历方式有:前序遍历、中序遍历、后序遍历。时间复杂度为O(n)
22.线索二叉树:利用二叉链表中的n+1个空指针域存放指向某种遍历次序下的前趋和后继结点的指针,这种指针称线索。加线索的二叉链表称线索链表。相应二叉树称线索二叉树。
23.线索链表结点结构:lchild|ltag|data|rtag|rchildltag=0,lchild是指向左孩子的指针;ltag=1,lchild是指向前趋的线索;rtag=0,rchild是指向右孩子的指针;rtag=1,rchild是指向后继的线索;
24.查找*p在指定次序下的前趋和后继结点。算法的时间复杂度为O(h)。线索对查找前序前趋和后序后继帮助不大。
25.遍历线索二叉树。时间复杂度为O(n)
26.树、森林与二叉树的转换
1)树、森林与二叉树的转换
1)树与二叉树的转换:1}所有兄弟间连线;2}保留与长子的连线,去除其它连线。该二叉树的根结点的右子树必为空。
2)森林与二叉树的转换:1}将所有树转换成二叉树;2}将所有树根连线。
2)二叉树与树、森林的转换。是以上的逆过程。
27.树的存储结构
1)双亲链表表示法:为每个结点设置一个parent指针,就可唯一表示任何一棵树。Data|parent
2)孩子链表表示法:为每个结点设置一个firstchild指针,指向孩子链表头指针,链表中存放孩子结点序号。Data|firstchild
3双亲孩子链表表示法:将以上方法结合。Data|parent|firstchild
4)孩子兄弟链表表示法:附加两个指向左孩子和右兄弟的指针。Leftmostchild|data|rightsibling
28.树和森林的遍历:前序遍历一棵树等价于前序遍历对应二叉树;后序遍历等价于中序遍历对应二叉树。
 
29.最优二叉树(哈夫曼树):树的路径长度是从树根到每一结点的路径长度之和。将树中的结点赋予实数称为结点的权。
30.结点的带权路径是该结点的路径长度与权的乘积。树的带权路径长度又称树的代价,是所有叶子的带权路径长度之和。
31.带权路径长度最小的二叉树称最优二叉树(哈夫曼树)。
32.具有2n-1个结点其中有n个叶子,并且没有度为1的分支结点的树称为严格二叉树。
33.哈夫曼编码
34.对字符集编码时,要求字符集中任一字符的编码都不是其它字符的编码前缀,这种编码称前缀码。
35.字符出现频度与码长乘积之和称文件总长;字符出现概率与码长乘积之和称平均码长;
36.使文件总长或平均码长最小的前缀码称最优前缀码
37.利用哈夫曼树求最优前缀码,左为0,右为1。编码平均码长最小;没有叶子是其它叶子的祖先,不可能出现重复前缀。


第 八 章   排 序
 
1.文件:由一组记录组成,记录有若干数据项组成,唯一标识记录的数据项称关键字;
2.排序是将文件按关键字的递增(减)顺序排列;

3.排序文件中有相同的关键字时,若排序后相对次序保持不变的称稳定排序,否则称不稳定排序;

4.在排序过程中,文件放在内存中处理不涉及数据的内、外存交换的称内排序,反之称外排序;

5.排序算法的基本操作:1)比较关键字的大小;2)改变指向记录的指针或移动记录本身。

6.评价排序方法的标准:1)执行时间;2)所需辅助空间,辅助空间为O(1)称就地排序;另要注意算法的复杂程度。

7.若关键字类型没有比较运算符,可事先定义宏或函数表示比较运算。

8.插入排序
1)直接插入排序

2)希尔排序
实现过程:是将直接插入排序的间隔变为dd的取值要注意:1)最后一次必为12)避免d值互为倍数;

9.交换排序
1)冒泡排序
实现过程:从下到上相邻两个比较,按小在上原则扫描一次,确定最小值,重复n-1次。

2)快速排序
实现过程:将第一个值作为基准,设置i,j指针交替从两头与基准比较,有交换后,交换jii=j时确定基准,并以其为界限将序列分为两段。重复以上步骤。

10.选择排序
1)直接选择排序
实现过程:选择序列中最小的插入第一位,在剩余的序列中重复上一步,共重复n-1次。

2)堆排序
实现过程:把序列按层次填入完全二叉树,调整位置使双亲大于或小于孩子,建立初始大根或小根堆,调整树根与最后一个叶子的位置,排除该叶子重新调整位置。

11.归并排序
实现过程:将初始序列分为2个一组,最后单数轮空,对每一组排序后作为一个单元,对2个单元排序,直到结束。

12结论:
1) 若规模较小可采用直接插入或直接选择排序;
2) 若文件初始状态基本有序可采用直接插入、冒泡或随机快速排序;
3) 若规模较大可采用快速排序、堆排序或归并排序;
4) 任何借助于比较的排序,至少需要O(nlog2n)的时间,箱排序和基数排序只适用于有明显结构特征的关键字;
5) 有的语言没有提供指针及递归,使归并、快速、基数排序算法复杂;
6) 记录规模较大时为避免大量移动记录可用链表作为存储结构,如插入、归并、基数排序,但快速、堆排序在链表上难以实现,可提取关键字建立索引表,然后对索引表排序。
 
第 九 章   查 找

1.查找的同时对表做修改操作(如插入或删除)则相应的表称之为动态查找表,否则称之为静态查找表。
2.衡量一个查找算法次序优劣的标准是在查找过程中对关键字需要执行的平均比较次数(即平均查找长度ASL).

3.线性表上进行查找的方法主要有三种:顺序查找、二分查找和分块查找。
1)顺序查找的算法基本思想:是从表的一端开始顺序扫描线性表,依次将扫描到的结点关键字与给定值K比较,若当前扫描到的结点关键字与k相等则查找成功;若扫描结束后,仍未找到关键字等于K的结点,则查找失败。
1)顺序查找方法可用链式存储结构和顺序存储结构实现。
2)在顺序存储结构的顺序查找算法中所设的哨兵是为了简化循环的边界条件而引入的附加结点(元素),其作用是使for循环中省去判定防止下标越界的条件从而节省了比较的时间。
3)在等概率情况下,查找成功时其平均查找长度约为表长的一半(n+1)/2.查找失败的话其平均查找长度为n+1.
2)二分查找(又称折半查找),它的算法思想:是对一有序表中的元素,从初始的查找区间开始,每经过一次与当前查找区间的中点位置上的结点关键字进行比较,若相等,则查找成功,否则,当前查找区间的缩小一半,按k值大小在某半个区间内重复相同的步骤进行查找,直到查找成功或失败为止。
1)二分查找在等概率的情况下查找成功的平均查找长度ASLlg(n+1)-1,在查找失败时所需比较的关键字个数不超过判定树的深度,最坏情况下查找成功的比较次数也不超过判定树的深度┌lg(n+1)┐(不小于lg(n+1)的最小整数)
2)二分查找只适用于顺序存储结构而不能用链式存储结构实现。因为链表无法进行随机访问,如果要访问链表的中间结点,就必须先从头结点开始进行依次访问,这就要浪费很多时间,还不如进行顺序查找,而且,用链存储结构将无法判定二分的过程是否结束,因此无法用链表实现二分查找。
3)分块查找(又称索引顺序查找)的基本思想:是将原表分成若干块,各块内部不一定有序,但表中的块是"分块有序"的,并抽取各块中的最大关键字及其起始位置建立索引表。因为索引表是有序的,分块查找就是先用二分查找或顺序查找确定待查结点在哪一块,然后在已确定的块中进行顺序查找(不能用二分查找,因为块内是无序的)。分块查找实际上是两次查找过程,它的算法效率介与顺序查找和二分查找之间。

4.以上三种查找方法的比较如下表:
查找算法 存储结构 优点 缺点 适用于
顺序查找 顺序结构
链表结构  算法简单且对表的结构无任何要求 查找效率低n较小的表的查找和查找较少但改动较多的表(用链表作存储结构)
二分查找 顺序结构 查找效率高 关键字要有序且只能用顺序存储结构实现 特别适用于一经建立就很少改动又经常需要查找的线性表
分块查找 顺序结构
链表  在表中插入或删除记录时就只要在该记录所属块内操作,因为块内记录的存放是随意的,所以插入和删除比较容易 要增加一个辅助数组的存储空间,并要进行将初始表分块排序运算 适用于有分块特点的记录,如一个学校的学生登记表可按系号或班号分块。

9.散列技术:可以无需任何比较就找到待查关键字,其查找的期望时间为O(1).
散列表的概念:就是将所有可能出现的关键字的集合U(全集)映射到一个表T[0..m-1]的下标集上,这个表就是散列表。
10.而关键字与这个表地址之间以什么样的关系发生联系呢,这就要通过一个函数来建立,这个函数是以U中的关键字为自变量,以相应结点的存储地址为函数值,它就称为散列函数。将结点按其关键字的散列地址存储到散列表的过程称为散列。
11.根据某种散列函数,一个关键字的散列函数值是唯一的,但是有可能两个或多个不同关键字的函数值是相同的,这时就会把几个结点存储到同一个表位置上,这时就造成冲突(或碰撞)现象,这两个关键字称为该散列函数的同义词。
要完全(不是"安全")避免冲突需满足两个条件,一是关键字集合U不大于散列表长m,另一个是选择合适的散列函数,如果用h(ki)=0)这样的函数的话,看看有什么结果。

12.通常情况下U总是大大于m的,因此不可能完全避免冲突。冲突的频繁程度还与表的填满程度相关。装填因子α表示表中填入的结点数与表长的比值,通常取α≤1,因为α越大,表越满,冲突的机会也越大。
13.散列函数的选择有两条标准:简单和均匀。看看h(ki)=0这样的函数,简单是简单,但绝不均匀。

14.下面是常见的几种散列函数构的造方法:
1)平方取中法
2)除余法:它是用表长m来除关键字,取余数作为散列地址。若选除数m是关键字的基数的幂次,就会使得高位不同而低位相同的关键字互为同义词。因此最好选取素数为除数.
3)相乘取整法:有两个步骤,先用关键字key乘上某个常数A(0)
4)随机数法,此法以关键字为自变量,通过一随机函数得到的值作为散列地址。

15.处理冲突的方法:当不可避免发生冲突时,就必须对冲突加以解决,使发生冲突的同义词能存储到表中。

16.通常有两类方法处理冲突:开放定址法和拉链法。前者是将所有结点均存放在散列T[0..m-1]中,后者是将互为同义词的结点链成一个单链表,而将此链表的头指针放在散列表中。

17.开放定址法的一般形式为:hi=(h(key)+di)%m 1≤i≤m-1
18.开放定址法要求散列表的装填因子α≤1。开放定址法又有线性探查法、二次探查法和双重散列法之分。
1)由于线性探查法在构造散列表时,遇到冲突(有同义词)的时候会按探查序列向后面的空地址插入,从而使原来应插入到此位置的结点又与它发生冲突,当一连串的位置均已有结点时,本应插入到这些位置的结点又只能将其插入到更后面的同一个空结点上,这种散列地址不同的结点争夺同一个后继散列地址的现象就是聚集或堆积。(注意,同义词发生冲突不是堆积)
为了减小堆积现象的发生,可以用二次探查法和双重散列法进行探查。
2)拉链法解决冲突的做法是,将所有关键字为同义词的结点链接在同一个单链表中。

19.与开放定址法相比,拉链法有如下几个优点:
(1)拉链法处理冲突简单,且无堆积现象,即非同义词决不会发生冲突,因此平均查找长度较短;(简单无堆积)
(2)由于拉链法中各链表上的结点空间是动态申请的,故它更适于造表前无法确定表长的情况;(动态申表长)
(3)开放定址法为减少冲突要求装填因子α较小,当结点规模较大时会浪费很多空间,拉链法中α可以大于1,且结点较大时,其指针域可忽略不计,因此节省空间;(空间可节省)
(4)拉链法构造的散列表删除结点易实现,而开放定址法中则不能真正删除结点只能做删除标记。(删除易实现)
20.拉链法也有缺点:当结点规模较小时,用拉链法中的指针域也要占用额外空间,还是开放定址法省空间。

21.在散列表上的运算有查找、插入和删除,主要是查找。这三个操作的算法并不复杂,也容易理解。关于查找操作的时间性能,可看教材p202的表9.1。由表可见,散列表的平均查找长度不是结点个数n的函数,而是装填因子α的函数。α越小,冲突的概率越小,但空间的浪费将增加,当α大小合适时,散列表上的平均查找长度就是一个常数,时间性能是O(1).
 

 

2 0