第十四章 数组
1. 什么是数组
三要素:数组类型 数组名 下标
2. 数组元素
3. 数组下标越界
int a[10];该数值有a[0]到a[9]10个元素
一旦越界程序就会报错
4. 倒序输出
5. 将数组的下标定义为常量以便于修改
const int n=10;
int a[n];
6. 手动操作数组元素
7. 数组的初始化和赋值
整形数值/字符数值 都可以单一赋值for(){a[i]=i;} 也可以统一初始化 int a[10]={1,2,3};/int a[]={1,2,3};
//空间不够报错,多了用0补;初始化数组的值不能跳过,逗号的方式来省略,这在C中是允许的,但C++中不允许;
字符数组的初始化
char array[10]={“hello”}
等价于char array[10]= “hello”
等价于char array[10]={'h','e','l','l','o',’ ’};
不等价于char array[10]={'h','e','l','l','o'};
char array[]={'h','e','l','l','o'};占用5个字节
char array[]={'h','e','l','l','o',’ ’};占用6个字节
char array[]=”hello”;占用6个字节
空间不够报错,多了用 补
char a[3]={‘a’,’b’,’c’} //没有错误
char a[3]=”abc” //报错 overland 字符串数组系统自动加
8. 求平均考试成绩
total=total+girl[i];
9. 兔子繁殖问题
rabbit[13]={1,1};
for(n=2;n<13;n++)
{
rabbit[n]= rabbit[n-1]+ rabbit[n-2];
cout<<rabbit[n]<<endl;
}
10. 数字排序问题
1.内部八大排序:
当n较大,则应采用时间复杂度为O(nlog2n)的排序方法:快速排序、堆排序或归并排序
☆快速排序:是目前基于比较的内部排序中被认为是最好的方法,当待排序的关键字是随机分布时,快速排序的平均时间最短
九大排序你会几个?:http://www.cnblogs.com/openeim/p/3921645.html#3059390
数据结构/算法:http://blog.csdn.net/cjf_iceking/article/category/1223234
一、插入排序
①直接插入排序原理:
插入排序:插入即表示将一个新的数据插入到一个有序数组中,并继续保持有序。例如有一个长度为N的无序数组,进行N-1次的插入即能完成排序;第一次,数组第1个数认为是有序的数组,将数组第二个元素插入仅有1个有序的数组中;第二次,数组前两个元素组成有序的数组,将数组第三个元素插入由两个元素构成的有序数组中......第N-1次,数组前N-1个元素组成有序的数组,将数组的第N个元素插入由N-1个元素构成的有序数组中,则完成了整个插入排序。
1 //插入排序 之 直接插入排序 2 #include"iostream" 3 using namespace std; 4 void InsertSort(int [],int ); 5 int main() 6 { 7 int a[]={46,58,15,45,90,18,10,62}; 8 InsertSort(a,8); 9 for(int i=0;i<8;i++) 10 {cout<<a[i]<<endl;} 11 return 0; 12 } 13 void InsertSort(int arr[],int n) 14 { 15 for(int i=1;i<n;i++)//循环从第二个数组元素开始,因为arr[0]作为最初已排序部分 16 { 17 int temp=arr[i];//temp标记为未排序第一个元素 18 int j=i-1; 19 while (j>=0 && arr[j]>temp)/*将temp与已排序元素从右向左比较,寻找temp应插入的位置*/ 20 { 21 arr[j+1]=arr[j]; 22 j--; 23 } 24 arr[j+1]=temp; 25 } 26 }
②希尔排序原理图:
1 //插入排序 之 希尔排序 2 3 #include"iostream" 4 using namespace std; 5 void ShellSort(int [],int ); 6 int main() 7 { 8 int a[]={46,58,15,45,90,18,10,62}; 9 ShellSort(a,8); 10 for(int i=0;i<8;i++) 11 {cout<<a[i]<<endl;} 12 return 0; 13 } 14 /******************************************************** 15 *函数名称:ShellInsert 16 *参数说明:pDataArray 无序数组; 17 * d 增量大小 18 * iDataNum为无序数据个数 19 *说明: 希尔按增量d的插入排序 20 *********************************************************/ 21 void ShellInsert(int* pDataArray, int d, int iDataNum) 22 { 23 for (int i = d; i < iDataNum; i += 1) //从第2个数据开始插入 24 { 25 int j = i - d; 26 int temp = pDataArray[i]; //记录要插入的数据 27 while (j >= 0 && pDataArray[j] > temp) //从后向前,找到比其小的数的位置 28 { 29 pDataArray[j+d] = pDataArray[j]; //向后挪动 30 j -= d; 31 } 32 33 if (j != i - d) //存在比其小的数 34 pDataArray[j+d] = temp; 35 } 36 } 37 /******************************************************** 38 *函数名称:ShellSort 39 *参数说明:pDataArray 无序数组; 40 * iDataNum为无序数据个数 41 *说明: 希尔排序 42 *********************************************************/ 43 void ShellSort(int* pDataArray, int iDataNum) 44 { 45 int d = iDataNum / 2; //初始增量设为数组长度的一半 46 while(d >= 1) 47 { 48 ShellInsert(pDataArray, d, iDataNum); 49 d = d / 2; //每次增量变为上次的二分之一 50 } 51 }
二、交换排序
①冒泡排序原理:
冒泡排序:依次比较相邻的数据,将小数据放在前,大数据放在后;即第一趟先比较第1个和第2个数,大数在后,小数在前,再比较第2个数与第3个数,大数在后,小数在前,以此类推则将最大的数"滚动"到最后一个位置;第二趟则将次大的数滚动到倒数第二个位置......第n-1(n为无序数据的个数)趟即能完成排序。
1 //冒泡排序方法一:1.设置一标志性变量flag,用于记录每趟排序中最后一次进行交换的位置。由于flag位置之后的记录均已交换到位,故在进行下一趟排序时只要扫描到flag位置即可 2 #include"iostream" 3 using namespace std; 4 void BubbleSort(int [],int ); 5 int main() 6 { 7 int a[]={46,58,15,45,90,18,10,62}; 8 BubbleSort(a,8); 9 for(int i=0;i<8;i++) 10 {cout<<a[i]<<endl;} 11 return 0; 12 } 13 void BubbleSort(int a[], int n) 14 { 15 int j, k; 16 int flag; 17 int temp; 18 19 flag = n; 20 while (flag > 0) 21 { 22 k = flag; //① 23 flag = 0; //② 24 for (j = 1; j < k; j++)//flag的作用①:给k赋值,避免多余的循环//一轮下来90已经在最后面了,所以下一轮只需判断前k个数 25 if (a[j - 1] > a[j]) 26 { 27 temp = a[j - 1]; 28 a[j-1] = a[j]; 29 a[j] = temp; 30 flag = j; //flag的作用②:判断有没有交换排序,若没有则直接跳出 31 } 32 } 33 }
1 //冒泡排序方法二:传统冒泡排序中每一趟排序操作只能找到一个最大值或最小值,我们考虑利用在每趟排序中进行正向和反向两遍冒泡的方法一次可以得到两个最终值(最大者和最小者) , 从而使排序趟数几乎减少了一半。 2 void Bubble_2 ( int r[], int n){ 3 int low = 0; 4 int high = n -1; //设置变量的初始值 5 int tmp,j; 6 while (low < high) { 7 for (j = low; j < high; ++j) //正向冒泡,找到最大者 8 if (r[j] > r[j+1]) { 9 tmp = r[j]; r[j]=r[j+1];r[j+1]=tmp; 10 } 11 --high; //修改high值, 前移一位 12 for ( j = high; j >low; --j) //反向冒泡,找到最小者 13 if (r[j] < r[j-1]) { 14 tmp = r[j]; r[j] = r[j-1];r[j-1] = tmp; 15 } 16 ++low; //修改low值,后移一位 17 } 18 }
②快速排序原理:
快速排序:快速排序采用分治法进行排序,首先是分割,选取数组中的任意一个元素value(默认选用第一个),将数组划分为两段,前一段小于value,后一段大于value;然后再分别对前半段和后半段进行递归快速排序。其实现细节如下图所示:
1 //快速排序方法一 2 #include"iostream" 3 using namespace std; 4 void QuickSort(int [],int, int ); 5 int main() 6 { 7 int a[]={46,58,15,45,90,18,10,62}; 8 QuickSort(a,0,7); 9 for(int i=0;i<8;i++) 10 {cout<<a[i]<<endl;} 11 return 0; 12 } 13 void QuickSort(int s[], int l, int r) 14 { 15 if (l < r) 16 { 17 //Swap(s[l], s[(l + r) / 2]); //将中间的这个数和第一个数交换 参见注1 18 int i = l, j = r, x = s[l]; 19 while (i < j) 20 { 21 while(i < j && s[j] >= x) // 从右向左找第一个小于x的数 22 j--; 23 if(i < j) 24 s[i++] = s[j]; 25 26 while(i < j && s[i] < x) // 从左向右找第一个大于等于x的数 27 i++; 28 if(i < j) 29 s[j--] = s[i]; 30 } 31 s[i] = x; 32 //x左右两边重复以上操作 33 QuickSort(s, l, i - 1); // 递归调用 34 QuickSort(s, i + 1, r); 35 } 36 }
1 //快速排序方法二 2 //原理:快排选用数组第一个元素作为分割元素,如果是一个已经基本有序的数组,那么时间复杂度将会提升到O(n2);可以从数组中随机选择一个元素作为划分数据,这样即使针对基本有序的数据来说,效率同样达到(nlog2n),优化后分割函数如下所示 3 int Split(int *pDataArray,int iBegin,int iEnd) 4 { 5 int rIndex = rand() % (iEnd - iBegin + 1); //随机获得偏移位置 6 7 int pData = pDataArray[iBegin + rIndex]; //将iBegin+rIndex处的值作为划分值 8 9 while (iBegin < iEnd) //循环分割数组,使其前半段小于pData,后半段大于pData 10 { 11 while (iEnd > iBegin && pDataArray[iEnd] >= pData) //从后向前寻找小于pData的数据位置 12 iEnd--; 13 14 if (iEnd != iBegin) 15 { 16 pDataArray[iBegin] = pDataArray[iEnd]; //将小于pData数据存放到数组前方 17 iBegin++; 18 } 19 while (iBegin < iEnd && pDataArray[iBegin] <= pData) 20 iBegin++; 21 22 if (iBegin != iEnd) 23 { 24 pDataArray[iEnd] = pDataArray[iBegin]; //将大于pData数据存放到数组后方 25 iEnd--; 26 } 27 26 } 28 29 pDataArray[iEnd] = pData; //此时iBegin=iEnd,此处存储分割数据pData 30 return iEnd; 31 }
③、STL中的sort与qsort区别:
1 //快速排序qsort与sort的区别 2 //百度啊 http://blog.csdn.net/abcjennifer/article/details/6707237 http://blog.sina.com.cn/s/blog_6439f26f01012xw3.html 3 #include<iostream> 4 #include<algorithm>//sort的头文件。qsort是#include<stdlib.h> 5 using namespace std; 6 bool cmp (const int a, const int b) 7 { 8 return a>b; 9 } 10 int main() 11 { 12 char ch[20]="sdasdacsdasdas"; 13 cout<<ch<<endl; 14 sort(ch,ch+14); //缺省状态时升序排列 15 cout<<ch<<endl; 16 int data[5]; 17 for(int i = 0; i < 5; i++) 18 cin >> data[i]; 19 sort(data, data + 5,cmp);//此处的cmp为降序排列 20 for(int i = 0; i < 5; i++) 21 cout<<data[i]<<" "; 22 return 0; 23 }
三、选择排序
①简单选择排序原理:
选择排序:比如在一个长度为N的无序数组中,在第一趟遍历N个数据,找出其中最小的数值与第一个元素交换,第二趟遍历剩下的N-1个数据,找出其中最小的数值与第二个元素交换......第N-1趟遍历剩下的2个数据,找出其中最小的数值与第N-1个元素交换,至此选择排序完成。
1 //简单选择排序方法一 2 #include"iostream" 3 using namespace std; 4 void SelectSort(int [],int ); 5 int main() 6 { 7 int a[]={46,58,15,45,90,18,10,62}; 8 SelectSort(a,8); 9 for(int i=0;i<8;i++) 10 {cout<<a[i]<<endl;} 11 return 0; 12 } 13 void SelectSort(int R[], int n) 14 { 15 int i, j, m; 16 int t; 17 for(i=0; i<n-1; i++) 18 { 19 m = i; 20 for(j = i+1; j < n; j++) 21 { 22 if(R[j] < R[m]) //寻找最小数 23 m = j; 24 } 25 if(m != i) //如果不是本身就进行交换 26 { 27 t = R[i]; 28 R[i] = R[m]; 29 R[m] = t; 30 } 31 } 32 }
1 //简单选择排序方法二:简单选择排序,每趟循环只能确定一个元素排序后的定位。我们可以考虑改进为每趟循环确定两个元素(当前趟最大和最小记录)的位置,从而减少排序所需的循环次数。改进后对n个数据进行排序,最多只需进行[n/2+1]趟循环即可。具体实现如下: 2 void SelectSort(int r[],int n) { 3 int i ,j , min ,max, tmp; 4 for (i=1 ;i <= n/2+1;i++) { 5 // 做不超过n/2趟选择排序 6 min = i; max = i ; //分别记录最大和最小关键字记录位置 7 for (j= i+1; j<= n-i; j++) { 8 if (r[j] > r[max]) { 9 max = j ; continue ; //此处的continue是因为满足了这个if()下面的if()就不需要执行了 10 } 11 if (r[j]< r[min]) { 12 min = j ; 13 } 14 } 15 //该交换操作还可分情况讨论以提高效率 16 tmp = r[i-1]; r[i-1] = r[min]; r[min] = tmp; 17 tmp = r[n-i]; r[n-i] = r[max]; r[max] = tmp; 18 19 } 20 }
②堆排序原理:
//堆排序。不会!!!!!!!!!!???????????
//归并排序。不会!!!!!!!!!!!!???????????????
//基数排序。有时间在去看吧!!!!!!!!???????????
11. 数组在内存中的分布
下标越界,C++不提供报错。只有靠程序员自己时时注意。
12. 数组名
数组名代表的是第一个元素的地址
int a[4]={1,2,3,4}
//int *a=&a[0]; //系统自动生成一个指向数组第一个元素的指针
13. 数组名与函数
☆14. 传递与接收
接收的三种方法:
①void a(int x[]) //被调用函数的声明
②void a(int *x) //被调用函数的声明
③void a(int x[30]) //被调用函数的声明
15. 将数组传递给函数
folat Average(const float *,int ) //指针的声明
16. 求数组所有元素的和
17. 用递增法查找数据
1 int find(int m,int a[],int n) 2 { 3 for(int i=0;i<n;i++) 4 { 5 if(a[i]==m) 6 return i; 7 } 8 return n; 9 }
18. 用二分法来查找数据
1 //用于排序好的数组(且不能有相同大小的数字) 2 int find(int m,int a[],int n) 3 { 4 int s=0,l=n-1,i; 5 while(s<=l) 6 { 7 i=(s+l)/2; 8 if(a[i]==m) 9 return i; 10 if(a[i]<m) //数组递增时用< 递减时用> 11 s=i+1; 12 else l=i-1; 13 } 14 return n; 15 }
19. 判断数组是否按照顺序排列
1 bool check(int a[],int n) 2 { 3 if(a[0] < a[1]) 4 { 5 for(int i=1;i<n;i++) 6 if(a[i] > a[i+1]) 7 return false; 8 } 9 else if(a[0] > a[1]) 10 { 11 for(int i=1;i<n;i++) 12 if(a[i] < a[i+1]) 13 return false; 14 } 15 return true; 16 }
20. 判断数组排列方式然后执行不同的函数
int check(int a[],int n) //返回的可以是任意值
21. 数组在对象中的传参
22. 对象数组
23. 在对象数组中初始化成员变量
area one[4]={area(10,10),area(13,34),area(20,30),area(40,40)};
24. 堆中对象
for(int i=0;i<10000;i++)
{
area *pone=new area(i,2*i);
}
//在堆中存放一个area类的对象,并用area类的指针pone来指向它,然后初始化该对象的两个成员的值
这个程序只能保存最后一个对象的地址,所以前面的产生了内存泄露
25. 指针数组
解决办法
area *one[10000]; //声明一个由10000个指向类area的指针组成的数组,即指针数组(存放指针的数组)
for(int i=0;i<10000;i++)
{
area *pone=new area(i,2*i);
one[i]=pone;
cout<<one[i]->get()<<endl;
}
//由于这个数组元素保存的是堆中对象的内存地址,所以必须用成员指针运算符->来访问该对象的成员函数,((*one[i]).get())
//表示是利用指针的一次间接访问
26. 堆中对象数组
area *one=new area[10000]; //声明了一个由10000个area对象组成的数组,通过调用new操作符把整个数组创建在堆区中
one[i].set(i,i*2); //设置堆中每个对象的参数
delete []one; //删除数组中的每个对象
☆总结:
Area one[10000];
Area *one[10000];
Area *one=new Area[10000];
对象数组,指针数组,堆中对象数组
27. 枚举常量与数组
枚举常量方便识别,限定范围。
enum day{Monday=1,Tuesday,Wendesday,Thursday,Firday,Saturday,Sunday} //enum 是关键字,day 是类型
enum weekl
{sun,mon,tue=5,wed,thu,fri,sat};
则枚举常量的初值如下:sun值为0,mon值为1,tue值为5, wed值为6,thu值为7,fri值为8,sat值为9。
枚举变量:
enum color
{red,yellow,blue}c_l;
c_l=yellow; //正确,将值为1的枚举常量yellow赋予枚举型变量c_1
c_l=1; //错误,不能直接将整型常量赋予枚举型变量
c_l=(enum color)l; //正确,先将l强制转换成枚举型常量yellow再赋值
28. 多维数组
29. 多维数组的初始化
30. 定义字符数组
31. 二维数组输出图形
32. 字符串数组
33. 字符串的输入、输出问题
//1、cin>> //用法1:最基本,也是最常用的用法,输入一个数字(只能是数字) //用法2:接受一个字符串,遇“空格”、“TAB”、“回车”都结束 #include <iostream> using namespace std; void main () { int a,b; cin>>a>>b; cout<<a+b<<endl; char c[20]; cin>>c; cout<<c<<endl; }
//2、cin.get() //用法1:cin.get(字符变量名)可以用来接收字符 //用法2:cin.get(字符数组名,接收字符数目)用来接收一行字符串,可以接收空格,因为它会在末尾自动加上 ,实际保存.字符只有n-1; //用法3:cin.get(无参数)没有参数主要是用于舍弃输入流中的不需要的字符,或者舍弃回车,弥补cin.get(字符数组名,接收字符数目)的不足. #include <iostream> using namespace std; void main () { char ch; ch=cin.get(); //或者cin.get(ch);接收单个字符 cin.sync(); //用于清除整个输入流的缓冲区 char c[20]; cin.get(c,20); cout<<ch<<endl; cout<<c<<endl; }
//3、cin.getline() // 接受一个字符串,可以接收空格并输出 #include <iostream> using namespace std; void main () { char m[20]; cin.getline(m,5);//接受5个字符到m中,其中最后一个为' ',所以只看到4个字符输出; cout<<m<<endl; } //延伸: //cin.getline()实际上有三个参数,cin.getline(* m,n,结束字符) //当第三个参数省略时,系统默认为' ' //如果将例子中cin.getline()改为cin.getline(m,5,'a');当输入jlkjkljkl时输出jklj,输入jkaljkljkl时,输出jk //当用在多维数组中的时候,也可以用cin.getline(m[i],20)之类的用法: //#include<iostream> //using namespace std; //void main () //{ // char m[3][20]; // for(int i=0;i<3;i++) // { // cout<<" 请输入第"<<i+1<<"个字符串:"<<endl; // cin.getline(m[i],20); // //cin.sync(); 当输入大于20的时候就会出问题 // } // cout<<endl; // for(int j=0;j<3;j++) // cout<<"输出m["<<j<<"]的值:"<<m[j]<<endl; //}
//4、getline() // 接受一个字符串,可以接收空格并输出,需包含“#include<string>” #include<iostream> #include<string> using namespace std; void main () { string str; getline(cin,str); cout<<str<<endl; } //和cin.getline()类似,但是cin.getline()属于istream流,而getline()属于string流,是不一样的两个函数
//5、gets() // 接受一个字符串,可以接收空格并输出,需包含“#include<string>” #include<iostream> #include<string> using namespace std; void main () { char m[20]; gets(m); //不能写成m=gets(); cout<<m<<endl; //保护会自动添加‘ ’ } //类似cin.getline()里面的一个例子,gets()同样可以用在多维数组里面: //#include<iostream> //#include<string> //using namespace std; //void main () //{ // char m[3][20]; // for(int i=0;i<3;i++) // { // cout<<" 请输入第"<<i+1<<"个字符串:"<<endl; // gets(m[i]); // } // cout<<endl; // for(int j=0;j<3;j++) // cout<<"输出m["<<j<<"]的值:"<<m[j]<<endl; //} //自我感觉gets()和cin.getline()的用法很类似,只不过cin.getline()多一个参数罢了;
//6、getchar() //接受一个字符,需包含“#include<string>” #include<iostream> #include<string> using namespace std; void main () { char ch; ch=getchar(); //不能写成getchar(ch); cout<<ch<<endl; } //getchar()是C语言的函数,C++也可以兼容,但是尽量不用或少用;
34. strcat函数
原型:extern char *strcat(char *dest,char *src);
char a[20]="My name is"; //不能定义a[]="My name is"; char b[]=" jack."; cout<<strcat(a,b)<<endl; //string catenate 连接后储存在a数组中 //Char:strncat(ch1,ch2,3);//将ch2的头三个字符连接到ch1后; //String:str1.append(str2,3,1);//将str2的下标为3的头1个字符连接到str1中
35. strcpy函数
原型:extern char *strcpy(char* dest, const char *src);
char a[20]="My name is"; //必须是数组 char b[]=" Jack."; //也可以是字符串 cout<<strcpy(a,b)<<endl; //string copy cout<<strcpy(a,"I'm Jack")<<endl; //先清除a中的所有字符,再将b中的所有字符(包括空字符)复制到a中 //另外string还有自己的赋值函数assign str1.assign(str2,3,1);//将字符串str2下标为3开始的头1个字符赋给str1 cout<<str1<<endl;//输出:c
36. strcmp函数
原型:extern int strcmp(const char *s1,const char * s2);
特别注意:strcmp(const char *s1,const char * s2)这里面只能比较字符串,不能比较数字等其他形式的参数。
if(strcmp(str1,str2)>0)//(string compare) //如果 str1小于 str2,返回负数;如果 str1大于 str2,返回正数;二者相等则返回 0。
37. strupr函数
原型:extern char *strupr(char *s);
转化为大写字
38. strlwr函数
原型:extern char *strlwr(char *s);
转化为小写字
#include"iostream" using namespace std; void main() { char a[]="My name is Jack"; cout<<strupr(a)<<endl; //uppercase cout<<strlwr(a)<<endl; //lowercase }
39. strlen函数 sizeof关键字的区别
原型:extern unsigned int strlen(char *s)
函数:strlen(数组名) //不包括' '
运算符:sizeof(类型说明符/数组名/表达式对象/指针) //包括' '
char a[20]={"hello world! "};
char *p;
strlen(a)=strlen(p)=13;sizeof(a)=20;sizeof(p)=4;
string要用strlen他就得转化strlen(str1.c_str())
//c_str是将str的对象转化成char的数组并且将数组名传给strlen
另外string也有自己的“strlen“ 即成员函数size,调用时str1.size();
40. 打印杨辉三角形
//打印金字塔形杨辉三角 #include "iostream" //#include<iomanip> using namespace std; int main() { const int n=10; int a[n][n]; for(int i=0;i<n;i++) { for(int m=0;m<2*(n-i-1);m++)//打印空格成就金字塔形 { cout<<" "; } for(int j=0;j<=i;j++) { if(j==0||j==i) //两侧是“1” a[i][j]=1; else a[i][j]=a[i-1][j-1]+a[i-1][j]; cout.width(4);//控制接下来输出数字的占用空间 cout.fill('*');//填充空格 cout<<a[i][j]; //放后面有区别 /*cout.width(4); cout.fill('*');*/ //cout<<a[i][j]<<setw(4);//这个需要加头文件 } cout<<" "; } //cout<<" "<<"1"<<endl;//说明一个空格占一个字节 }
41. 重载数组下标操作符
重载数组可以避免数组越界不报错的问题
char &operator[](int o);//此处&一定要用,因为返回的的是一个数组元素????????????
return size[length-1];//此处的length-1是关键????????????????
本章总结: