顺序表表 2-1设n个人围坐在一个圆桌周围,现在从第s个人开始报数,数到第m个人,让他出局 然后从出局的下一个人重新开始报数,数到第m个人,再让他出局,……,如此反复直到 所有的人全部出局为止。下面要解决的 Josephus问题是:对于任意给定的n,s和m,求 出这n个人的出局序列。请以n=9,s=1,m=5为例,人工模拟 Josephus的求解过 程以求得问题的解。 解答】 出局人的顺序为5,1 3,6,9,2,8 2-2试编写一个求解 Josephus问题的函数。用整数序列1,2,3,……,n表示顺序围坐 在圆桌周围的人,并采用数组表示作为求解过程中使用的数据结构。然后使用n=9,s= l,m=5,以及n=9,s=1,m=0,或者n=9,s=1,m=10作为输入数据,检 查你的程序的正确性和健壮性。最后分析所完成算法的时间复杂度 【解答】函数源程序清单如下 id Josephus( int A[], int int 0){ cout<"m=0是无效的参数!"<<end1; for(i=0;i<n;i艹)A[i]=i+1;/*初始化,执行n次* /*报名起始位置* for(k /*逐个出局,执行n-1次*/ if i=(i+m-1)%k; /*寻找出局位置*/ if k-1){ tmp Ali] /*出局者交换到第k-1位置*/ for(j=i;j<k-l: j++)alj] ALj+1]: for(k=0;k<n/2;k++) /*全部逆置,得到出局序列*/ tmp = A[k]: A[k]=A[n-k+1]: A[n-k+1]=tmp; 9
5 顺序表表 2-1 设 n 个人围坐在一个圆桌周围,现在从第 s 个人开始报数,数到第 m 个人,让他出局; 然后从出局的下一个人重新开始报数,数到第 m 个人,再让他出局,……,如此反复直到 所有的人全部出局为止。下面要解决的 Josephus 问题是:对于任意给定的 n, s 和 m,求 出这 n 个人的出局序列。请以 n = 9, s = 1, m = 5 为例,人工模拟 Josephus 的求解过 程以求得问题的解。 【解答】 出局人的顺序为 5, 1, 7, 4, 3, 6, 9, 2, 8。 2-2 试编写一个求解 Josephus 问题的函数。用整数序列 1, 2, 3, ……, n 表示顺序围坐 在圆桌周围的人,并采用数组表示作为求解过程中使用的数据结构。然后使用 n = 9, s = 1, m = 5,以及 n = 9, s = 1, m = 0,或者 n = 9, s = 1, m = 10 作为输入数据,检 查你的程序的正确性和健壮性。最后分析所完成算法的时间复杂度。 【解答】函数源程序清单如下: void Josephus( int A[ ], int n, s, m ) { int i, j, k, tmp; if ( m == 0 ) { cout 1; i-- ) { /*逐个出局,执行 n-1 次*/ if ( i == k ) i = 0; i = ( i + m - 1 ) % k; /*寻找出局位置*/ if ( i != k-1 ) { tmp = A[i]; /*出局者交换到第 k-1 位置*/ for ( j = i; j < k-1; j++ ) A[j] = A[j+1]; A[k-1] = tmp; } } for ( k = 0; k < n / 2; k++ ) { /*全部逆置, 得到出局序列*/ tmp = A[k]; A[k] = A[n-k+1]; A[n-k+1] = tmp; } } 例:n = 9, s = 1, m = 5
6789第5人出局,i=4 5第1人出局,i=0 kkkk 8915第7人出局,i=4 689715第4人出局,i 第3人出局,i=1 k =三 第6人出局,i=1 第9人出局,i=2 第2人出局,i=0 第8人出局,i=0 逆置 最终出局顺序 例:n=9, 报错信息m=0是无效的参数 例 9, 「23456789第1人出局,i=0 k=8 第3人出局,i=1 第6人出局 k=6 第2人出局,i=0 第9人出局,i=4 333334 第5人出局,i=1 第7人出局,i=1 第4人出局,i=0 第8人出局,i=0 逆置 3|629 最终出局顺序 当m=1时,时间代价最大。达到(n-1)+(n-2)+…+1=n(n-1)/2≈0(n2)。 2-3设有一个线性表(eo,en,…,en2,en-)存放在一个一维数组A[ arraySize]中的前n 个数组元素位置。请编写一个函数将这个线性表原地逆置,即将数组的前n个原址内容置 换为(en,en2 【解答】 templatevoid inverse( Type a[ ], int n T ype tm for(inti=0;i<=(n-1)/2;i++) tmp Ali]: A[i] A[n-i-1]: A[n-i-1= tmp 2-4顺序表的插入和删除要求仍然保持各个元素原来的次序。设在等概率情形下,对有 127个元素的顺序表进行插入,平均需要移动多少个元素?删除一个元素,又平均需要移 动多少个元素? 【解答 若设顺序表中已有n=last+1个元素,last是顺序表的数据成员,表明最后表项的 位置。又设插入或删除表中各个元素的概率相等,则在插入时因有n+1个插入位置(可以 AMN=I 1)=n(n-1)+(m-2)+…+1+0)=1-=n-1
6 0 1 2 3 4 5 6 7 8 k = 9 1 2 3 4 5 6 7 8 9 第 5 人出局, i = 4 k = 8 1 2 3 4 6 7 8 9 5 第 1 人出局, i = 0 k = 7 2 3 4 6 7 8 9 1 5 第 7 人出局, i = 4 k = 6 2 3 4 6 8 9 7 1 5 第 4 人出局, i = 2 k = 5 2 3 6 8 9 4 7 1 5 第 3 人出局, i = 1 k = 4 2 6 8 9 3 4 7 1 5 第 6 人出局, i = 1 k = 3 2 8 9 6 3 4 7 1 5 第 9 人出局, i = 2 k = 2 2 8 9 6 3 4 7 1 5 第 2 人出局, i = 0 8 2 9 6 3 4 7 1 5 第 8 人出局, i = 0 逆置 5 1 7 4 3 6 9 2 8 最终出局顺序 例:n = 9, s = 1, m = 0 报错信息 m = 0 是无效的参数! 例:n = 9, s = 1, m = 10 0 1 2 3 4 5 6 7 8 k = 9 1 2 3 4 5 6 7 8 9 第 1 人出局, i = 0 k = 8 2 3 4 5 6 7 8 9 1 第 3 人出局, i = 1 k = 7 2 4 5 6 7 8 9 3 1 第 6 人出局, i = 3 k = 6 2 4 5 7 8 9 6 3 1 第 2 人出局, i = 0 k = 5 4 5 7 8 9 2 6 3 1 第 9 人出局, i = 4 k = 4 4 5 7 8 9 2 6 3 1 第 5 人出局, i = 1 k = 3 4 7 8 5 9 2 6 3 1 第 7 人出局, i = 1 k = 2 4 8 7 5 9 2 6 3 1 第 4 人出局, i = 0 8 4 7 5 9 2 6 3 1 第 8 人出局, i = 0 逆置 1 3 6 2 9 5 7 4 8 最终出局顺序 当 m = 1 时,时间代价最大。达到( n-1 ) + ( n-2 ) + ∙∙∙∙∙∙ + 1 = n(n-1)/2 O(n2 )。 2-3 设有一个线性表 (e0, e1 , …, en-2, en-1) 存放在一个一维数组 A[arraySize]中的前 n 个数组元素位置。请编写一个函数将这个线性表原地逆置,即将数组的前 n 个原址内容置 换为 (en-1, en-2, …, e1, e0)。 【解答】 template void inverse ( Type A[ ], int n ) { Type tmp; for ( int i = 0; i <= ( n-1 ) / 2; i++ ) { tmp = A[i]; A[i] = A[n-i-1]; A[n-i-1] = tmp; } } 2-4 顺序表的插入和删除要求仍然保持各个元素原来的次序。设在等概率情形下, 对有 127 个元素的顺序表进行插入, 平均需要移动多少个元素? 删除一个元素, 又平均需要移 动多少个元素? 【解答】 若设顺序表中已有 n = last+1 个元素,last 是顺序表的数据成员,表明最后表项的 位置。又设插入或删除表中各个元素的概率相等,则在插入时因有 n+1 个插入位置(可以 − = − = − = − − = − + − + + + = n 1 i 0 2 n 1 2 (n 1)n n 1 ((n 1) (n 2) 1 0) n 1 (n i 1) n 1 AMN
在表中最后一个表项后面追加),每个元素位置插入的概率为1/(n+1),但在删除时只能 在已有n个表项范围内删除,所以每个元素位置删除的概率为1/n 插入时平均移动元素个数AMN( Averagy Moving Number)为 删除时平均移动元素个数AMN为 2-5利用顺序表的操作,实现以下的函数。 (1)从顺序表中删除具有最小值的元素并由函数返回被删元素的值。空出的位置由最 后一个元素填补,若顺序表为空则显示出错信息并退出运行。 (2)从顺序表中删除第ⅰ个元素并由函数返回被删元素的值。如果i不合理或顺序表 为空则显示出错信息并退出运行。 (3)向顺序表中第i个位置插入一个新的元素x。如果i不合理则显示出错信息并退 出运行。 (4)从顺序表中删除具有给定值x的所有元素。 (5)从顺序表中删除其值在给定值s与t之间(要求s小于t)的所有元素,如果s 或t不合理或顺序表为空则显示出错信息并退出运行。 (6)从有序顺序表中删除其值在给定值s与t之间(要求s小于t)的所有元素,如 果s或t不合理或顺序表为空则显示出错信息并退出运行。 (7)将两个有序顺序表合并成一个新的有序顺序表并由函数返回结果顺序表 (8)从顺序表中删除所有其值重复的元素,使表中所有元素的值均不相同。 【解答】 (1)实现删除具有最小值元素的函数如下 templateType Seqlist:: DelMin() if( last ==-1 //表空,中止操作返回 cerr:: De lNo#i( int i if last last)//表空,或i不合理,中止操作返回 cerr<<" List is Empty or Parameter is out range! "< endl; exit(1) Type temp data[i] //暂存第i个元素的值 for(intj=i;j<last;j+)//空出位置由后续元素顺次填补 datalj] data[j+1] //表最后元素位置减1 turn temp: (3)实现向第i个位置插入一个新的元素x的函数如下(设第i个元素在data[i]
7 在表中最后一个表项后面追加),每个元素位置插入的概率为 1/(n+1),但在删除时只能 在已有 n 个表项范围内删除,所以每个元素位置删除的概率为 1/n。 插入时平均移动元素个数 AMN(Averagy Moving Number )为 删除时平均移动元素个数 AMN 为 2-5 利用顺序表的操作,实现以下的函数。 (1) 从顺序表中删除具有最小值的元素并由函数返回被删元素的值。空出的位置由最 后一个元素填补,若顺序表为空则显示出错信息并退出运行。 (2) 从顺序表中删除第 i 个元素并由函数返回被删元素的值。如果 i 不合理或顺序表 为空则显示出错信息并退出运行。 (3) 向顺序表中第 i 个位置插入一个新的元素 x。如果 i 不合理则显示出错信息并退 出运行。 (4) 从顺序表中删除具有给定值 x 的所有元素。 (5) 从顺序表中删除其值在给定值 s 与 t 之间(要求 s 小于 t)的所有元素,如果 s 或 t 不合理或顺序表为空则显示出错信息并退出运行。 (6) 从有序顺序表中删除其值在给定值 s 与 t 之间(要求 s 小于 t)的所有元素,如 果 s 或 t 不合理或顺序表为空则显示出错信息并退出运行。 (7) 将两个有序顺序表合并成一个新的有序顺序表并由函数返回结果顺序表。 (8) 从顺序表中删除所有其值重复的元素,使表中所有元素的值均不相同。 【解答】 (1) 实现删除具有最小值元素的函数如下: template Type SeqList :: DelMin ( ) { if ( last == -1 ) //表空, 中止操作返回 { cerr Type SeqList :: DelNo#i ( int i ) { if ( last == -1 || i last ) //表空, 或 i 不合理, 中止操作返回 { cerr << “ List is Empty or Parameter is out range! ” << endl; exit(1); } Type temp = data[i]; //暂存第 i 个元素的值 for ( int j = i; j < last; j++ ) //空出位置由后续元素顺次填补 data[j] = data[j+1]; last--; //表最后元素位置减 1 return temp; } (3) 实现向第 i 个位置插入一个新的元素 x 的函数如下(设第 i 个元素在 data[i]
0,1,,last): templatevoid Seqlist:: Ins No#i int i, Type&x)i //表满或参数i不合理,中止操作返回 if last = MaxSize-1 0 last+l i cerr=i:j-) data[计+1]=data[j; datai] =X //插入 last+ //表最后元素位置加 (4)从顺序表中删除具有给定值x的所有元素。 templatevoid Seqlist:: Delvalue( Type&x)i int i =0, j: while(ivoid SeqList:: DelNo#sto#t( Type& s, Type&t) if( last ==-1 i cerr =s&& data[i]void SeqList:: DelNo#sto#t1( Type& s, Type& t)[ f la cerr =s)break //退出循环时,i指向该元素 if (i t) break //退出循环时,ij指向该元素 //删除满足条件的元素,后续元素前移
8 i=0,1,,last): template void SeqList :: InsNo#i ( int i, Type& x ) { //表满或参数 i 不合理, 中止操作返回 if ( last == MaxSize-1|| i last+1 ) { cerr = i; j-- ) data[j+1] = data[j]; data[i] = x; //插入 last++; //表最后元素位置加 1 } (4) 从顺序表中删除具有给定值 x 的所有元素。 template void SeqList :: DelValue ( Type& x ) { int i = 0, j; while ( i void SeqList :: DelNo#sto#t ( Type& s, Type& t ) { if ( last == -1 || s >= t ) { cerr = s && data[i] void SeqList :: DelNo#sto#t1 ( Type& s, Type& t ) { if ( last == -1 || s >= t ) { cerr = s ) break; //退出循环时, i 指向该元素 if ( i t 的第一个元素 if ( data[i+j] > t ) break; //退出循环时, i+j 指向该元素 //删除满足条件的元素, 后续元素前移
t k= i+j: k Seglist& Seglist:: Merge( SeqList&A, Seqlist MaxSize I cerr void SeqList:: DelDouble()i if last ==-1) I cerr<< "List is empty! "< endl: exit(1) int i=0, j, k: Type temp ile( i ( last) //循环检测 j=i+l: temp data[i]: while(j<= last) //对于每一个i,重复检测一遍后续元素 if(temp==data[j]){//如果相等,后续元素前移 for(k=j+l: k<= last; k++)data[k-1]=datalk] last //表最后元素位置减1 //检测完data[i],检测下一个
9 for ( int k = i+j; k SeqList& SeqList :: Merge ( SeqList& A, SeqList& B ) { //合并有序顺序表 A 与 B 成为一个新的有序顺序表并由函数返回 if ( A.Length() + B.Length() > MaxSize ) { cerr void SeqList :: DelDouble ( ) { if ( last == -1 ) { cerr << “List is empty!” << endl; exit(1); } int i = 0, j, k; Type temp; while ( i <= last ) { //循环检测 j = i + 1; temp = data[i]; while ( j <= last ) { //对于每一个 i, 重复检测一遍后续元素 if ( temp == data[j] ) { //如果相等, 后续元素前移 for ( k = j+1; k <= last; k++ ) data[k-1] = data[k]; last--; //表最后元素位置减 1 } else j++; } i++; //检测完 data[i], 检测下一个 }
线性链表 单链表的结点类 lListNode class)和链表类 List class)的类定义。 template class list //前视的类定义 template class ListNode i //链表结点类的定义 friend class List //List类作为友元类定义 ListNode *link /链指针域 ListNode(): link (NULL)(] //仅初始化指针成员的构造函数 ListNode( const Type& item) data (item), link (NULL)I /初始化数据与指针成员的构造函数 ListNode* getNode const Type item, ListNode*next NULL /以item和next建立一个新结点 ListNode* getLink ()i return link; 取得结点的下一结点地址 Type getData()i return data; F //取得结点中的数据 void setLink( ListNode* next) link next 修改结点的1ink指针 void setData Type value)i data value: K //修改结点的data值 template class List /单链表类定义 ListNode *first, *current //链表的表头指针和当前元素指针 const Type& value )(first new ListNode( //构造函数 List() Make Empty ( delete first: 1 //析构函数 void Make Empty ( //将链表置为空表 int Length ()const //计算链表的长度
10 } 线性链表 单链表的结点类(ListNode class)和链表类(List class)的类定义。 template class List; //前视的类定义 template class ListNode { //链表结点类的定义 friend class List; //List 类作为友元类定义 private: Type data; //数据域 ListNode *link; //链指针域 public: ListNode ( ) : link (NULL) { } //仅初始化指针成员的构造函数 ListNode ( const Type& item ) : data (item), link (NULL) { } //初始化数据与指针成员的构造函数 ListNode * getNode ( const Type& item, ListNode *next = NULL ) //以 item 和 next 建立一个新结点 ListNode * getLink ( ) { return link; } //取得结点的下一结点地址 Type getData ( ) { return data; } //取得结点中的数据 void setLink ( ListNode * next ) { link = next; } //修改结点的 link 指针 void setData ( Type value ) { data = value; } //修改结点的 data 值 }; template class List { //单链表类定义 private: ListNode *first, *current; //链表的表头指针和当前元素指针 public: List ( const Type& value ) { first = current = new ListNode ( value ); } //构造函数 ~List ( ) { MakeEmpty ( ); delete first; } //析构函数 void MakeEmpty ( ); //将链表置为空表 int Length ( ) const; //计算链表的长度
ListNode* Find( Type value ) //搜索含数据 value的元素并成为当前元素 ListNode* Locate( int i //搜索第i个元素的地址并置为当前元素 Type GetData () //取出表中当前元素的值 int Insert( Type value //将 value插在表当前位置之后并成为当前元素 //将链表中的当前元素删去,填补者为当前元素 Listnodelink ! NULL: 1 //当前元素下一元素空否?空返回1,不空返回0 2-6线性表可用顺序表或链表存储。试问: (1)两种存储表示各有哪些主要优缺点? (2)如果有n个表同时并存,并且在处理过程中各表的长度会动态发生变化,表的总数也可能自 动改变、在此情况下,应选用哪种存储表示?为什么? (3)若表的总数基本稳定,且很少进行插入和删除,但要求以最快的速度存取表中的元素,这时 应采用哪种存储表示?为什么? 【解答】 (1)顺序存储表示是将数据元素存放于一个连续的存储空间中,实现顺序存取或(按下标)直接存 取。它的存储效率高,存取速度快。但它的空间大小一经定义,在程序整个运行期间不会发生改变, 因此,不易扩充。同时,由于在插入或删除时,为保持原有次序,平均需要移动一半(或近一半)元素, 修改效率不高 链接存储表示的存储空间一般在程序的运行过程中动态分配和释放,且只要存储器中还有空间, 就不会产生存储溢出的问题。同时在插入和删除时不需要保持数据元素原来的物理顺序,只需要保持 原来的逻辑顺序,因此不必移动数据,只需修改它们的链接指针,修改效率较高。但存取表中的数据 元素时,只能循链顺序访问,因此存取效率不高 (2)如果有n个表同时并存,并且在处理过程中各表的长度会动态发生变化,表的总数也可能自 动改变、在此情况下,应选用链接存储表示 如果采用顺序存储表示,必须在一个连续的可用空间中为这n个表分配空间。初始时因不知道哪 个表增长得快,必须平均分配空间。在程序运行过程中,有的表占用的空间增长得快,有的表占用的 空间增长得慢;有的表很快就用完了分配给它的空间,有的表才用了少量的空间,在进行元素的插入 时就必须成片地移动其他的表的空间,以空出位置进行插入;在元素删除时,为填补空白,也可能移 动许多元素。这个处理过程极其繁琐和低效。 如果采用链接存储表示,一个表的存储空间可以连续,可以不连续。表的增长通过动态存储分配 解决,只要存储器未满,就不会有表溢出的问题;表的收缩可以通过动态存储释放实现,释放的空间 还可以在以后动态分配给其他的存储申请要求,非常灵活方便。对于n个表(包括表的总数可能变化 共存的情形,处理十分简便和快捷。所以选用链接存储表示较好 (3)应采用顺序存储表示。因为顺序存储表示的存取速度快,但修改效率低。若表的总数基本稳 定,且很少进行插入和删除,但要求以最快的速度存取表中的元素,这时采用顺序存储表示较好。 2-7针对带表头结点的单链表,试编写下列函数。 (1)定位函数 Locate:在单链表中寻找第i个结点。若找到,则函数返回第i个结点的地址:若
11 ListNode * Find ( Type value ); //搜索含数据 value 的元素并成为当前元素 ListNode * Locate( int i ); //搜索第 i 个元素的地址并置为当前元素 Type * GetData ( ); //取出表中当前元素的值 int Insert ( Type value ); //将 value 插在表当前位置之后并成为当前元素 Type *Remove ( ); //将链表中的当前元素删去, 填补者为当前元素 ListNode * Firster ( ) { current = first; return first; } //当前指针定位于表头结点 Type *First ( ); //当前指针定位于表中第一个元素并返回其值 Type *Next ( ); //将当前指针进到表中下一个元素并返回其值 int NotNull ( ) { return current != NULL; } //表中当前元素空否?空返回 1, 不空返回 0 int NextNotNull ( ) { return current != NULL && current->link != NULL; } //当前元素下一元素空否?空返回 1, 不空返回 0 }; 2-6 线性表可用顺序表或链表存储。试问: (1) 两种存储表示各有哪些主要优缺点? (2) 如果有 n 个表同时并存,并且在处理过程中各表的长度会动态发生变化,表的总数也可能自 动改变、在此情况下,应选用哪种存储表示?为什么? (3) 若表的总数基本稳定,且很少进行插入和删除,但要求以最快的速度存取表中的元素,这时, 应采用哪种存储表示?为什么? 【解答】 (1) 顺序存储表示是将数据元素存放于一个连续的存储空间中,实现顺序存取或(按下标)直接存 取。它的存储效率高,存取速度快。但它的空间大小一经定义,在程序整个运行期间不会发生改变, 因此,不易扩充。同时,由于在插入或删除时,为保持原有次序,平均需要移动一半(或近一半)元素, 修改效率不高。 链接存储表示的存储空间一般在程序的运行过程中动态分配和释放,且只要存储器中还有空间, 就不会产生存储溢出的问题。同时在插入和删除时不需要保持数据元素原来的物理顺序,只需要保持 原来的逻辑顺序,因此不必移动数据,只需修改它们的链接指针,修改效率较高。但存取表中的数据 元素时,只能循链顺序访问,因此存取效率不高。 (2) 如果有 n 个表同时并存,并且在处理过程中各表的长度会动态发生变化,表的总数也可能自 动改变、在此情况下,应选用链接存储表示。 如果采用顺序存储表示,必须在一个连续的可用空间中为这 n 个表分配空间。初始时因不知道哪 个表增长得快,必须平均分配空间。在程序运行过程中,有的表占用的空间增长得快,有的表占用的 空间增长得慢;有的表很快就用完了分配给它的空间,有的表才用了少量的空间,在进行元素的插入 时就必须成片地移动其他的表的空间,以空出位置进行插入;在元素删除时,为填补空白,也可能移 动许多元素。这个处理过程极其繁琐和低效。 如果采用链接存储表示,一个表的存储空间可以连续,可以不连续。表的增长通过动态存储分配 解决,只要存储器未满,就不会有表溢出的问题;表的收缩可以通过动态存储释放实现,释放的空间 还可以在以后动态分配给其他的存储申请要求,非常灵活方便。对于 n 个表(包括表的总数可能变化) 共存的情形,处理十分简便和快捷。所以选用链接存储表示较好。 (3) 应采用顺序存储表示。因为顺序存储表示的存取速度快,但修改效率低。若表的总数基本稳 定,且很少进行插入和删除,但要求以最快的速度存取表中的元素,这时采用顺序存储表示较好。 2-7 针对带表头结点的单链表,试编写下列函数。 (1) 定位函数 Locate:在单链表中寻找第 i 个结点。若找到,则函数返回第 i 个结点的地址;若
找不到,则函数返回NULL (2)求最大值函数max:通过一趟遍历在单链表中确定值最大的结点。 (3)统计函数 number:统计单链表中具有给定值x的所有元素。 (4)建立函数 create:根据一维数组a[n]建立一个单链表,使单链表中各元素的次序与a[n]中 各元素的次序相同,要求该程序的时间复杂性为0(m)。 (5)整理函数 tidyup:在非递减有序的单链表中删除值相同的多余结点 【解答】 (1)实现定位函数的算法如下: template ListNode * List : Locate int i)i //取得单链表中第i个结点地址,i从1开始计数,i*p= first: int k = 0: /从表头结点开始检测 while(p!=NuLL&匙klink;k+;}/循环,p=NL表示链短,无第i个结点 return //若p!=NLL,则k=i,返回第i个结点地址 (2)实现求最大值的函数如下 template ListNode * List : Max ( /在单链表中进行一趟检测,找出具有最大值的结点地址,如果表空,返回指针NUL if first->link null) return NULL /空表,返回指针NULL ListNodedata >pmax->data )pmax = p /指针pmax记忆当前找到的具最大值结点 p->link //检测下一个结点 return pmax: (3)实现统计单链表中具有给定值x的所有元素的函数如下: template int List : Count Type&x) //在单链表中进行一趟检测,找出具有最大值的结点地址,如果表空,返回指针NUL ListNode * p= first-link /从第一个结点开始检测 while( p!= NULL /循环,下一个结点存在 if p>data ==x)n++ /找到一个,计数器加1 //检测下一个结点 (4)实现从一维数组A[n]建立单链表的函数如下: /根据一维数组A[n]建立一个单链表,使单链表中各元素的次序与A[n]中各元素的次序相同 /创建表头结点 p->link new ListNode<Type)( Ali] //链入一个新结点,值为A[i]
12 找不到,则函数返回 NULL。 (2) 求最大值函数 max:通过一趟遍历在单链表中确定值最大的结点。 (3) 统计函数 number:统计单链表中具有给定值 x 的所有元素。 (4) 建立函数 create:根据一维数组 a[n]建立一个单链表,使单链表中各元素的次序与 a[n]中 各元素的次序相同,要求该程序的时间复杂性为 O(n)。 (5) 整理函数 tidyup:在非递减有序的单链表中删除值相同的多余结点。 【解答】 (1) 实现定位函数的算法如下: template ListNode * List :: Locate ( int i ) { //取得单链表中第 i 个结点地址, i 从 1 开始计数, i * p = first; int k = 0; //从表头结点开始检测 while ( p != NULL && k link; k++; } //循环, p == NULL 表示链短, 无第 i 个结点 return p; //若 p != NULL, 则 k == i, 返回第 i 个结点地址 } (2) 实现求最大值的函数如下: template ListNode * List :: Max ( ) { //在单链表中进行一趟检测,找出具有最大值的结点地址, 如果表空, 返回指针 NULL if ( first->link == NULL ) return NULL; //空表, 返回指针 NULL ListNode * pmax = first->link, p = first->link->link; //假定第一个结点中数据具有最大值 while ( p != NULL ) { //循环, 下一个结点存在 if ( p->data > pmax->data ) pmax = p; //指针 pmax 记忆当前找到的具最大值结点 p = p->link; //检测下一个结点 } return pmax; } (3) 实现统计单链表中具有给定值 x 的所有元素的函数如下: template int List :: Count ( Type& x ) { //在单链表中进行一趟检测,找出具有最大值的结点地址, 如果表空, 返回指针 NULL int n = 0; ListNode * p = first->link; //从第一个结点开始检测 while ( p != NULL ) { //循环, 下一个结点存在 if ( p->data == x ) n++; //找到一个, 计数器加 1 p = p->link; //检测下一个结点 } return n; } (4) 实现从一维数组 A[n]建立单链表的函数如下: template void List :: Create ( Type A[ ], int n ) { //根据一维数组 A[n]建立一个单链表,使单链表中各元素的次序与 A[n]中各元素的次序相同 ListNode * p; first = p = new ListNode; //创建表头结点 for ( int i = 0; i link = new ListNode ( A[i] ); //链入一个新结点, 值为 A[i]
p=p-link /指针p总指向链中最后一个结点 p->link NULL 采用递归方法实现时,需要通过引用参数将已建立的单链表各个结点链接起来。为此,在递归地 扫描数组A[n]的过程中,先建立单链表的各个结点,在退出递归时将结点地址p(被调用层的形参) 带回上一层(调用层)的实参p->1ink templatevoid List:: create( Type a[l, intn, int i, ListNode*& p)t //私有函数:递归调用建立单链表 p NULL else p new ListNode( Ali] ) /建立链表的新结点 create (A, n, i+l, p->link //递归返回时p1ink中放入下层p的内容 templatevoid List: create( Type a[ l, int n //外部调用递归过程的共用函数 first current new ListNode //建立表头结点 create(A, n, 0, first->link //递归建立单链表 (5)实现在非递减有序的单链表中删除值相同的多余结点的函数如下 template void List : tidyup()[ ListNodeType>*p= first->link, temp /检测指针,初始时指向链表第一个结点 while(p!= NULL & p->link !=NULL //循环检测链表 if( p->data = p->link->data )I //若相邻结点所包含数据的值相等 temp p->first: p->link temp->link: /为删除后一个值相同的结点重新拉链 delete temp: //删除后一个值相同的结点 else p= p->link //指针p进到链表下一个结点 2-8设ha和hb分别是两个带表头结点的非递减有序单链表的表头指针,试设计一个算法,将这两个 有序链表合并成一个非递增有序的单链表。要求结果链表仍使用原来两个链表的存储空间,不另外占 用其它的存储空间。表中允许有重复的数据。 【解答】 #include te class list friend class List public ListNode ( /构造函数 ListNode( const Typet item //构造函数 private T ListNode*link:
13 p = p->link; //指针 p 总指向链中最后一个结点 } p->link = NULL; } 采用递归方法实现时,需要通过引用参数将已建立的单链表各个结点链接起来。为此,在递归地 扫描数组 A[n]的过程中,先建立单链表的各个结点,在退出递归时将结点地址 p(被调用层的形参) 带回上一层(调用层)的实参 p->link。 template void List :: create ( Type A[ ], int n, int i, ListNode *& p ) { //私有函数:递归调用建立单链表 if ( i == n ) p = NULL; else {p = new ListNode( A[i] ); //建立链表的新结点 create ( A, n, i+1, p->link ); //递归返回时 p->link 中放入下层 p 的内容 } } template void List :: create ( Type A[ ], int n ) { //外部调用递归过程的共用函数 first = current = new ListNode; //建立表头结点 create ( A, n, 0, first->link ); //递归建立单链表 } (5) 实现在非递减有序的单链表中删除值相同的多余结点的函数如下: template void List :: tidyup ( ) { ListNode * p = first->link, temp; //检测指针, 初始时指向链表第一个结点 while ( p != NULL && p->link != NULL ) //循环检测链表 if ( p->data == p->link->data ) { //若相邻结点所包含数据的值相等 temp = p->first; p->link = temp->link; //为删除后一个值相同的结点重新拉链 delete temp; //删除后一个值相同的结点 } else p = p->link; //指针 p 进到链表下一个结点 } 2-8 设 ha 和 hb 分别是两个带表头结点的非递减有序单链表的表头指针, 试设计一个算法, 将这两个 有序链表合并成一个非递增有序的单链表。要求结果链表仍使用原来两个链表的存储空间, 不另外占 用其它的存储空间。表中允许有重复的数据。 【解答】 #include template class List; template class ListNode { friend class List; public: ListNode ( ); //构造函数 ListNode ( const Type& item ); //构造函数 private: Type data; ListNode *link;
template class List i public const Type finishied /建立链表 //打印链表 void Merge( List&hb //连接链表 private ListNode*first, *last //各成员函数的实现 template ListNode:: ListNode () link ( NULL )[] //构造函数,仅初始化指针成员 template ListNode:: ListNode const Type & item): data ( item), link( NULL //构造函数,初始化数据与指针成员 template List:: List const Type finishied)i /创建一个带表头结点的有序单链表, finished是停止建表输入标志,是所有输入值中不可能出现的数值 first =last= new ListNode*p, *q,* while( value finished ) /循环建立各个结点 s= new ListNode( value ) q= first; p= first->link while(p ! NULL & p->data link: F //寻找新结点插入位置 g->link /在q,p间插入新结点 if( p= NULL ) last s: cin > value: template void List:: Browse()i //浏览并输出链表的内容 coutlink while(p!=NULL )i cout data; if( pl= last )cout else cout < end1
14 }; template class List { public: List ( const Type finishied ); //建立链表 void Browse ( ); //打印链表 void Merge ( List &hb ); //连接链表 private: ListNode *first, *last; }; //各成员函数的实现 template ListNode :: ListNode ( ) : link ( NULL ) { } //构造函数, 仅初始化指针成员。 template ListNode :: ListNode ( const Type & item ) : data ( item ), link ( NULL ) { } //构造函数, 初始化数据与指针成员。 template List :: List ( const Type finishied ) { //创建一个带表头结点的有序单链表, finished 是停止建表输入标志, 是所有输入值中不可能出现的数值。 first = last = new ListNode( ); //创建表头结点 Type value; ListNode *p, *q, *s; cin >> value; while ( value != finished ) { //循环建立各个结点 s = new ListNode( value ); q = first; p = first->link; while ( p != NULL && p->data link; } //寻找新结点插入位置 q->link = s; s->link = p; //在 q, p 间插入新结点 if ( p == NULL ) last = s; cin >> value; } } template void List :: Browse ( ) { //浏览并输出链表的内容 cout *p = first->link; while ( p != NULL ) { cout data; if ( p != last ) cout "; else cout << endl;