第7章集合与搜索 第7章集合与搜索 7-1设A={1,2,3},B={3,4,5},求下列结果 (2)A*B (3)A-B (4)AContains(1)(5)A.AddMember(1) (6)A. DelMember(1) (7)A Min o 【解答】 (1)集合的并A+B={1,2,3,4,5} (2)集合的交A*B={3} (3)集合的差A-B={1,2} (4)包含 A Contains(1)=1,表示运算结果为"True (5)增加 A. AddMember(1),集合中仍为{1,2,3},因为增加的是重复元素,所以不加入 (6)删除 A. DelMember(1),集合中为{2,3} (7)求最小元素AMn(),结果为1 2试编写一个算法,打印一个有穷集合中的所有成员。要求使用集合抽象数据类型中的基本操作。如 果集合中包含有子集合,各个子集合之间没有重复的元素,采用什么结构比较合适 【解答】 集合抽象数据类型的部分内容 template class Set i ∥对象:零个或多个成员的聚集。其中所有成员的类型是一致的,但没有一个成员是相同的。 int Contains( const Type x ∥判元素x是否集合this的成员 int SubSet( Set& right )i 判集合this是否集合rght的子集 int operator==( Set & right ) ∥判集合this与集合rght是否相等 int Elemtype (; ∥返回集合元素的类型 Type GetData ( 返回集合原子元素的值 char GetName ( 返回集合this的集合名 Set *GetSubSet(; 回集合this的子集合地址 Set t)i ∥友元函数,将集合t输出到输出流对象out . traverse( out, t ) return out; void traverse( ostream& out, Set s)i ∥友元函数,集合的遄历算法 if(s Is Empty (=0)1 ∥集合元素不空 if(s Elemtype (==0)out <<s GetName ()<<'i 输出集合名及花括号 else if(s Elemtype (=1)( ∥集合原子元素 out<<sGetData ( ∥输出原子元素的值
第 7 章 集合与搜索 80 第 7 章 集合与搜索 7-1 设 A = { 1, 2, 3 }, B = { 3, 4, 5 },求下列结果: (1) A + B (2) A * B (3) A - B (4) A.Contains (1) (5) A.AddMember (1) (6) A.DelMember (1) (7) A.Min ( ) 【解答】 (1) 集合的并 A + B = { 1, 2, 3, 4, 5 } (2) 集合的交 A * B = { 3 } (3) 集合的差 A - B = { 1, 2 } (4) 包含 A.Contains (1) = 1,表示运算结果为"True" (5) 增加 A.AddMember (1),集合中仍为{ 1, 2, 3 },因为增加的是重复元素,所以不加入 (6) 删除 A.DelMember (1),集合中为{ 2, 3 } (7) 求最小元素 A.Min ( ),结果为 1 7-2 试编写一个算法,打印一个有穷集合中的所有成员。要求使用集合抽象数据类型中的基本操作。如 果集合中包含有子集合,各个子集合之间没有重复的元素,采用什么结构比较合适。 【解答】 集合抽象数据类型的部分内容 template class Set { //对象: 零个或多个成员的聚集。其中所有成员的类型是一致的, 但没有一个成员是相同的。 int Contains ( const Type x ); //判元素 x 是否集合 this 的成员 int SubSet ( Set & right ); //判集合 this 是否集合 right 的子集 int operator == ( Set & right ); //判集合 this 与集合 right 是否相等 int Elemtype ( ); //返回集合元素的类型 Type GetData ( ); //返回集合原子元素的值 char GetName ( ); //返回集合 this 的集合名 Set * GetSubSet ( ); //返回集合 this 的子集合地址 Set * GetNext ( ); //返回集合 this 的直接后继集合元素 int IsEmpty ( ); //判断集合 this 是否空。空则返回 1, 否则返回 0 }; ostream& operator t ) { //友元函数, 将集合 t 输出到输出流对象 out。 t.traverse ( out, t ); return out; } void traverse ( ostream& out, Set s ) { //友元函数, 集合的遍历算法 if ( s.IsEmpty ( ) == 0 ) { //集合元素不空 if ( s.Elemtype ( ) == 0 ) out << s.GetName ( ) << ‘{’; //输出集合名及花括号 else if ( s.Elemtype ( ) == 1 ) { //集合原子元素 out << s.GetData ( ); //输出原子元素的值
第7章集合与搜索 if(s GetNext (I=NULL )out <<',i ∥子集合 输出子集合 if(s GetNext ()I= NULL )out<<',; } traverse(s GetNext (); ∥向同一集合下一元素搜索 else out<<"}’; 如果集合中包含有子集合,各个子集合之间没有重复的元素,采用广义表结构比较合适。也可以使 用并查集结构 7-3当全集合可以映射成1到N之间的整数时,可以用位数组来表示它的任一子集合。当全集合是下列 集合时,应当建立什么样的映射?用映射对照表表示。 (1)整数0,1,…,99 (2)从n到m的所有整数,n≤m (3)整数n,n+2,n+4,…,n+2k (4)字母‘a,b;,c (5)两个字母组成的字符串,其中,每个字母取自‘a',b',ec',…,z。 【解答】 (1)i→i的映射关系,i=0,1,2,…,99 (2)i→ni的映射关系,i=n,n+1,n+2,…,m nn+1|n+2 (3)i→(i-n)/2的映射关系,i=n,n+2,n+4,…,n+2k (4)ord(c)→ord(c)-ord(a)的映射关系,c=a,b,e,…,z 心… (5)(ord(cl)-ord(a))*26+ord(c2)-ord(a)的映射关系,cl=c2=a,"b,'c,…,z 7-4试证明:集合A是集合B的子集的充分必要条件是集合A和集合B的交集是A。 【证明】 必要条件:因为集合A是集合B的子集,有A≌B,此时,对于任一x∈A,必有ⅹ∈B,因此可以 推得x∈A∩B,就是说,如果A是B的子集,一定有A∩B=A 充分条件:如果集合A和集合B的交集A∩B是A,则对于任一x∈A,一定有x∈A∩B,因此可
第 7 章 集合与搜索 81 if ( s.GetNext ( ) != NULL ) out << ‘,’; } else { //子集合 traverse ( s. GetSubSet ( ) ); //输出子集合 if ( s.GetNext ( ) != NULL ) out << ‘,’; } traverse ( s.GetNext ( ) ); //向同一集合下一元素搜索 } else out << ‘}’; } 如果集合中包含有子集合,各个子集合之间没有重复的元素,采用广义表结构比较合适。也可以使 用并查集结构。 7-3 当全集合可以映射成 1 到 N 之间的整数时,可以用位数组来表示它的任一子集合。当全集合是下列 集合时,应当建立什么样的映射?用映射对照表表示。 (1) 整数 0, 1, …, 99 (2) 从 n 到 m 的所有整数,n m (3) 整数 n, n+2, n+4, …, n+2k (4) 字母 ‘a’, ‘b’, ‘c’, …, ‘z’ (5) 两个字母组成的字符串, 其中, 每个字母取自 ‘a’, ‘b’, ‘c’, …, ‘z’。 【解答】 (1) i → i 的映射关系,i = 0, 1, 2, …, 99 (2) i → n-i 的映射关系,i = n, n+1, n+2, …, m 0 1 2 m-n n n+1 n+2 … m (3) i → (i-n)/2 的映射关系,i = n, n+2, n+4, …, n+2k 0 1 2 k n n+2 n+4 … n+2k (4) ord (c) → ord (c) - ord ('a') 的映射关系,c = 'a', 'b', 'c', …, 'z' 0 1 2 25 'a' 'b' 'c' … 'z' (5) (ord (c1) - ord ('a') )*26 + ord (c2) - ord ('a')的映射关系,c1 = c2 = 'a', 'b', 'c', …, 'z' 0 1 2 675 'aa' 'ab' 'ba' … 'zz' 7-4 试证明:集合 A 是集合 B 的子集的充分必要条件是集合 A 和集合 B 的交集是 A。 【证明】 必要条件:因为集合 A 是集合 B 的子集,有 A B,此时,对于任一 x A,必有 x B,因此可以 推得 x A∩B,就是说,如果 A 是 B 的子集,一定有 A∩B = A。 充分条件:如果集合 A 和集合 B 的交集 A∩B 是 A,则对于任一 x A,一定有 x A∩B,因此可
第7章集合与搜索 以推得x∈B,由此可得AcB,即集合A是集合B的子集。 7-5试证明:集合A是集合B的子集的充分必要条件是集合A和集合B的并集是B 【证明】 必要条件:因为集合A是集合B的子集,有AcB,此时,对于任一x∈A,必有x∈B,它一定 在AUB中。另一方面,对于那些xgA但x∈B的元素,它也必在AUB中,因此可以得出结论:凡 是属于集合B的元素一定在A∪B中,AUB=B 充分条件:如果存在元素x∈A且xgB,有x∈AUB,但这不符合集合A和集合B的并集AUB 是B的要求。集合的并AUB是集合B的要求表明,对于任一x∈AUB,同时应有x∈B。对于那些满 足x∈A的x,既然x∈AUB,也应当x∈B,因此,在此种情况下集合A应是集合B的子集 7-6设+、*、-是集合的或、与、差运算,试举一个例子,验证 A+B=(A- B)+(B-A)+A*B 【解答】 若设集合A={1,3,4,7,9,15},集合B={2,3,5,6,7,12,15,17}。则 A+B={1,2,3,4,5,6,7,9,12,15,17 又A*B={3,7,15},A-B={1,4,9},B-A={2,5,6,12,17} 则 (A-B)+(B-A)+A*B={1,2,3,4,5,6,7,9,12,15,17} H A+B=(A-B)+(B-A)+A*B 7给定一个用无序链表表示的集合,需要在其上执行 operator+(, operator+(, operator-( Contains(x Add Member(x, DelMember(x),Mn(),试写出它的类声明,并给出所有这些成员函数的实现。 【解答】 下面给出用无序链表表示集合时的类的声明 template class SetNode i ∥集合的结点类定义 friend class SetList Type data; ∥每个成员的数据 SetNode "link ∥链接指针 Node( const Type&item):data(item),link(NUL;∥构造函数 template class Set ∥集合的类定义 private SetNode *first,"last; ∥无序链表的表头指针,表尾指针 Setlist (i first=last=new SetNode (0):) ∥构造函数 -Setlist()( MakeEmpty ( delete first;) ∥析构函数 void Make Empty ( 置空集合
第 7 章 集合与搜索 82 以推得 x B,由此可得 A B,即集合 A 是集合 B 的子集。 7-5 试证明:集合 A 是集合 B 的子集的充分必要条件是集合 A 和集合 B 的并集是 B。 【证明】 必要条件:因为集合 A 是集合 B 的子集,有 A B,此时,对于任一 x A,必有 x B, 它一定 在 A∪B 中。另一方面,对于那些 x' A, 但 x' B 的元素,它也必在 A∪B 中,因此可以得出结论:凡 是属于集合 B 的元素一定在 A∪B 中,A∪B = B。 充分条件:如果存在元素 x A 且 x B,有 x A∪B,但这不符合集合 A 和集合 B 的并集 A∪B 是 B 的要求。集合的并 A∪B 是集合 B 的要求表明,对于任一 x A∪B,同时应有 x B。对于那些满 足 x' A 的 x',既然 x' A∪B,也应当 x' B,因此,在此种情况下集合 A 应是集合 B 的子集。 7-6 设+、*、-是集合的或、与、差运算,试举一个例子,验证 A + B = (A - B) + (B - A) + A * B 【解答】 若设集合 A= { 1, 3, 4, 7, 9, 15 },集合 B = { 2, 3, 5, 6, 7, 12, 15, 17 }。则 A + B = { 1, 2, 3, 4, 5, 6, 7, 9, 12, 15, 17 } 又 A * B = { 3, 7, 15 }, A – B = { 1, 4, 9 }, B –A = { 2, 5, 6, 12, 17 } 则 (A – B) + (B – A) + A * B = { 1, 2, 3, 4, 5, 6, 7, 9, 12, 15, 17 } 有 A + B = (A – B) + ( B – A ) + A * B。 7-7 给定一个用无序链表表示的集合,需要在其上执行 operator+( ), operator*( ), operator- ( ), Contains(x), AddMember (x), DelMember(x), Min( ),试写出它的类声明,并给出所有这些成员函数的实现。 【解答】 下面给出用无序链表表示集合时的类的声明。 template class Set; //用以表示集合的无序链表的类的前视定义 template class SetNode { //集合的结点类定义 friend class SetList; private: Type data; //每个成员的数据 SetNode *link; //链接指针 public: SetNode (const Type& item ) : data (item), link (NULL); //构造函数 }; template class Set { //集合的类定义 private: SetNode *first, *last; //无序链表的表头指针, 表尾指针 public: SetList ( ) { first = last = new SetNode (0); } //构造函数 ~SetList ( ) { MakeEmpty ( ); delete first; } //析构函数 void MakeEmpty ( ); //置空集合
第7章集合与搜索 int AddMember( const Type&x ) ∥把新元素x加入到集合之中 int DelMember const Type& x ) 把集合中成员x删去 Set & operator=( Set & right ) ∥复制集合 right到this Set & operator + Set & right ) 集合this与集合 right的并 Set & operator * Set & right ) ∥集合this与集合 right的交 Set& operator-(Set& right);∥求集合this与集合 right的差 t Contains( const Type&x ) /判ⅹ是否集合的成员 nt operator == Set & right ) ∥判集合this与集合 right相等 Type& Min ( ∥返回集合中的最小元素的值 (I)operator+( template Set & Set & right)t ∥求集合this与集合 right的并,计算结果通过临时集合temp返回,this集合与rght集合不变。 SetNode pb=right. first->link right集合的链扫描指针 SetNode "pa, *pc; ∥this集合的链扫描指针和结果链的存放指针 Set temp: firs while( pa I= NULL)t ∥首先把集合this的所有元素复制到结果链 pc->link new SetNode( pa->data ); pa->ink: pc=pc->link while( pb I= NULL ) ∥将集合rght中元素一个个拿出到this中查重 while( pa I=NULL & pa->data I=pb->data )pa=pa->link if pa== NULL) ∥在集合this中未出现,链入到结果链 i pc->link= new SetNode( pa->data ) pc= pc->link; 1 b= pb->link return temp: template Set & Set : operator*( Set & right )i ∥求集合this与集合 right的交,计算结果通过临时集合temp返回,this集合与rght集合不变。 SetNode *pb= rightfirst->link ght集合的链扫描指针 Set temp SetNode "pc=temp. first; ∥结果链的存放指针 while( pb I= NULL)t ∥将集合rght中元素一个个拿出到this中查重 SetNode"pa=first-> ∥this集合的链扫描指针 while( pa I=NULL ) if( pa->data=pb->data ∥两集合公有的元素,插入到结果链
第 7 章 集合与搜索 83 int AddMember ( const Type& x ); //把新元素 x 加入到集合之中 int DelMember ( const Type& x ); //把集合中成员 x 删去 Set & operator = ( Set & right ); //复制集合 right 到 this。 Set & operator + ( Set & right ); //求集合 this 与集合 right 的并 Set & operator * ( Set & right ); //求集合 this 与集合 right 的交 Set & operator - ( Set & right ); //求集合 this 与集合 right 的差 int Contains ( const Type& x ); //判 x 是否集合的成员 int operator == ( Set & right ); //判集合 this 与集合 right 相等 Type& Min ( ); //返回集合中的最小元素的值 } (1) operator + ( ) template Set & Set :: operator + ( Set & right ) { //求集合 this 与集合 right 的并, 计算结果通过临时集合 temp 返回,this 集合与 right 集合不变。 SetNode *pb = right.first->link; //right 集合的链扫描指针 SetNode *pa, *pc; //this 集合的链扫描指针和结果链的存放指针 Set temp; pa = first->link; pc = temp.first; while ( pa != NULL ) { //首先把集合 this 的所有元素复制到结果链 pc->link = new SetNode ( pa->data ) ; pa = pa->link; pc = pc->link; } while ( pb != NULL ) { //将集合 right 中元素一个个拿出到 this 中查重 pa = first->link; while ( pa != NULL && pa->data != pb->data ) pa = pa->link; if ( pa == NULL ) //在集合 this 中未出现, 链入到结果链 { pc->link = new SetNode ( pa->data ); pc = pc->link; } pb = pb->link; } pc->link = NULL; last = pc; //链表收尾 return temp; } (2) operator * ( ) template Set & Set :: operator * ( Set & right ) { //求集合 this 与集合 right 的交, 计算结果通过临时集合 temp 返回,this 集合与 right 集合不变。 SetNode *pb = right.first->link; //right 集合的链扫描指针 Set temp; SetNode *pc = temp.first; //结果链的存放指针 while ( pb != NULL ) { //将集合 right 中元素一个个拿出到 this 中查重 SetNode *pa = first->link; //this 集合的链扫描指针 while ( pa != NULL ) { if ( pa->data == pb->data ) //两集合公有的元素, 插入到结果链
第7章集合与搜索 i pc-link= new SetNode( pa->data ) pc->link= NULL; last=pc; ∥置链尾指针 return temp: ()operator-(), template Set & Set : operator-( Set & right )i ∥求集合this与集合 right的差,计算结果通过临时集合temp返回,this集合与 right集合不变 SetNode *pa= first-> link; /this集合的链扫描指针 Set temp: SetNode *pc= temp->first ∥结果链的存放指针 while( pa I= NULL )t ∥将集合this中元素一个个拿出到 right中查重 SetNode *pb=right. first->link; rght集合的链扫描指针 while( pb I= nulL & pa->data I=pb->data pb= pb-> link if pb== NULL) 此this中的元素在 right中未找到,插入 i pc->link= new SetNode ( pa->data ) pc=pc->link; 3 ∥链表收尾 x ∥测试函数:如果x是集合的成员,则函数返回1,否则返回0。 SetNode*temp= first->link; ∥链的扫描指针 while( temp I= NULL & temp->data I=x )temp =temp-> link ∥循链搜索 ∥找到,返回 else return 0: ∥未找到,返回0 (5)AddMember(x) template int Set : AddMember( const Type&x)( ∥把新元素x加入到集合之中。若集合中已有此元素,则函数返回0,否则函数返回1 ∥temp是扫描指针 while( temp I= NULL & temp->data I=x)temp=temp->link /循链扫描 if( temp I=NULL )return 0; ∥集合中已有此元素,不加
第 7 章 集合与搜索 84 { pc->link = new SetNode ( pa->data ); pc = pc->link; } pa = pa->link; } pb = pb->link; } pc->link = NULL; last = pc; //置链尾指针 return temp; } (3) operator - ( ), template Set & Set :: operator - ( Set & right ) { //求集合 this 与集合 right 的差, 计算结果通过临时集合 temp 返回,this 集合与 right 集合不变。 SetNode *pa = first->link; //this 集合的链扫描指针 Set temp; SetNode *pc = temp->first; //结果链的存放指针 while ( pa != NULL ) { //将集合 this 中元素一个个拿出到 right 中查重 SetNode *pb = right.first->link; //right 集合的链扫描指针 while ( pb != NULL && pa->data != pb->data ) pb = pb->link; if ( pb == NULL ) //此 this 中的元素在 right 中未找到, 插入 { pc->link = new SetNode ( pa->data ); pc = pc->link; } pa = pa->link; } pc->link = NULL; last = pc; //链表收尾 return temp; } (4) Contains(x) template int Set :: Contains ( const Type& x ) { //测试函数: 如果 x 是集合的成员, 则函数返回 1, 否则返回 0。 SetNode * temp = first->link; //链的扫描指针 while ( temp != NULL && temp->data != x ) temp = temp->link; //循链搜索 if ( temp != NULL ) return 1; //找到, 返回 1 else return 0; //未找到, 返回 0 } (5) AddMember (x) template int Set :: AddMember ( const Type& x ) { //把新元素 x 加入到集合之中。若集合中已有此元素, 则函数返回 0, 否则函数返回 1。 SetNode * temp = first->link; // temp 是扫描指针 while ( temp != NULL && temp->data != x ) temp = temp->link; /循链扫描 if ( temp != NULL ) return 0; //集合中已有此元素, 不加
第7章集合与搜索 last=last->link new SetNode(x); ∥则,创建数据值为x的新结点,链入 template int Set : DelMember( const Type&x)t ∥把集合中成员x删去。若集合不空且元素x在集合中,则函数返回1,否则返回0。 while( p I= NULL) ∥重新链接 if (p==last )last=q ∥删去链尾结点时改链尾指针 ∥删除含x结点 ∥循链扫描 ∥集合中无此元素 emplate SetNode. Set : Min()i ∥在集合中寻找值最小的成员并返回它的位置 SetNode*p=first->link, *q= first-> link 是检测指针,q是记忆最小指针 while(pI= NULL)t data)q=p: ∥找到更小的,让q记忆它 ∥继续检测 7-8设有序顺序表中的元素依次为017,094,154,170,275,503,509,512553,612,677,765,897,908。试 画出对其进行折半搜索时的二叉搜索树,并计算搜索成功的平均搜索长度和搜索不成功的平均搜索长 度 【解答】 路 C 45 1+2
第 7 章 集合与搜索 85 last = last->link = new SetNode (x); //否则, 创建数据值为 x 的新结点, 链入 return 1; } (6) DelMember (x) template int Set :: DelMember ( const Type& x ) { //把集合中成员 x 删去。若集合不空且元素 x 在集合中, 则函数返回 1, 否则返回 0。 SetNode * p = first->link, *q = first; while ( p != NULL ) { if ( p->data == x ) { //找到 q->link = p->link; //重新链接 if ( p == last ) last = q; //删去链尾结点时改链尾指针 delete p; return 1; //删除含 x 结点 } else { q = p; p = p->link; } //循链扫描 return 0; //集合中无此元素 } (7) Min ( ) template SetNode * Set :: Min ( ) { //在集合中寻找值最小的成员并返回它的位置。 SetNode * p = first->link, *q = first->link; //p 是检测指针, q 是记忆最小指针 while ( p != NULL ) { if ( p->data data ) q = p; //找到更小的, 让 q 记忆它 p = p->link; //继续检测 } return q; } 7-8 设有序顺序表中的元素依次为 017, 094, 154, 170, 275, 503, 509, 512, 553, 612, 677, 765, 897, 908。试 画出对其进行折半搜索时的二叉搜索树, 并计算搜索成功的平均搜索长度和搜索不成功的平均搜索长 度。 【解答】 509 154 677 017 275 553 897 094 170 503 512 612 765 908 14 45 (1 2*2 3*4 4*7) 14 1 C 14 1 ASL 1 4 i 1 succ = i = + + + = =
第7章集合与搜索 7-9若对有n个元素的有序顺序表和无序顺序表进行顺序搜索,试就下列三种情况分别讨论两者在等搜 索概率时的平均搜索长度是否相同? AS15之C1=1(3*1+4+14)-15 (1)搜索失败 (2)搜索成功,且表中只有一个关键码等于给定值k的对象; (3)搜索成功,且表中有若干个关键码等于给定值k的对象,要求一次搜索找出所有对象。 【解答】 (1)不同。因为有序顺序表搜索到其关键码比要查找值大的对象时就停止搜索,报告失败信息,不 必搜索到表尾;而无序顺序表必须搜索到表尾才能断定搜索失败。 (2)相同。搜索到表中对象的关键码等于给定值时就停止搜索,报告成功信息。 (3)不同。有序顺序表中关键码相等的对象相继排列在一起,只要搜索到第一个就可以连续搜索到 其它关键码相同的对象。而无序顺序表必须搜索全部表中对象才能确定相同关键码的对象都找了出来 所需时间就不相同了 前两问可做定量分析。第三问推导出的公式比较复杂,不再进一步讨论 7-10假定用一个循环链表来实现一个有序表,并让指针head指向具有最小关键码的结点。指针 current 初始时等于head,每次搜索后指向当前检索的结点,但如果搜索不成功则 current重置为head。试编写 个函数 search(head, current key)实现这种搜索。当搜索成功时函数返回被检索的结点地址,若搜索不 成功则函数返回空指针0。请说明如何保持指针 current以减少搜索时的平均搜索长度。 【解答】 current M-=1020[3045o6o 相应的搜索函数可以定义为链表及链表结点类的友元函数,直接使用链表及链表结点类的私有数据 成员 template ListNode * Search( ListNode* head, ListNode*& current, Type key )t ListNode *p, *9: if( key data==key )i =p: return p; 3 ∥找到,返回结点地址 Ise (current=head; return NULL; 3 ∥未找到,返回空指针 7-11考虑用双向链表来实现一个有序表,使得能在这个表中进行正向和反向搜索。若指针p总是指向最 后成功搜索到的结点,搜索可以从p指示的结点出发沿任一方向进行。试根据这种情况编写一个函数 search(head,p,key),检索具有关键码key的结点,并相应地修改p。最后请给出搜索成功和搜索不成功 时的平均搜索长度。 【解答】
第 7 章 集合与搜索 86 7-9 若对有 n 个元素的有序顺序表和无序顺序表进行顺序搜索, 试就下列三种情况分别讨论两者在等搜 索概率时的平均搜索长度是否相同? (1) 搜索失败; (2) 搜索成功, 且表中只有一个关键码等于给定值 k 的对象; (3) 搜索成功, 且表中有若干个关键码等于给定值 k 的对象, 要求一次搜索找出所有对象。 【解答】 (1) 不同。因为有序顺序表搜索到其关键码比要查找值大的对象时就停止搜索,报告失败信息,不 必搜索到表尾;而无序顺序表必须搜索到表尾才能断定搜索失败。 (2) 相同。搜索到表中对象的关键码等于给定值时就停止搜索,报告成功信息。 (3) 不同。有序顺序表中关键码相等的对象相继排列在一起,只要搜索到第一个就可以连续搜索到 其它关键码相同的对象。而无序顺序表必须搜索全部表中对象才能确定相同关键码的对象都找了出来, 所需时间就不相同了。 前两问可做定量分析。第三问推导出的公式比较复杂,不再进一步讨论。 7-10 假定用一个循环链表来实现一个有序表,并让指针 head 指向具有最小关键码的结点。指针 current 初始时等于 head,每次搜索后指向当前检索的结点,但如果搜索不成功则 current 重置为 head。试编写 一个函数 search(head, current, key)实现这种搜索。当搜索成功时函数返回被检索的结点地址,若搜索不 成功则函数返回空指针 0。请说明如何保持指针 current 以减少搜索时的平均搜索长度。 【解答】 相应的搜索函数可以定义为链表及链表结点类的友元函数,直接使用链表及链表结点类的私有数据 成员。 template ListNode * Search ( ListNode * head, ListNode *& current, Type key ) { ListNode * p, * q; if ( key data link; //循链搜索其值等于 key 的结点 if ( p->data == key ) { current = p; return p; } //找到, 返回结点地址 else { current = head; return NULL; } //未找到, 返回空指针 } 7-11 考虑用双向链表来实现一个有序表,使得能在这个表中进行正向和反向搜索。若指针 p 总是指向最 后成功搜索到的结点,搜索可以从 p 指示的结点出发沿任一方向进行。试根据这种情况编写一个函数 search(head, p, key),检索具有关键码 key 的结点,并相应地修改 p。最后请给出搜索成功和搜索不成功 时的平均搜索长度。 【解答】 15 59 (3*1 4*14) 15 1 C 15 1 ASL 15 i 0 ' unsucc = i = + = = head current 10 20 30 40 50 60 p
第7章集合与搜索 d-2-刘一刘叫卡日 template *Search( DbIListNode*head, DblListNode *& p, Type key)i ∥在以head为表头的双向有序链表中搜索具有值key的结点。算法可视为双向链表类和双向链表结点类的友元 函数。若给定值key大于结点p中的数据,从p向右正向搜索,否则,从p向左反向搜索 if key data )i while( qI= NUlL & q->data >key )q=q-> ILink; 3 ∥反向搜索 else while(ql=NULL&& q->data key )q=q->rLink; j 向搜索 if (q= null & q->data =key)(p=q: return p:) ∥搜索成功 Ise return nulls 如果指针p处于第i个结点(i=1,2,…,n),它左边有i-1个结点,右边有n个结点。找到左边第 1号结点比较2次,找到第ⅰ-2号结点比较3次,…,找到第1号结点比较i次,一般地,找到左边第 k个结点比较i-k+1次(k=1,2,…,i-1)找到右边第计1号结点比较2次,找到第计+2号结点比较3次 找到第n号结点比较ni计+1次,一般地,找到右边第k个结点比较k-计+1次(k=计+1,i+2,…,n)。因此 n+3 i2-i-i*n 1+∑(-k+1)+∑(-i+1)|n= n(n+3) +i2-i-i*n k=i+1 当指针处于第i个结点时的搜索成功的平均数据比较次数为 般地,搜索成功的平均数据比较次数为 n+3i2-i-i*n)n2+3n-1 3n 如果指针p处于第i个结点(i=1,2,…,n),它左边有i个不成功的位置,右边有n+1个不成功的位置。 -k)+2(k-i+1)/(n+1) n*(n+3) +i2-i-i*n+1/(n+1) k=0 般地,搜索不成功的平均数据比较次数为 NS*+5)+211n+1=22+7+6 7-12在一棵表示有序集S的二叉搜索树中,任意一条从根到叶结点的路径将S分为3部分:在该路径 左边结点中的元素组成的集合S1:在该路径上的结点中的元素组成的集合S:在该路径右边结点中的元 素组成的集合S3。S=S1∪S2∪S3。若对于任意的a∈S1,b∈S2,c∈S3,是否总有a≤b≤c?为什么? 【解答】 答案是否定的。举个反例:看下图粗线所示的路径
第 7 章 集合与搜索 87 40 template DblListNode * Search ( DblListNode * head, DblListNode *& p, Type key ) { //在以 head 为表头的双向有序链表中搜索具有值 key 的结点。算法可视为双向链表类和双向链表结点类的友元 //函数。若给定值 key 大于结点 p 中的数据, 从 p 向右正向搜索, 否则, 从 p 向左反向搜索。 DblListNode * q = p; if ( key data ){ while ( q != NULL && q->data > key ) q = q-> lLink; } //反向搜索 else { while ( q != NULL && q->data rLink; } //正向搜索 if ( q != NULL && q->data == key ) { p = q; return p; } //搜索成功 else return NULL; } 如果指针 p 处于第 i 个结点(i = 1, 2, …, n),它左边有 i-1 个结点,右边有 n-i 个结点。找到左边第 i-1 号结点比较 2 次,找到第 i-2 号结点比较 3 次,…,找到第 1 号结点比较 i 次,一般地,找到左边第 k个结点比较i-k+1次(k = 1, 2, …, i-1)。找到右边第i+1号结点比较2次,找到第i+2号结点比较3次,…, 找到第 n 号结点比较 n-i+1 次,一般地,找到右边第 k 个结点比较 k-i+1 次(k = i+1, i+2, …, n)。因此, 当指针处于第 i 个结点时的搜索成功的平均数据比较次数为 一般地,搜索成功的平均数据比较次数为 如果指针 p 处于第 i 个结点(i = 1, 2, …, n),它左边有 i 个不成功的位置,右边有 n-i+1 个不成功的位置。 一般地,搜索不成功的平均数据比较次数为 7-12 在一棵表示有序集 S 的二叉搜索树中,任意一条从根到叶结点的路径将 S 分为 3 部分:在该路径 左边结点中的元素组成的集合 S1;在该路径上的结点中的元素组成的集合 S2;在该路径右边结点中的元 素组成的集合 S3。S = S1 S2 S3。若对于任意的 a S1, b S2, c S3, 是否总有 a b c?为什么? 【解答】 答案是否定的。举个反例:看下图粗线所示的路径 head ∧ 10 20 30 40 50 60 70 ∧ 45 30 15 25 70 65 35 50 n i i i*n 2 n 3 i i i*n n 2 n *(n 3) 1 (i k 1) (k i 1) n 2 2 n k i 1 i 1 k 1 − − + + = + − − + = + − + + − + = + − = 3n n 3n 1 n i i i*n 2 n 3 n 1 ASL n 2 i 1 2 succ + − = − − + + = = i i i*n 1 (n 1) 2 n *(n 3) (i k) (k i 1) (n 1) 2 n k i i 1 k 0 + + − − + + + = − + − + = − = = + + + = + − − + + + = n i 0 2 2 unsucc 2 n 1 2n 7n 6 i i i*n 1 2 n*(n 3) (n 1) 1 ASL
第7章集合与搜索 Sl={15},S2={25,30,35,45},S3={40,50,65,70} 40∈S3,b=45∈S2,b≤c不成立。 7-13请给出下列操作序列运算的结果: Union(1,2), Union(3,4), Union(3,5), Union(,7), Union(3,6) Union(8, 9), Union(1, 8), Union(3, 10), Union(3, 11),Union( 3, 12), Union(3, 13), Union(14, 15), Union(16, 17) Union(14,16), Union(1,3), Union(1,14),要求 (1)以任意方式执行 Union (2)根据树的高度执行Unon (3)根据树中结点个数执行 Union 【解答】 (1)对于 union(i,j),以i作为j的双亲 15 ○④③⊙○①@○ (2)按i和j为根的树的高度实现 union(i,j),高度大者为高度小者的双亲; 1516 ○○○○○@@@○ (3)按i和j为根的树的结点个数实现 union(i,j),结点个数大者为结点个数小者的双亲 ⑦8①
第 7 章 集合与搜索 88 S1 = { 15 }, S2 = { 25, 30, 35, 45 }, S3 = { 40, 50, 65, 70 } c = 40 S3,b = 45 S2,b c 不成立。 7-13 请给出下列操作序列运算的结果:Union(1, 2), Union(3, 4), Union(3, 5), Union(1, 7), Union(3, 6), Union(8, 9), Union(1, 8), Union(3, 10), Union(3, 11), Union(3, 12), Union(3, 13), Union(14, 15), Union(16, 17), Union(14, 16), Union(1, 3), Union(1, 14),要求 (1) 以任意方式执行 Union; (2) 根据树的高度执行 Union; (3) 根据树中结点个数执行 Union。 【解答】 (1) 对于 union(i, j),以 i 作为 j 的双亲 (2) 按 i 和 j 为根的树的高度实现 union(i, j),高度大者为高度小者的双亲; (3) 按 i 和 j 为根的树的结点个数实现 union(i, j),结点个数大者为结点个数小者的双亲。 1 2 7 8 9 4 5 6 10 11 12 13 3 14 15 16 0 3 15 16 0 1 2 7 8 9 4 5 6 10 11 12 13 14 4 5 6 10 11 12 13 3 1 2 7 8 9 15 16 0 14
第7章集合与搜索 7-14有n个结点的二叉搜索树具有多少种不同形态? 【解答】 n+1 7-15设有一个输入数据的序列是{46,25,78,62,12,37,70,29},试画出从空树起,逐个输入各个数据 而生成的二叉搜索树。 【解答】 空树加46 加12 7-16设有一个标识符序列{else, public, return, template},p=0.05,p=0.2,p=0.1,p=0.05,q=0.2,q=0.1, 触9=0.05,94=0.05,计算WC和R可,构造最优二叉搜索树。 将标识符序列简化为{e,p,r,t},并将各个搜索概率值化整,有 PI 2 (1)首先构造只有一个内结点的最优二叉搜索树
第 7 章 集合与搜索 89 7-14 有 n 个结点的二叉搜索树具有多少种不同形态? 【解答】 7-15 设有一个输入数据的序列是 { 46, 25, 78, 62, 12, 37, 70, 29 }, 试画出从空树起,逐个输入各个数据 而生成的二叉搜索树。 【解答】 7-16 设有一个标识符序列{else, public, return, template},p1=0.05, p2=0.2, p3=0.1, p4=0.05, q0=0.2, q1=0.1, q2=0.2, q3=0.05, q4=0.05,计算 W[i][j]、C[i][j]和 R[i][j],构造最优二叉搜索树。 【解答】 将标识符序列简化为 { e, p, r, t },并将各个搜索概率值化整,有 e p r t p1 = 1 p2 = 4 p3 = 2 p4 = 1 q0 = 4 q1 = 2 q2 = 4 q3 = 1 q4 = 1 (1) 首先构造只有一个内结点的最优二叉搜索树: C n 2n n 1 1 + p1 p2 p3 p4 q0 q1 q1 q2 q2 q3 q3 q4 46 46 25 46 25 78 46 25 78 62 46 25 78 12 62 46 25 78 12 37 62 46 25 78 12 37 62 70 46 25 78 12 37 62 29 70 空树 加46 加25 加 78 加 62 加 12 加 37 加 70 加 29