第五单元 数组与 指针( i )

24
第第第第 第第第第第I 5.8 第第第第第 5.7 第第第第第第第第第 第第第 ()

Upload: jolie-hall

Post on 02-Jan-2016

77 views

Category:

Documents


0 download

DESCRIPTION

第五单元 数组与 指针( I ). 5.7 多级指针与多维数组(补充). 5.8 查找与排序. 变量 ppval. 变量 pval. 变量 val. &pval. &val. 10. 5.7 多级指针与多维数组 (选读). 多级指针的概念: 多级指针变量中存的是另一个指针变量的地址,其说明如下: int val=10; int *pval=&val; int **ppval=&pval; // 是多少级指针就有多少 * 号 cout

TRANSCRIPT

Page 1: 第五单元 数组与 指针( I )

第五单元 数组与指针( I )

5.8 查找与排序

5.7 多级指针与多维数组(补充)

Page 2: 第五单元 数组与 指针( I )

5.7 多级指针与多维数组 (选读)多级指针的概念:多级指针变量中存的是另一个指针变量的地址,其说明如下:int val=10;int *pval=&val;int **ppval=&pval;// 是多少级指针就有多少 * 号

cout<< val<<‘\t’<< *pval<<‘\t’<< **ppval;

这里 val 、 *pval 、 **ppval 值均为 10 。注意 * 号为间接引用运算符,定义中的 * 号是指针说明符。

&pval &val 10

变量 ppval 变量 pval 变量 val

Page 3: 第五单元 数组与 指针( I )

5.7 一级指针与二维数组

指针与一维数组: 指向一维数组(首元素)的同类型指针与该数组名等效:

int a[10], *pa =a;// =&a[0];

则 pa[0] ,就是 a[0] ;*pa ,即 a[0];*(pa+1) 和 pa[1] ,都代表 a[1] 。

即 a 与 pa 两者等效。

Page 4: 第五单元 数组与 指针( I )

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] ,相等。

Page 5: 第五单元 数组与 指针( I )

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;}

启示:可用指向任意维数组首地址的一级指针以及长度信息,将数组完整地传递到函数中,从而实现函数(算法)的通用性。

Page 6: 第五单元 数组与 指针( I )

5.8 查找与排序 (I)(P.196)

5.8.2 常用的排序法

5.8.1 常用查找方法

查找( search ):是最重要的计算应用之一,是在数据集合中寻找满足条件的数据,找到后进一步给出该数据对象的细节信息,在数据库技术中称为检索( retrieval )。不同于枚举查找!排序( sorting ): 要实现查找,首先要对数据排序。查找算法不适用杂乱无章的数据。

例如利用计算机数据库可以快速查找到数据,首先因为其数据有序排列,其次使用了查找算法。

Page 7: 第五单元 数组与 指针( I )

5.8.1 常用查找方法 顺序查找:从首个元素开始,依据一定原则顺序查找直到找到或查到最后元素为止。 查找是按关键字( key word )进行。可以唯一地把资料区分出来的数据项被称为主关键字。如学生的资料,学号可作为主关键字。 对于数据集合,按照关键字小的往前还是后排列,可分别称为升序排列与降序排列。对于升序排列,可以采用对半查找( binary search )。

Page 8: 第五单元 数组与 指针( I )

5.8.1 常用查找方法

low

8 9 171311 207 19 21 23 3126 292 5 37 3923

查找 low mid high

20 21 292623 31 37 39

mid highlow

20 21 23

mid high

23

low mid high 成功

图 6.3 查找成功例

首先安排两个变量 low 和 high 作为关键词序列的首尾两元素的下标,取 mid= (low+ high)/2 ,如 mid 位置的元素是所查找的,则结束。如果该元素大了,则取 low=mid +1 , high 不变,继续查找;如果该元素小了,则取 high=mid-1 , low 不变,继续查找。如果查到 low>=high 仍未找到,则失败,停止。

对半查找 :

Page 9: 第五单元 数组与 指针( I )

5.8.1 常用查找方法—对半查找2 5 7 8 11 13 179 19 20 2321 26 29 31 3710

查找 low

39

mid high

2 5 7 8 11 13 179

low mid high

11 13 179

low mid high

9

low mid high图 6.4 查找失败例

注意: 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 了。

Page 10: 第五单元 数组与 指针( I )

5.8.1 常用查找方法

【参见例6.4】对半查找递归算法,升序表采用数组来表示。递归方法易读易懂,但效率低。注意递归的隐式循环代码编写。

【参见例6.5】对半查找迭代算法。该例中迭代算法的可读性也不差,效率要高于递归。注意迭代的显式循环代码编写 的关键点。

Page 11: 第五单元 数组与 指针( I )

