STRUCT DATA3

本文介绍了数据结构中的二叉排序树和散列表的概念及其查找方法,同时涵盖了排序的基本原理,包括排序的目标、关键字的选择、排序的稳定性及不同类型的排序方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.1.1    树上的查找

当用线性表作为表的组织形式时,可以有三种查找法。其中以二分查找效率最高。但由于二分查找要求表中结点按关键字有序,且不能用链表作存储结构,因此,当表的插入或删除操作频繁时,为维护表的有序性,势必要移动表中很多结点。这种由移动结点引起的额外时间开销,就会抵消二分查找的优点。也就是说,二分查找只适用于静态查找表。若要对动态查找表进行高效率的查找,可采用下面介绍的几种特殊的二叉树或树作为表的组织形式。不妨将它们统称为树表。下面将分别讨论在这些树表上进行查找和修改操作的方法。


二叉排序树

1、二叉排序树的定义
  二叉排序树(Binary Sort Tree)又称二叉查找(搜索)(Binary Search Tree)。其定义为:二叉排序树或者是空树,或者是满足如下性质的二叉树:
①若它的左子树非空,则左子树上所有结点的值均小于根结点的值;
②若它的右子树非空,则右子树上所有结点的值均大于根结点的值;
③左、右子树本身又各是一棵二叉排序树。
  上述性质简称二叉排序树性质(BST性质),故二叉排序树实际上是满足BST性质的二叉树。

2、二叉排序树的特点
  由BST性质可得:
  (1)二叉排序树中任一结点x,其左()子树中任一结点y(若存在)的关键字必小()x的关键字。
  (2)二叉排序树中,各结点关键字是惟一的。
  注意:
  实际应用中,不能保证被查找的数据集中各元素的关键字互不相同,所以可将二叉排序树定义中BST性质(1)里的"小于"改为"大于等于",或将BST性质(2)里的"大于"改为"小于等于",甚至可同时修改这两个性质。
  (3)按中序遍历该树所得到的中序序列是一个递增有序序列。

1.1.2        散列查找

散列方法不同于顺序查找、二分查找、二叉排序树及B-树上的查找。它不以关键字的比较为基本操作,采用直接寻址技术。在理想情况下,无须任何比较就可以找到待查关键字,查找的期望时间为O(1)

散列表的概念

1、散列表
     设所有可能出现的关键字集合记为U(简称全集)。实际发生(即实际存储)的关键字集合记为K|K||U|小得多)。
     散列方法是使用函数hU映射到表T[0..m-1]的下标上(m=O(|U|))。这样以U中关键字为自变量,以h为函数的运算结果就是相应结点的存储地址。从而达到在O(1)时间内就可完成查找。
  其中:
     hU→{012m-1} ,通常称h为散列函数(Hash Function)。散列函数h的作用是压缩待处理的下标范围,使待处理的|U|个值减少到m个值,从而降低空间开销。
     ② T为散列表(Hash Table)
     ③ h(Ki)(KiU)是关键字为Ki结点存储地址(亦称散列值或散列地址)
     ④将结点按其关键字的散列地址存储到散列表中的过程称为散列(Hashing)
  3
、散列表的冲突现象
1)冲突
     两个不同的关键字,由于散列函数值相同,因而被映射到同一表位置上。该现象称为冲突(Collision)或碰撞。发生冲突的两个关键字称为该散列函数的同义词(Synonym)
   【例】上图中的k2≠k5,但h(k2)=h(k5),故k2K5所在的结点的存储地址相同。

2)安全避免冲突的条件
     最理想的解决冲突的方法是安全避免冲突。要做到这一点必须满足两个条件:
①其一是|U|≤m
②其二是选择合适的散列函数。
     这只适用于|U|较小,且关键字均事先已知的情况,此时经过精心设计散列函数h有可能完全避免冲突。

3)冲突不可能完全避免
     通常情况下,h是一个压缩映像。虽然|K|≤m,但|U|>m,故无论怎样设计h,也不可能完全避免冲突。因此,只能在设计h时尽可能使冲突最少。同时还需要确定解决冲突的方法,使发生冲突的同义词能够存储到表中。

4)影响冲突的因素
     冲突的频繁程度除了与h相关外,还与表的填满程度相关。
     设mn分别表示表长和表中填人的结点数,则将α=n/m定义为散列表的装填因子(Load Factor)α越大,表越满,冲突的机会也越大。通常取α≤1

1.2      排序

