240 likes | 422 Views
第五单元 数组与 指针( I ). 5.7 多级指针与多维数组(补充). 5.8 查找与排序. 变量 ppval. 变量 pval. 变量 val. &pval. &val. 10. 5.7 多级指针与多维数组 (选读). 多级指针的概念: 多级指针变量中存的是另一个指针变量的地址,其说明如下: int val=10; int *pval=&val; int **ppval=&pval; // 是多少级指针就有多少 * 号 cout<< val<< ‘ t ’ << * pval<< ‘ t ’ << ** ppval;
E N D
第五单元 数组与指针(I) 5.7 多级指针与多维数组(补充) 5.8 查找与排序
变量ppval 变量pval 变量val &pval &val 10 5.7 多级指针与多维数组 (选读) 多级指针的概念: 多级指针变量中存的是另一个指针变量的地址,其说明如下: int val=10; int *pval=&val; int **ppval=&pval; //是多少级指针就有多少*号 cout<< val<<‘\t’<< *pval<<‘\t’<< **ppval; 这里val、*pval、**ppval值均为10。 注意*号为间接引用运算符,定义中的*号是指针说明符。
5.7 一级指针与二维数组 指针与一维数组: 指向一维数组(首元素)的同类型指针与该数组名等效: int a[10], *pa =a;// =&a[0]; 则pa[0] ,就是a[0]; *pa,即a[0]; *(pa+1) 和pa[1] ,都代表a[1]。 即a 与pa 两者等效。
5.7 一级指针与二维数组 二维数组与一级指针: 二维数组在内存中以一维数组的形式存储,所以可以采用一级指针指向并访问它。如有: int x2d[m][n]={1,2,3,4,…..}; int *pt=&x2d[0][0];//&x2d或&x2d[0],可以?,错,不匹配 如何用pt访问x2d[i][j]? X2d数组对应一维内存的索引为i*n+j。 所以*(pt+i*n+j) 就是x2d[i][j],相等。
5.7 一级指针与二维数组 【例5.14】用指向二维数组首地址的一级指针访问数组元素。 int main( ){ int a[3][6]={{1,2,3,4,5,6},{7,8,9,10,11,12},{13,14,15,16,17,18}}; int * ptr,i,j; ptr=&a[0][0] ; //不能ptr = a; for(i=0;i<18;i++){ cout<<*(ptr+i)<<'\t'; if(i%6==5) cout<<endl; } return 0;} 启示:可用指向任意维数组首地址的一级指针以及长度信息,将数组完整地传递到函数中,从而实现函数(算法)的通用性。
5.8查找与排序 (I)(P.196) 查找(search):是最重要的计算应用之一,是在数据集合中寻找满足条件的数据,找到后进一步给出该数据对象的细节信息,在数据库技术中称为检索(retrieval)。不同于枚举查找! 排序(sorting): 要实现查找,首先要对数据排序。查找算法不适用杂乱无章的数据。 例如利用计算机数据库可以快速查找到数据,首先因为其数据有序排列,其次使用了查找算法。 5.8.1 常用查找方法 5.8.2 常用的排序法
5.8.1 常用查找方法 顺序查找: 从首个元素开始,依据一定原则顺序查找直到找到或查到最后元素为止。 查找是按关键字(key word)进行。可以唯一地把资料区分出来的数据项被称为主关键字。如学生的资料,学号可作为主关键字。 对于数据集合,按照关键字小的往前还是后排列,可分别称为升序排列与降序排列。对于升序排列,可以采用对半查找(binary search)。
mid high 20 21 23 26 29 31 37 39 high low mid low 图6.3 查找成功例 23 成功 low mid high 23 2 5 7 8 9 11 13 17 19 20 21 23 39 26 29 37 31 查找 low 20 21 23 mid high 5.8.1 常用查找方法 对半查找: 首先安排两个变量low和high作为关键词序列的首尾两元素的下标,取mid= (low+ high)/2,如mid位置的元素是所查找的,则结束。如果该元素大了,则取low=mid +1, high不变,继续查找;如果该元素小了,则取 high=mid-1,low不变,继续查找。如果查到low>=high仍未找到,则失败,停止。
mid high mid high mid high 9 图6.4 查找失败例 low mid high 10 2 2 5 5 7 7 8 8 9 9 9 11 11 11 13 13 13 17 17 17 19 29 20 31 21 23 37 26 39 查找 low low low 5.8.1 常用查找方法—对半查找 注意:low=mid+1和high=mid-1非常重要,没有加1和减1时,可能数据存在而找不到。如在图6.3中,如果找到了仅剩20、21、23这一步,这时取low=mid,则剩下21、23,mid = (low + high)/2,得mid = low ,下一步low = mid ,还是剩下21、23,mid 不为23,永远找不到23了。
5.8.1 常用查找方法 【参见例6.4】对半查找递归算法,升序表采用数组来表示。 递归方法易读易懂,但效率低。注意递归的隐式循环代码编写。 【参见例6.5】对半查找迭代算法。 该例中迭代算法的可读性也不差,效率要高于递归。注意迭代的显式循环代码编写 的关键点。
参见【例6.4】对半查找递归算法 参见【例6.4】对半查找递归算法,数组表示有序表。 int BinarysearchRec (constint & x, const int *pslst, int low, int high) { // x为定值 int mid=-1; if (low<=high) { mid=(low+high)/2; if (*(pslst+mid)<x) mid = Binarysearch(x,pslst,mid+1,high); //中间点小于定值,查找右区间,注意mid+1 elseif(x<*(pslst+mid)) mid=Binarysearch(x,pslst,low,mid-1); //中间点大于定值,查找左区间,注意 mid-1 } returnmid;//找到返回下标;未找到但结束了,返回mid=-1,不判断 } 递归方法易读易懂,但效率低。
参见【例6.4】对半查找递归算法 int main() { constint h=20; int i,k=37; int a[h]={0,2,3,5,7,11, 13, 17, 19, 23, 31,34,39, 59,61,67,80,89,99,101}; //升序 int* pa=a; i=BinarysearchRec(k, pa, 0,h-1); cout<<"整数"<<k<<"在表中位置(下标):"<<i<<endl; return 0; }
5.8.1 常用查找方法 【参见例6.4】对半查找递归算法,升序表采用数组来表示。 递归方法易读易懂,但效率低。注意递归的隐式循环代码编写。 【参见例6.5】对半查找迭代算法。 该例中迭代算法的可读性也不差,效率要高于递归。注意迭代的显式循环代码编写 的关键点。
参见【例6.5】对半查找迭代算法 参见【例6.5】对半查找迭代算法。 int BinarySearchIt(const int & x, const int *pslst, const int & last) { int high=last, low=0, mid;// last 当前数组的最大下标 if( last == -1 ) return -1;//避免空表出错 while (low<=high ) { mid = (low+high)/2; if ( x<*(pslst+mid) ) high = mid-1; //左缩查找区间 elseif (*(pslst+mid)<x ) low = mid+1; // 右缩查找区间 elsereturn mid; // 找到,就是mid } if(*(pslst+mid) != x ) mid = -1; // 最后还未查找 return mid; } 该例中迭代算法的可读性也不差,效率要高于递归。注意迭代的显式循环代码编写 的关键点。
5.8.2 常用的排序法 排序的概念: 排序(sorting)是数据处理中经常使用的一种重要运算。用于将数据元素序列由无序调整为有序。 数据元素中一般有多个数据项,排序可选择其中一个可排序的数据项(可进行比较运算)来进行,称为排序关键字。 对高考统计表进行排序,可根据考生的准考证号,它可以保证排序结果的唯一性,称主关键字。 录取时,要按高考总分排序,只可称关键字,这样同一分数的人很多,这些人的排名可再取一个次关键字如数学或语文分来排序,以减少排名相同的几率。 从小到大排序称升序,反之为降序。最常见的排序有插入排序、选择排序和交换排序。
5.8.2 常用的排序法 1.插入排序(Insert Sorting) (1)直接插入排序的思想是:(以升序为例)当插入第i(i>=1)个元素s[i]时,前面的元素s[0],s[1],…,s[i-1]已经排好序,我们将s[i]的关键字与s[i-1], s[i-2],…,的关键码顺序进行比较,找到第一个比它小的,则s[i]插到该元素之后。 直接插入排序算法中用了一个临时变量temp,要插入的元素放到temp中,这样插入前各元素后移时允许将该元素冲掉。
5.8.2 常用的排序法 参见【例6.6】升序直接插入排序算法 void InsertSort (const int* pslst, const int &last){ int temp; int i,j; for (i=1;i<=last;i++){ temp=*(pslist+i); j=i; while (j>0&&temp< *(pslist+(j-1))){ *(pslist+j)= *(pslist+(j-1)) ; j--; //查找与移动同时做 } *(pslist+j) =temp; } }
void printArray (constint* pArray, int n) { int i, last=n-1; for(i=0;i<=last;i++){ cout<< *(pArray+i)<<endl; if(i%5==4) cout<<endl; } cout<<endl;} int main(){ const int h=12; int n[h]; int elem[10]={100,89,101,49,0,99,54,22,87,60};//10个 for (int i=0;i<10;i++) n[i]=elem[i];//注:10个元素 int *pa=n; cout<<"未排序表:"<<endl; printArray(pa,10); InsertSort(pa, 9); cout<<"已排序表:"<<endl; printArray(pa,10); return 0;}
5.8.2 常用的排序法 *(2)对半插入排序(Binary Insert Sort)利用了对半查找的思想。对半插入排序要快于直接插入排序。 【例6.7】升序对半插入排序算法
【例6.7】升序对半插入排序算法 void BinaryInsertSort(const int* pslst, const int &last){ int temp; int low,high,mid,i,j; for (i=1;i<=last;i++){ temp=*(pslst+i); low=0; high=i-1; while (low<=high){ //请注意与对半查找的不同之处 mid=(low+high)/2; if(temp< *(pslst+mid)) high=mid-1; else low=mid+1; } //稳定排序 for(j=i-1;j>=low;j--) *(pslst+j+1)=*(pslst+j); *(pslst+low)=temp; } } 关键字相同的数据元素,原来在前的排序后仍在前,称稳定排序(同直接插入排序)。
5.8.2 常用的排序法 2.交换排序 交换排序的基本思想是按关键字两两排序,如果发生逆序则交换之,直到所有的数据都排好为止。 冒泡排序基本思想参见图6.6。最左列为最初的情况,最右列为完成后的情况。首先我们从一列数据底部开始,相邻的两数据进行比较,小的数放上面,一趟比较下来,最小的数据冒到了最上面。再缩小区域,按同样方法继续下一趟交换,如果有一趟比较中没有发生交换,则已排好序。图6.6中,第5列就已排好序,若再继续下一趟就不会发生交换。 图6.6从下往上扫描的冒泡程序
参见【例6.8】冒泡排序算法 冒泡排序算法。 void BubbleSort(const int* pslst, const int &last){ bool noswap; int i, j, temp; for ( i=0; i<last; i++ ) { //最多做last-1趟 noswap=true; //未交换标志为真 for ( j=last; j>i; j--) { //从下往上冒泡 if ( *(pslst+ j) < *(pslst+ j-1) ) { temp = *(pslst+ j) ; *(pslst+ j) = *(pslst+ j-1); *(pslst+ j-1) = temp; noswap = false; } } if ( noswap ) break; //本趟无交换,则终止算法。 } }
5.8.2 常用的排序法 3.选择排序(Selection Sort) 基本思想是:每一趟从待排序的记录中选出关键字最小的元素,顺序放在已排好序的子序列的后面,直到全部记录排序完成。直接选择排序(Straight Selection Sort)是最简单方法。 [49 38 65 97 76 13 27 49’] 13 [38 65 97 76 49 27 49’] 13 27 [65 97 76 49 38 49’] 13 27 38 [97 76 49 65 49’] 13 27 38 49 [76 97 65 49’] 13 27 38 49 49’ [9765 76] 13 27 38 49 49’ 65 [9776] 13 27 38 49 49’ 65 76 97 图6.7直接选择排序的过程 最大优点是易读。缺点是每次都要从未排序的子序列中逐一查找出最小元素,效率低下。
【例6.9】直接选择排序 • void SelectSort(const int* pslst, const int &last) • { • int i, j, k,temp; • for(i=0;i<last;i++) • { • k=i; temp= *(pslst+ i); • for(j=i+1;j<=last;j++) //j=i有误!先找到最小的元素 • if(*(pslst+ j)<temp) { • k=j; temp=*(pslst+ j) ; • } • if ( k != i ){ //k>i也行。后进行交换 • temp=*(pslst+ i) ; • *(pslst+ i) =*(pslst+ k) ; • *(pslst+ k)=temp; • } • } • }