参见【例 6.4 】对半查找递归算法参见【例 6.4 】对半查找递归算法,数组表示有序表。int BinarysearchRec (const int & 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 else if(x<*(pslst+mid)) mid=Binarysearch(x,pslst,low,mid-1);

// 中间点大于定值,查找左区间,注意 mid-1 } return mid;// 找到返回下标 ; 未找到但结束了,返回 mid=-1 ,不判断}递归方法易读易懂,但效率低。

Page 12: 第五单元 数组与 指针( I )

参见【例 6.4 】对半查找递归算法int main(){

const int 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;

}

Page 13: 第五单元 数组与 指针( I )

5.8.1 常用查找方法

【参见例 6.4 】对半查找递归算法,升序表采用数组来表示。递归方法易读易懂,但效率低。注意递归的隐式循环代码编写。

【参见例 6.5 】对半查找迭代算法。该例中迭代算法的可读性也不差,效率要高于递归。注意迭代的显式循环代码编写 的关键点。

Page 14: 第五单元 数组与 指针( I )

参见【例 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; // 左缩查找区间 else if (*(pslst+mid)<x ) low = mid+1; // 右缩查找区间 else return mid; // 找到,就是 mid } if (*(pslst+mid) != x ) mid = -1; // 最后还未查找 return mid;}该例中迭代算法的可读性也不差,效率要高于递归。注意迭代的显式循环代码编写 的关键点。

Page 15: 第五单元 数组与 指针( I )

5.8.2 常用的排序法

排序的概念:排序( sorting )是数据处理中经常使用的一种重要运算。用于将数据元素序列由无序调整为有序。 数据元素中一般有多个数据项,排序可选择其中一个可排序的数据项(可进行比较运算)来进行,称为排序关键字。 对高考统计表进行排序,可根据考生的准考证号,它可以保证排序结果的唯一性,称主关键字。 录取时,要按高考总分排序,只可称关键字,这样同一分数的人很多,这些人的排名可再取一个次关键字如数学或语文分来排序,以减少排名相同的几率。 从小到大排序称升序,反之为降序。最常见的排序有插入排序、选择排序和交换排序。

Page 16: 第五单元 数组与 指针( I )

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]插到该元素之后。

i 0 1 2 3 4 5 6 temp

初始序列 [8] 6 7 9 4 5 2 6

1 [6 8] 7 9 4 5 2 7

2 [6 7 8] 9 4 5 2 9

3 [6 7 8 9] 4 5 2 4

4 [4 6 7 8 9] 5 2 5

5 [4 5 6 7 8 9] 2 2

6 [2 4 5 6 7 8 9]

直接插入排序算法中用了一个临时变量 temp,要插入的元素放到 temp中,这样插入前各元素后移时允许将该元素冲掉。

Page 17: 第五单元 数组与 指针( I )

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;

}}

Page 18: 第五单元 数组与 指针( I )

void printArray (const int* 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;}

Page 19: 第五单元 数组与 指针( I )

5.8.2 常用的排序法

*(2)对半插入排序( Binary Insert Sort )利用了对半查找的思想。对半插入排序要快于直接插入排序。【例 6.7 】升序对半插入排序算法

Page 20: 第五单元 数组与 指针( I )

【例 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; } }关键字相同的数据元素,原来在前的排序后仍在前,称稳定排序(同直接插入排序)。

Page 21: 第五单元 数组与 指针( I )

5.8.2 常用的排序法

图 6.6 从下往上扫描的冒泡程序

2 .交换排序交换排序的基本思想是按关键字两两排序,如果发生逆序则交换之,直到所有的数据都排好为止。

49 13 13 13 13 13 13 13

38 49 27 27 27 27 27 27

65 38 49 38 38 38 38 38

97 65 38 49 49 49 49 49

76 97 65 49’ 49’ 49’ 49’ 49’

13 76 97 65 65 65 65 65

27 27 76 97 76 76 76 76

49’ 49’ 49’ 76 97 97 97 97

冒泡排序基本思想参见图 6.

6 。最左列为最初的情况,最右列为完成后的情况。首先我们从一列数据底部开始,相邻的两数据进行比较,小的数放上面,一趟比较下来,最小的数据冒到了最上面。再缩小区域,按同样方法继续下一趟交换,如果有一趟比较中没有发生交换,则已排好序。图 6.6 中,第 5 列就已排好序,若再继续下一趟就不会发生交换。

Page 22: 第五单元 数组与 指针( I )

参见【例 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; //本趟无交换,则终止算法。

}}

Page 23: 第五单元 数组与 指针( I )

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’ [97 65 76] 13 27 38 49 49’ 65 [97 76] 13 27 38 49 49’ 65 76 97

图 6.7 直接选择排序的过程

最大优点是易读。缺点是每次都要从未排序的子序列中逐一查找出最小元素,效率低下。

Page 24: 第五单元 数组与 指针( I )

【例 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;}

}}