排序(sort)或分类
所谓排序,就是要整理文件中的记录,使之按关键字递增(或递减)次序排列起来。其确切定义如下:
  输入:n个记录R1R2Rn,其相应的关键字分别为K1K2Kn
  输出:RilRi2Rin,使得Ki1≤Ki2≤…≤Kin(Ki1≥Ki2≥…≥Kin)
1.被排序对象--文件
  被排序的对象--文件由一组记录组成。
  记录则由若干个数据项(或域)组成。其中有一项可用来标识一个记录,称为关键字项。该数据项的值称为关键字(Key)
  注意:
     在不易产生混淆时,将关键字项简称为关键字。

2.排序运算的依据--关键字
     用来作排序运算依据的关键字,可以是数字类型,也可以是字符类型。
     关键字的选取应根据问题的要求而定。
【例】在高考成绩统计中将每个考生作为一个记录。每条记录包含准考证号、姓名、各科的分数和总分数等项内容。若要惟一地标识一个考生的记录,则必须用"准考证号"作为关键字。若要按照考生的总分数排名次,则需用"总分数"作为关键字。

排序的稳定性

当待排序记录的关键字均不相同时,排序结果是惟一的,否则排序结果不唯一。
在待排序的文件中,若存在多个关键字相同的记录,经过排序后这些具有相同关键字的记录之间的相对次序保持不变,该排序方法是稳定的;若具有相同关键字的记录之间的相对次序发生变化,则称这种排序方法是不稳定的。
  注意:
     排序算法的稳定性是针对所有输入实例而言的。即在所有可能的输入实例中,只要有一个实例使得算法不满足稳定性要求,则该排序算法就是不稳定的。

排序方法的分类

1.按是否涉及数据的内、外存交换分
     在排序过程中,若整个文件都是放在内存中处理,排序时不涉及数据的内、外存交换,则称之为内部排序(简称内排序);反之,若排序过程中要进行数据的内、外存交换,则称之为外部排序。
  注意:
     ①内排序适用于记录个数不很多的小文件
     ②外排序则适用于记录个数太多,不能一次将其全部记录放人内存的大文件。

2.按策略划分内部排序方法
     可以分为五类:插入排序、选择排序、交换排序、归并排序和分配排序。

排序算法分析

1.排序算法的基本操作
     大多数排序算法都有两个基本的操作:
  (1) 比较两个关键字的大小;
  (2) 改变指向记录的指针或移动记录本身。
  注意:
     (2)种基本操作的实现依赖于待排序记录的存储方式。

2.待排文件的常用存储方式
1)以顺序表(或直接用向量)作为存储结构
    排序过程:对记录本身进行物理重排(即通过关键字之间的比较判定,将记录移到合适的位置)

2)以链表作为存储结构
  排序过程:无须移动记录,仅需修改指针。通常将这类排序称为链表(或链式)排序;

3)用顺序的方式存储待排序的记录,但同时建立一个辅助表(如包括关键字和指向记录位置的指针组成的索引表)
  排序过程:只需对辅助表的表目进行物理重排(即只移动辅助表的表目,而不移动记录本身)。适用于难于在链表上实现,仍需避免排序过程中移动记录的排序方法。

3.排序算法性能评价
1)评价排序算法好坏的标准
  评价排序算法好坏的标准主要有两条:
     执行时间和所需的辅助空间
     算法本身的复杂程度

2)排序算法的空间复杂度
  若排序算法所需的辅助空间并不依赖于问题的规模n,即辅助空间是O(1),则称之为就地排序(In-PlaceSou)
  非就地排序一般要求的辅助空间为O(n)

3)排序算法的时间开销
  大多数排序算法的时间开销主要是关键字之间的比较和记录的移动。有的排序算法其执行时间不仅依赖于问题的规模,还取决于输入实例中数据的状态。

文件的顺序存储结构表示

  #define n l00 //假设的文件长度,即待排序的记录数目
  typedef int KeyType //假设的关键字类型
  typedef struct{ //记录类型
    KeyType key //关键字项
    InfoType otherinfo//其它数据项,类型InfoType依赖于具体应用而定义
   }RecType
  typedef RecType SeqList[n+1]//SeqList为顺序表类型,表中第0个单元一般用作哨兵
  注意:
     若关键字类型没有比较算符,则可事先定义宏或函数来表示比较运算。
【例】关键字为字符串时,可定义宏"#define LT(ab)(Stromp((a)(b))<0)"。那么算法中"a<b"可用"LT(ab)"取代。若使用C++,则定义重载的算符"<"更为方便。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值