资源描述
第1章 绪论1-1什么是数据? 它与信息是什么关系?【解答】什么是信息?广义地讲,信息就是消息。宇宙三要素(物质、能量、信息)之一。它是现实世界各种事物在人们头脑中的反映。此外,人们通过科学仪器能够认识到的也是信息。信息的特征为:可识别、可存储、可变换、可处理、可传递、可再生、可压缩、可利用、可共享。什么是数据?因为信息的表现形式十分广泛,许多信息在计算机中不方便存储和处理,例如,一个大楼中4部电梯在软件控制下调度和运行的状态、一个商店中商品的在库明细表等,必须将它们转换成数据才能很方便地在计算机中存储、处理、变换。因此,数据(data)是信息的载体,是描述客观事物的数、字符、以及所有能输入到计算机中并被计算机程序识别和处理的符号的集合。在计算机中,信息必须以数据的形式出现。1-2什么是数据结构? 有关数据结构的讨论涉及哪三个方面?【解答】数据结构是指数据以及相互之间的关系。记为:数据结构 = D, R 。其中,D是某一数据对象,R是该对象中所有数据成员之间的关系的有限集合。有关数据结构的讨论一般涉及以下三方面的内容: 数据成员以及它们相互之间的逻辑关系,也称为数据的逻辑结构,简称为数据结构; 数据成员极其关系在计算机存储器内的存储表示,也称为数据的物理结构,简称为存储结构; 施加于该数据结构上的操作。数据的逻辑结构是从逻辑关系上描述数据,它与数据的存储不是一码事,是与计算机存储无关的。因此,数据的逻辑结构可以看作是从具体问题中抽象出来的数据模型,是数据的应用视图。数据的存储结构是逻辑数据结构在计算机存储器中的实现(亦称为映像),它是依赖于计算机的,是数据的物理视图。数据的操作是定义于数据逻辑结构上的一组运算,每种数据结构都有一个运算的集合。例如搜索、插入、删除、更新、排序等。1-3数据的逻辑结构分为线性结构和非线性结构两大类。线性结构包括数组、链表、 栈、队列、优先级队列等; 非线性结构包括树、图等、这两类结构各自的特点是什么?【解答】线性结构的特点是:在结构中所有数据成员都处于一个序列中,有且仅有一个开始成员和一个终端成员,并且所有数据成员都最多有一个直接前驱和一个直接后继。例如,一维数组、线性表等就是典型的线性结构非线性结构的特点是:一个数据成员可能有零个、一个或多个直接前驱和直接后继。例如,树、图或网络等都是典型的非线性结构。1-4什么是抽象数据类型?试用C+的类声明定义“复数”的抽象数据类型。要求(1) 在复数内部用浮点数定义它的实部和虚部。(2) 实现3个构造函数:缺省的构造函数没有参数;第二个构造函数将双精度浮点数赋给复数的实部,虚部置为0;第三个构造函数将两个双精度浮点数分别赋给复数的实部和虚部。(3) 定义获取和修改复数的实部和虚部,以及+、-、*、/等运算的成员函数。(4) 定义重载的流函数来输出一个复数。【解答】抽象数据类型通常是指由用户定义,用以表示应用问题的数据模型。抽象数据类型由基本的数据类型构成,并包括一组相关的服务。/在头文件complex.h中定义的复数类#ifndef _complex_h_#define _complex_h_#include class comlex public: complex ( ) Re = Im = 0; /不带参数的构造函数 complex ( double r ) Re = r; Im = 0; /只置实部的构造函数 complex ( double r, double i ) Re = r; Im = i; /分别置实部、虚部的构造函数 double getReal ( ) return Re; /取复数实部 double getImag ( ) return Im; /取复数虚部 void setReal ( double r ) Re = r; /修改复数实部 void setImag ( double i ) Im = i; /修改复数虚部 complex& operator = ( complex& ob) Re = ob.Re; Im = ob.Im; /复数赋值 complex& operator + ( complex& ob );/重载函数:复数四则运算 complex& operator ( complex& ob ); complex& operator * ( complex& ob ); complex& operator / ( complex& ob ); friend ostream& operator ( ostream& os, complex& c );/友元函数:重载private: double Re, Im;/复数的实部与虚部;#endif /复数类complex的相关服务的实现放在C+源文件complex.cpp中#include #include #include “complex.h”complex& complex : operator + ( complex & ob ) /重载函数:复数加法运算。complex * result = new complex ( Re + ob.Re, Im + ob.Im );return *result; complex& complex : operator ( complex& ob ) /重载函数:复数减法运算 complex * result = new complex ( Re ob.Re, Im ob.Im );return * result;complex& complex : operator * ( complex& ob ) /重载函数:复数乘法运算complex * result = new complex ( Re * ob.Re Im * ob.Im, Im * ob.Re + Re * ob.Im );return *result;complex& complex : operator / ( complex& ) /重载函数:复数除法运算double d = ob.Re * ob.Re + ob.Im * ob.Im;complex * result = new complex ( ( Re * ob.Re + Im * ob.Im ) / d,( Im * ob. Re Re * ob.Im ) / d );return * result;friend ostream& operator ( ostream& os, complex & ob ) /友元函数:重载,将复数ob输出到输出流对象os中。 return os ob.Re = 0.0 ) ? “+” : “-” fabs ( ob.Im ) “i”;1-5 用归纳法证明:(1) (2) (3) 【证明】略1-6 什么是算法? 算法的5个特性是什么? 试根据这些特性解释算法与程序的区别。【解答】通常,定义算法为“为解决某一特定任务而规定的一个指令序列。”一个算法应当具有以下特性: 有输入。一个算法必须有0个或多个输入。它们是算法开始运算前给予算法的量。这些输入取自于特定的对象的集合。它们可以使用输入语句由外部提供,也可以使用赋值语句在算法内给定。 有输出。一个算法应有一个或多个输出,输出的量是算法计算的结果。 确定性。算法的每一步都应确切地、无歧义地定义。对于每一种情况,需要执行的动作都应严格地、清晰地规定。 有穷性。一个算法无论在什么情况下都应在执行有穷步后结束。 有效性。算法中每一条运算都必须是足够基本的。就是说,它们原则上都能精确地执行,甚至人们仅用笔和纸做有限次运算就能完成。算法和程序不同,程序可以不满足上述的特性(4)。例如,一个操作系统在用户未使用前一直处于“等待”的循环中,直到出现新的用户事件为止。这样的系统可以无休止地运行,直到系统停工。此外,算法是面向功能的,通常用面向过程的方式描述;程序可以用面向对象方式搭建它的框架。1-7 设n为正整数, 分析下列各程序段中加下划线的语句的程序步数。 (1) for (int i = 1; i = n; i+) (2) x = 0; y = 0; for (int j = 1; j = n; j+) for (int i = 1; i = n; i+) cij = 0.0; for (int j = 1; j = i; j+) for (int k = 1; k = n; k+) for (int k = 1; k = j; k+) cij = cij + aik * bkj; x = x + y; (3) int i = 1, j = 1; (4) int i =1; while (i=n & j=n) do i = i + 1; j = j + i; for (int j = 1; j = n; j+) i = i + j; while ( i arraySize或者对于某一个k (0 k n),使得k!*2k maxInt时,应按出错处理。可有如下三种不同的出错处理方式:(1) 用cerr及exit (1)语句来终止执行并报告错误;(2) 用返回整数函数值0, 1来实现算法,以区别是正常返回还是错误返回;(3) 在函数的参数表设置一个引用型的整型变量来区别是正常返回还是某种错误返回。试讨论这三种方法各自的优缺点,并以你认为是最好的方式实现它。【解答】#include iostream.h#define arraySize 100#define MaxInt 0x7fffffffint calc ( int T , int n ) int i, value = 1;if ( n != 0 ) int edge = MaxInt / n / 2;for ( i = 1; i edge ) return 0;value *= n * 2;Tn = value;cout A n = Tn endl;return 1;void main ( ) int AarraySize;int i;for ( i = 0; i arraySize; i+ )if ( !calc ( A, i ) ) cout failed at i . endl;break;1-9 (1) 在下面所给函数的适当地方插入计算count的语句:void d (ArrayElement x , int n ) int i = 1; do xi += 2; i +=2; while (i = n ); i = 1; while ( i = (n/2) ) xi += xi+1; i+; (2) 将由(1)所得到的程序化简。使得化简后的程序与化简前的程序具有相同的count值。(3) 程序执行结束时的count值是多少?(4) 使用执行频度的方法计算这个程序的程序步数,画出程序步数统计表。 【解答】(1) 在适当的地方插入计算count语句void d ( ArrayElement x , int n ) int i = 1; count +; do xi += 2; count +;i += 2; count +; count +;/针对while语句 while ( i = n ); i = 1; count +; while ( i = ( n / 2 ) ) count +;/针对while语句xi += xi+1;count +;i +;count +; count +;/针对最后一次while语句(2) 将由(1)所得到的程序化简。化简后的程序与原来的程序有相同的count值:void d ( ArrayElement x , int n ) int i = 1;do count += 3; i += 2; while ( i = n ); i = 1;while ( i = ( n / 2 ) ) count += 3; i +; count += 3;(3) 程序执行结束后的count值为 3n + 3。当n为偶数时,count = 3 * ( n / 2 ) + 3 * ( n / 2 ) + 3 = 3 * n + 3当n为奇数时,count = 3 * ( ( n + 1 ) / 2 ) + 3 * ( ( n 1 ) / 2 ) + 3 = 3 * n + 3(4) 使用执行频度的方法计算程序的执行步数,画出程序步数统计表:行 号 程 序 语 句一次执行步数执行频度程序步数 1 2 3 4 5 6 7 8 9 10 11 12void d ( ArrayElement x , int n ) int i = 1; do xi += 2;i += 2; while ( i = n ); i = 1; while ( i m ) m = b; if ( c m ) m = c; return m; 【方案2】(此程序可修改循环终止变量扩大到n个整数)int max ( int a, int b, int c ) int data3 = a, b, c ; int m = 0;/开始时假定data0最大 for ( int i = 1; i datam ) m = i;/m记录新的最大者 return datam;(2) 求3个整数中的最小整数的函数可将上面求最大整数的函数稍做修改,“”改为“”,可得求最小整数函数。 【方案1】int min ( int a, int b, int c ) int m = a; if ( b m ) m = b; if ( c m ) m = c; return m; 【方案2】(此程序可修改循环终止变量扩大到n个整数)int max ( int a, int b, int c ) int data3 = a, b, c ; int m = 0;/开始时假定data0最小 for ( int i = 1; i 3; i+ )/与其他整数逐个比较if ( datai ”改为“”,可得求最小整数函数。 【方案1】int mid ( int a, int b, int c ) int m1 = a, m2; if ( b m1 ) m2 = m1; m1 = b; else m2 = b; if ( c m1 ) m2 = m1; m1 = c; else if ( c m2 ) m2 = c; return m2; 【方案2】(此程序可修改循环终止变量扩大到n个整数寻求次小元素)int mid ( int a, int b, int c ) int data3 = a, b, c ; int m1 = 0, m2 = -1; /m1指示最小整数, m2指示次小整数 for ( int i = 1; i 3; i+ )/与其他整数逐个比较if ( datai datam1 ) m2 = m1; m1 = i; /原来最小变为次小, m1指示新的最小 else if ( m2 = -1 | datai datam2 ) m2 = i;/m2记录新的次小者 return datam2;第10章 索引与散列10-1 什么是静态索引结构?什么是动态索引结构?它们各有哪些优缺点?【解答】静态索引结构指这种索引结构在初始创建,数据装入时就已经定型,而且在整个系统运行期间,树的结构不发生变化,只是数据在更新。动态索引结构是指在整个系统运行期间,树的结构随数据的增删及时调整,以保持最佳的搜索效率。静态索引结构的优点是结构定型,建立方法简单,存取方便;缺点是不利于更新,插入或删除时效率低。动态索引结构的优点是在插入或删除时能够自动调整索引树结构,以保持最佳的搜索效率;缺点是实现算法复杂。10-2 设有10000个记录对象, 通过分块划分为若干子表并建立索引, 那么为了提高搜索效率, 每一个子表的大小应设计为多大? 【解答】每个子表的大小 s = n = 10000 = 100 个记录对象。10-3如果一个磁盘页块大小为1024 (=1K) 字节,存储的每个记录对象需要占用16字节,其中关键码占4字节,其它数据占12字节。所有记录均已按关键码有序地存储在磁盘文件中,每个页块的第1个记录用于存放线性索引。另外在内存中开辟了256K字节的空间可用于存放线性索引。试问:(1) 若将线性索引常驻内存,文件中最多可以存放多少个记录?(每个索引项8字节,其中关键码4字节,地址4字节)(2) 如果使用二级索引,第二级索引占用1024字节(有128个索引项),这时文件中最多可以存放多少个记录?【解答】(1) 因为一个磁盘页块大小为1024字节,每个记录对象需要占用16字节,则每个页块可存放1024 / 16 = 64个记录,除第一个记录存储线性索引外,每个页块可存储63个记录对象。又因为在磁盘文件中所有记录对象按关键码有序存储,所以线性索引可以是稀疏索引,每一个索引项存放一个页块的最大关键码及该页块的地址。若线性索引常驻内存,那么它最多可存放256 * (1024 / 8 ) = 256 * 128 = 32768个索引项,文件中可存放 32768 * 63 = 2064384个记录对象。(2) 由于第二级索引占用1024个字节,内存中还剩255K 字节用于第一级索引。第一级索引有255 * 128 = 32640个索引项,作为稀疏索引,每个索引项索引一个页块,则索引文件中可存放32640 * 63 = 2056320。 397 Hello World! 82 XYZ 1038 This string is rather long 1037 This is Shorter 42 ABC 2222 Hello new World!10-4 假设在数据库文件中的每一个记录是由占2个字节的整型数关键码和一个变长的数据字段组成。数据字段都是字符串。为了存放右面的那些记录,应如何组织线性索引?【解答】将所有字符串依加入的先后次序存放于一个连续的存储空间store中,这个空间也叫做“堆”,它是存放所有字符串的顺序文件。它有一个指针free,指示在堆store中当前可存放数据的开始地址。初始时free置为0,表示可从文件的0号位置开始存放。线性索引中每个索引项给出记录关键码,字符串在store中的起始地址和字符串的长度:索引表ID 堆store 关键码串长度串起始地址 0Hello World! XYZ This string is rather long This 4235682312is Shorter ABC Hello new World!free39712010371541103826152222165910-5 设有一个职工文件: 记录地址 职工号 姓 名 性别 职 业 年龄 籍贯 月工资(元) 10032 034 刘激扬 男 教 师 29 山东720.00 10068 064 蔡晓莉 女 教 师 32 辽宁1200.00 10104 073 朱 力 男 实验员 26 广东480.00 10140 081 洪 伟 男 教 师 36 北京1400.00 10176 092 卢声凯 男 教 师 28 湖北720.00 10212 123 林德康 男 行政秘书 33 江西480.00 10248 140 熊南燕 女 教 师 27 上海 780.00 10284 175 吕 颖 女 实验员 28 江苏480.00 10320 209 袁秋慧 女 教 师 24 广东720.00其中,关键码为职工号。试根据此文件,对下列查询组织主索引和倒排索引,再写出搜索结果关键码。(1) 男性职工;(2) 月工资超过800元的职工;(3) 月工资超过平均工资的职工;(4) 职业为实验员和行政秘书的男性职工;(5) 男性教师或者年龄超过25岁且职业为实验员和教师的女性职工。【解答】 主索引 月工资 倒排索引 职务 倒排索引 职工号记录地址月工资长度指针职务长度指针003410032480.3073教师6034106410068123064207310104175081308110140720.3034092409210176092140512310212209209614010248780.1140实验员20737175102841200.10641758209103201400.1081行政秘书1123 性别 倒排索引 年龄 倒排索引性别长度指针年龄长度指针男5034241209073261073081271140092282092123175女4064291034140321064175331123209361081搜索结果: (1) 男性职工 (搜索性别倒排索引):034, 073, 081, 092, 123(2) 月工资超过800元的职工 (搜索月工资倒排索引):064, 081(3) 月工资超过平均工资的职工(搜索月工资倒排索引) 月平均工资776元:064, 081, 140(4) 职业为实验员和行政秘书的男性职工(搜索职务和性别倒排索引):073, 123, 175 & 034, 073, 081, 092, 123 = 073, 123(5) 男性教师 (搜索性别与职务倒排索引):034, 073, 081, 092, 123 & 034, 064, 081, 092, 140, 209 = 034, 081, 092年龄超过25岁且职业为实验员和教师的女性职工 (搜索性别、职务和年龄倒排索引):064, 140, 175, 209 & 034, 064, 073, 081, 092, 140, 175, 209 & 034, 064, 073, 081,092, 123, 140, 175 = 064, 140, 17510-6 倒排索引中的记录地址可以是记录的实际存放地址,也可以是记录的关键码。试比较这两种方式的优缺点。 【解答】在倒排索引中的记录地址用记录的实际存放地址,搜索的速度快;但以后在文件中插入或删除记录对象时需要移动文件中的记录对象,从而改变记录的实际存放地址,这将对所有的索引产生影响:修改所有倒排索引的指针,不但工作量大而且容易引入新的错误或遗漏,使得系统不易维护。记录地址采用记录的关键码,缺点是寻找实际记录对象需要再经过主索引,降低了搜索速度;但以后在文件中插入或删除记录对象时,如果移动文件中的记录对象,导致许多记录对象的实际存放地址发生变化,只需改变主索引中的相应记录地址,其他倒排索引中的指针一律不变,使得系统容易维护,且不易产生新的错误和遗漏。10-7 m = 2的平衡m路搜索树是AVL树,m = 3的平衡m路搜索树是2-3树。它们的叶结点必须在同一层吗?m阶B树是平衡m路搜索树,反过来,平衡m路搜索树一定是B树吗?为什么?【解答】m = 3的平衡m路搜索树的叶结点不一定在同一层,而m阶B_树的叶结点必须在同一层,所以m阶B_树是平衡m路搜索树,反过来,平衡m路搜索树不一定是B_树。10-8 下图是一个3阶B树。试分别画出在插入65、15、40、30之后B树的变化。5580 9045958560 705025 35【解答】插入65后:55 80 90654525 35506070859555 80 插入15后:906525 4515706095855035插入40后:55 80 906525 455035 401570609585插入30后:55 803545259065301540706095855010-9 下图是一个3阶B树。试分别画出在删除50、40之后B树的变化。503060 805520957040【解答】删除50后: 5580309560 704020删除40后:55 8020 3060 709510-10 对于一棵有1999999个关键码的199阶B树,试估计其最大层数(不包括失败结点)及最小层数(不包括失败结点)。【解答】设B树的阶数m = 199,则m/2 = 100。若不包括失败结点层,则其最大层数为logm/2 (N+1)/2) + 1 = log100 1000000 +1 = 4若使得每一层关键码数达到最大,可使其层数达到最小。第0层最多有(m-1)个关键码,第1层最多有(m-1)2个关键码,第h-1层最多有(m-1)h个关键码。层数为h的B树最多有(m-1) + (m-1)2 + + (m-1)h-1 = (m-1) ( (m-1)h 1 ) / (m-2)个关键码。反之,若有n个关键码,n (m-1) ( (m-1)h 1 ) / (m-2),则 h log (m-1) (n(m-2)/(m-1)+1),所以,有1999999个关键码的199阶B树的最小层数为log (m-1) (n*(m-2)/(m-1)+1) = log198 (1999999*197 / 198 +1) = log 198 1989899 10-11 给定一组记录,其关键码为字符。记录的插入顺序为 C, S, D, T, A, M, P, I, B, W, N, G, U, R, K, E, H, O, L, J ,给出插入这些记录后的4阶B+树。假定叶结点最多可存放3个记录。【解答】 插入C, S, D 插入T 插入A 插入M D S S S C D S D MS T A CA C D S T S T C D 插入P 插入ID M S D S M PD IS T A CD M PA CS T D M S U 插入B, W, N, G 插入U D M S U W S T M N PD G IA B CD G IM N PS T W A B C 插入RP S U D M M N D G IA B CP R S T U W 插入KP S U D I M I K D G M N P R S T U W A B CP 插入ES U D I M D E G I K S T P R U W M N A B C 插入HP D G I M S U G H I K M N S T P R U W D E A B C 插入O, LP D G I M S U M N O A B CI K L S T P R G H D E U W 插入JI P D G K M S U I J K L P R S T M N O U W G H D E A B C10-12 设有一棵B+树,其内部结点最多可存放100个子女,叶结点最多可存储15个记录。对于1, 2, 3, 4, 5层的B+树,最多能存储多少记录,最少能存储多少记录。【解答】一层B+树:根据B+树定义,一层B+树的结点只有一个,它既是根结点又是叶结点,最多可存储m1 = 15个记录,最少可存储 m1/2 = 8个记录。二层B+树:第0层是根结点,它最多有m = 100棵子树,最少有2个结点;第1层是叶结点,它最多有m个结点,最多可存储m*m1 = 100*15 = 1500个记录,最少有2个结点,最少可存储2* m1/2 = 16个记录。三层B+树:第2层是叶结点。它最多有m2个结点,最多可存储m2 * m1 = 150000个记录。最少有2* m/2 = 100个结点,最少可存储2* m/2 * m1/2 = 800个记录。四层B+树:第3层是叶结点。它最多有m3个结点,可存储m3 * m1 = 15000000个记录。最少有2* m/2 2 = 2 * 502 = 5000个结点,存储2* m/2 2 * m1/2 = 40000个记录。五层B+树:第4层是叶结点。它最多有m4个结点,可存储m4 * m1 = 1500000000个记录。最少有2* m/2 3 = 2 * 503 = 250000个结点,存储2* m/2 3 * m1/2 = 2000000个记录。10-13设散列表为HT13, 散列函数为 H (key) = key %13。用闭散列法解决冲突, 对下列关键码序列 12, 23, 45, 57, 20, 03, 78, 31, 15, 36 造表。(1) 采用线性探查法寻找下一个空位, 画出相应的散列表, 并计算等概率下搜索成功的平均搜索长度和搜索不成功的平均搜索长度。(2) 采用双散列法寻找下一个空位, 再散列函数为 RH (key) = (7*key) % 10 + 1, 寻找下一个空位的公式为 Hi = (Hi-1 + RH (key) % 13, H1 = H (key)。画出相应的散列表, 并计算等概率下搜索成功的平均搜索长度。【解答】使用散列函数 H(key) = key mod 13,有H(12) = 12, H(23) = 10,H(45) = 6,H(57) = 5,H(20) = 7,H(03) = 3,H(78) = 0,H(31) = 5,H(15) = 2,H(36) = 10.(1) 利用线性探查法造表: 0 1 2 3 4 5 6 7 8 9 10 11 12 78 15 03 57 45 20 31 23 36 12 (1) (1) (1) (1) (1) (1) (4) (1) (2) (1)搜索成功的平均搜索长度为ASLsucc = (1 + 1 + 1 + 1 + 1 + 1 + 4 + 1 + 2 + 1) = 搜索不成功的平均搜索长度为ASLunsucc = (2 + 1 + 3 + 2 + 1 + 5 + 4 + 3 + 2 + 1 + 5 + 4 + 3) = (2) 利用双散列法造表:Hi = (Hi-1 + RH (key) % 13, H1 = H (key) 0 1 2 3 4 5 6 7 8 9 10 11 12 78 15 03 57 45 20 31 36 23 12 (1) (1) (1) (1) (1) (1) (3) (5) (1) (1)搜索成功的平均搜索长度为ASLsucc = (1 + 1 + 1 + 1 + 1 + 1 + 3 + 5 + 1 + 1) = 10-14 设有150个记录要存储到散列表中, 要求利用线性探查法解决冲突, 同时要求找到所需记录的平均比较次数不超过2次。试问散列表需要设计多大? 设a是散列表的装载因子,则有【解答】已知要存储的记录数为n = 150,查找成功的平均查找长度为ASLsucc 2,则有ASLsucc = 2,解得 a 。又有a = ,则 m 225。10-15 若设散列表的大小为m,利用散列函数计算出的散列地址为h = hash(x)。(1) 试证明:如果二次探查的顺序为(h + q2), (h + (q-1)2), , (h+1), h, (h-1), , (h-q2),其中, q = (m-1)/2。因此在相继被探查的两个桶之间地址相减所得的差取模(%m)的结果为m-2, m-4, m-6, , 5, 3, 1, 1, 3, 5, , m-6, m-4, m-2(2) 编写一个算法,使用课文中讨论的散列函数h(x)和二次探查解决冲突的方法,按给定值x来搜索一个大小为m的散列表。如果x不在表中,则将它插入到表中。【解答】(1) 将探查序列分两部分讨论: (h + q2), (h + (q-1)2), , (h+1), h 和 (h-1), (h-22), , (h-q2)。对于前一部分,设其通项为h + ( q d )2, d = 0, 1, , q,则相邻两个桶之间地址相减所得的差取模: ( h + (q (d -1) )2 ( h + (q d )2 ) ) % m = ( (q (d -1 ) )2 (q d )2 ) % m= (2*q -2*d +1) % m = ( m 2*d ) % m. ( 代换 q = (m-1)/2 )代入 d = 1, 2, , q,则可得到探查序列如下:m-2, m-4, m-6, , 5, 3, 1。 ( m 2*q = m 2* (m-1)/2 = 1 )对于后一部分,其通项为h ( q d )2, d = q, q+1, , 2q,则相邻两个桶之间地址相减所得的差取模:( h ( q d )2 ( h ( q (d+1) )2 ) ) % m = ( ( q (d+1)2 (q d )2 ) % m= ( 2*d 2*q +1) % m = ( 2*d m + 2) % m ( 代换 q = (m-1)/2 )代入 d = q, q+1, , 2q-1,则可得到2*dm+2 = 2*q m +2 = m 1 m +2 = 1,2*dm+2 = 2*q + 2 m +2 = m 1 + 2 m +2 = 3, , 2*dm+2 = 2*(2*q-1) m +2 = 2*(m11) m + 2 = 2*m 4 m +2 = m 2。证毕(2) 编写算法下面是使用二次探查法处理溢出时的散列表类的声明。template class HashTable /散列表类的定义public: enum KindOfEntry Active, Empty, Deleted ;/表项分类 (活动 / 空 / 删) HashTable ( ) : TableSize ( DefaultSize ) AllocateHt ( ); CurrentSize = 0; /构造函数 HashTable ( ) delete ht; /析构函数 const HashTable & operator = ( const HashTable & ht2 );/重载函数:表赋值 int Find ( const Type & x );/在散列表中搜索x int IsEmpty ( ) return !CurrentSize ? 1 : 0; /判散列表空否,空则返回1 private: struct HashEntry /散列表的表项定义 Type Element;/表项的数据, 即表项的关键码 KindOfEntry info;/三种状态: Active, Empty, Deleted HashEntry ( ) : info (Empty ) /表项构造函数 HashEntry ( const Type &E, KindOfEntry i = Empty ) : Element (E), info (i) ; enum DefualtSize = 31; HashEntry *ht; /散列表存储数组 int TableSize;/数组长度,要求是满足4k+3的质数,k是整数 int CurrentSize;/已占据散列地址数目 void AllocateHt ( ) ht = new HashEntryTableSize ; /为散列表分配存储空间; int FindPos ( const Type & x )
展开阅读全文