2第2章(1)向量与数组
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
第2章:向量与数组
(§2.1 数组)
这节课我们介绍三个问题:
1.C++数组及其不足之处;
2.向量(一维数组)抽象数据类型;
3.多维数组及其元素的地址映射。
一、C++数组及其不足之处(§ 2.1.1)
1.数组类数据结构的特点(P37.L7~16)
(1)数组元素均为相同类型;
(2)是元素的有限序列;
(3)在内存中连续存储,可以顺序或随机(直接)访问。
2.C++运行栈数组的定义和初始化(P37.L19~P38.L16)
(1)定义的形式。
int a1[3] = {3, 5, 7}, a2[3] = {3}, a3[3];
(2)定义的限制。
不能使用变量(即使已经赋值)说明数组的大小。
(3)初始化的规定。
全局数组、静态数组未初始化的自动取相应的“0”值;
动态数组未初始化的可能为任意值。
(4)使用方式。
可以使用下标法:a1[0], a1[1], a1[2];
地址法:*a1, *(a1+1), *(a1+2);
指针法:int * p = a1; *p++;
3.C++堆内存(动态)数组的定义和释放
(1)定义时动态地在对内存中分配空间:
int *a1, a1size = 3;
a1 = new int[a1size];
(2)动态堆内存数组使用完毕时要由程序释放所分配的空间:
delete [ ]a1;
(3)初始化的规定和使用与运行栈数组相同。
4.C++数组的不足之处
(1)不够灵活。
静态数组不能改变大小;动态数组虽然可以改变大小,但需要程序员
在程序中完成对原内容的拷贝。
(2)不够方便。
对每种元素类型分别定义;仅有存储(写)和抽取(读)操作。
(3)不够安全。
下标或指针访问越界可能造成难以预料的问题。
为此,重新定义向量(一维安全数组)抽象数据类型。
二、向量(一维安全数组)抽象数据类型(§ 2.1.2)
希望数组能增加越界检查而更安全,能修改大小而更灵活,不需每种类型分别定义,又能够执行较多的操作而更方便。
这样的抽象数据类型在许多《数据结构》教材中称之为“向量”或“安全数组”。
1.下面在头文件”array.h”中给出抽象数据类型向量(安全数组)的类声明。
#include <iostream.h>
#include <stdlib.h>
const int DefaultSize = 30; //向量的缺省大小template<class Type>class array{
public:
array (int Size = DefaultSize);//构造函数
array (const array < Type > & x);//复制构造函数
~array( ){delete [ ] elements ;} //析构函数
array < Type > & operator =(const array <Type >& A);//数组赋值
Type& operator [ ] (int i);//下标
Type* operator *() const{
return elements ;} //指针转换,返回指向数组指针int length() const{return arraySize ;} //取数组长度
void reSize(int sz);//修改数组长度private:
Type * elements ; //指向数组的指针
int arraySize ; //元素个数
};
类的定义中的公有成员通常提供类的功能函数。
应用程序的编程人员应该熟悉:1.功能函数的名称和功能;
2.功能函数的参数的个数、类型和次序;
3.功能函数有无返回值,及有返回值时的返回值类型。
类的定义中的的私有成员通常封装类的数据成员,有时也包括一些内部函数。
例如,向量类的私有成员封装了向量即安全数组的存储结构:一个用指针elements 给出的数组,和用整数ArraySize标记的数组元素的个数。
2.下面给出向量数据结构的部分操作的实现。
类的实现指根据类的数据成员的物理存储结构,对类的功能函数的具体编程。
一个有较高水准的数据结构工作者应当掌握这种能力,至少应能读懂相关的代码。
一个类的构造函数的作用是在应用程序中生成相应类的一个对象。
类的构造函数的主要功能一是为这个对象分配它的数据成员所需要的动态内存空间;二是为其本身的数据成员赋初值。
template<class Type>array<Type>::array(int sz) {//构造函数,建最大长度为sz的数组if ( sz <= 0){cerr<<"Imxilid array Sue<<mdl; Amiysize=0; retum;}//参数检查
arraySize = sz; //数组长度
elements = new Type[arraySize ] ; //创建数组并返回数组指针
if(elements == 0) { //若返回指针为0,出错
cerr<<"Memory Allocation Error"<<endl; Arrrrysize = 0; return;} } //注意:此时向量中实有元素个数为0,sz为最多可放的元素个数。
template<class Type>array<Type>::array(const array<Type>&x){
//按照引用向量x复制构造当前向量
int arraySize = x.arraySize ; //以x的长度为当前向量的长度elements = new Type[arraySize]; //为当前向量分配存储空间
if(elements = = 0) //返回地址为0,直接返回
{ cerr<<"Memory Allocation Error"<<endl ; arraySize=0 ; return ; } Type * srcptr = x.elements ; //源数组首地址
Type * destptr = elements ; //目的数组首地址
while(n--) * destptr + + = * srcptr + +;//传送数组元素}
//注意:常量引用即可节省参数传递的时空开销,又可避免改写实参的值。
template<dEmType>Type&array<Type>::operator [ ] (int i) { //下标访问
//取下标为i的数组元素。
if (i<0 || i>arraySize-1) //检查下标的取值范围{ cerr<<"Index out of Range"<<endl; exit(1);} //下标越界,直接返回return elements [ i ] ; //返回下标为i的数组元素}
//注意:返回值为元素的引用,下标访问就既可作右值来读,又可做左值来写。
template<class Type>void array < Type>::reSize(int sz){ //修改向量大小到sz if ( sz<=0 ) cerr<<”Invalid array Size”<<endl; //检查参数的合理性if ( sz != arraySize) {
Type * newarray = new Type[ sz]; //建立新数组
if(newarray = = 0){cerr <<”Memory Allocation Error” <<endl; return; }
int n = (sz <= arraySize )?sz :arraySize; //向新数组传送数据个数当新的小时只传sz个;新的大时原有元素全部传送
Type * srcptr = elements ; //源数组首地址
Type * destptr = newairay ; //目的数组首地址
while (n--) * destptr++ = * srcptr ++;//逐个复制数组元素
delete [ ] elements ; //删释放老数组空间
elements = newarray ; //elements指向新数组
arraySize = sz;//修改数组大小为sz
}
}
3.下面给出向量(安全数组)类使用的一个实例(习题2.4的另解)
# include “StdAfx.h”
# include “array.h”//包含向量类的头文件
int main(int argc, char * argv[ ]){
int c[20] = {1,0,2,0,3,0,4,0,5,0,6,0,7,0,8,0,9,0,10,0}; //C++数组,20个整型元素array<float> a(15); //浮点向量,15个元素未赋值for(int k=0, j=0; j<20; j++) //将c数组中非零元素除2.0 if(c[j] != 0) a[k++] = c[j]/2.0; //集中放在a数组前部
//注意:k为已有的非零元素个数。
a.reSize(k); //按k修改a数组大小
for( j=0; j<a.length(); j++) cout<<a[j]; //输出a数组中k个非零元素return 0;
}
三、多维数组及其元素的地址映射(§ 2.1.3)**
1.多维数组
一维数组的元素类型可以是基本数据类型,可以是复杂数据类型,还可以是用户自定义的数据类型。
当元素类型也是数组时,一维数组扩充为二维数组,参看图2.2(a)。
(P40.L9)当元素类型是n-1维数组时,数组扩充为n维数组。
2.多维数组的存储实现
不论是多少维的数组,其存储实现总是顺序存放在一个连续的内存区域,可以等价地视为一个一维数组,并将多维数组元素的地址映射到相应的一维数组的某个位置,以进行高效率的访问。
以二维数组a[m1][m2]行优先(先放完一行全部元素再放下一行的首元素)为例,a[j][k]元素的起始地址为:
Loc (j, k) = Loc (a) + j*m2 + k
式中Loc(a)为数组a的起始地址,其后的偏移为二项:j*m2为0~j-1共j行各m2个元素所占空间,k为第j行的0~k-1共k个元素所占的空间。
对于三维数组a[m1][m2][m3],a[j][k][li]元素的起始地址为:
Loc (j, k, li) = Loc (a) + j*m2*m3 + k*m3 + li
式中Loc(a)为数组a的起始地址,其后的偏移为三项。
对于n维数组a[m1][m2]…[mn],a[j1][j2]…[jn]元素的起始地址为:
举例:若由float a[3][4][5]的起始地址为2000H,元素a[1][2][3]的起始地址为:Loc (1,2,3) = Loc (a) + 1*4*5*4 + 2*5*4 + 3*4
= 2000H + 80 + 40 + 12
= 2000H + 132 //注意改为16进制
= 2000H + 84H
= 2084H
3.一点说明:
以上多维数组的地址映射只适用于连续存储的多维数组,不适用于向量(安全数组)方式存储的矩阵和多维向量,因为它们可能并不是连续存储的。