关于数组,我想直接套用我的一篇文章的内容:
一维数组
定义方式:
数据类型符 数组变量名[整型常量];
- 数组定义时,必须指定数组的大小(长度),大小必须是整型常量,不能是变量或变量表达式。
- 数组定义后,系统将给其分配一定大小的内存单元。数组所占内存单元= 数组大小 × sizeof(元素类型)short int a[20]; 则数组a所占内存单元的大小为:20 * sizeof(short) = 20 * 2 = 40(字节)。
- 占用内存中连续的存储单元,其中第一个数组元素的地址是数组的首地址。
一维数组的引用
数组变量名[下标];记得需要传地址操作时,数组整体可以用首项地址/数组名
而需要对数组特定元素或者从特定元素开始传址操作时,记得加取地址符号&或者使用首地址指针加减
(2) 只能逐个引用数组元素,不能一次引用整个数组!!
(3) 数组中的一个元素相当于一个变量,数组元素也称下标变量。对变量的一切操作适合于数组元素。
(4) 数组引用要注意越界问题。
一维数组的赋值
1、直接赋值
(4) 如果表达式的个数小于数组的大小,则未指定值的数组元素被赋值为0;
在定义数组时,如果不给数组变量赋初值,数组大小不能省略
2、通过循环来赋值
二维数组亦然,不再叙述。
主要想聊聊排序,在学C的时候,我们只有两种排序方法可以选择(其实是老师只讲了两种),分别是冒泡排序法,选择排序法,学C++时,发现了快速排序法(quick sort),深感其效率之高,证明了二分法的优越性。
首先,列举两个排序法:
1、冒泡排序法(bubble sort)
冒泡排序法,顾名思义,就像你在煮开水,密度较低的泡泡被密度较高的水挤压上升一样,使较小的数据浮上或让较大的数据沉底【当然你想让较小的数据沉底也没什么问题】
这是冒泡排序法的一个范例,循环分为两层,第一层控制所有参加冒泡的元素个数(例如一个数组有6个元素,则允许5个元素参加排序),第二层控制一个元素与另外的元素比较、排序的次数(例如数组总共6个元素,我是第二个元素,我就要和另外的元素比较4次),最后的if判断决定是否交换,其余就是输出模块。
#include<iostream>
using namespace std;
void bubble(int[],int);//函数声明
int main()
{
int a[]={5,3,6,8,1,9,2};
int n=sizeof(a)/4//淦,16位编译器上int型占2个byte,而32位以及64位都是占4个byte,被坑了,米娜桑一定要注意
bubble(a,n);
return 0;
}
void bubble (int haha[],int size)
{
for(int pass=1;pass<size;pass++)
{
for(int i=0;i<size-pass;i++)
{
if(haha[i]>haha[i+1])
{
int temp=haha[i];
haha[i]=haha[i+1];
haha[i+1]=temp;
}
}
}
for(int i=0;i<size;i++)
cout<<haha[i]<<" ";
cout<<endl;
}
冒泡排序最直观最简单,但是效率最低(因为需要遍历数组元素)
2、选择排序法
排序过程:
首先通过n-1次比较,从n个数中找出最小的(最小数的下标), 将它与第一个数交换—第一趟选择排序,结果最小的数被安置在第一个元素位置上;再通过n-2次比较,从剩余的n-1个数中找出次小的(次小数的下标),将它与第二个数交换—第二趟选择排序;重复上述过程,共经过n-1趟排序后,排序结束。(n-1+n-2+n-3+.......+1)
for (i = 0; i < NUM-1; i++)
{
k = i;
for (j = i+1; j <NUM; j++)
if (a[j] < a[k]) k = j;
if (i != k)
{ x = a[i]; a[i] = a[k]; a[k] = x; }
}
优点是比较次数不随初始值变化而变化,缺点是比较趟数仍然很多。
3、快速排序法
快速排序(Quicksort)是对冒牌排序法的一种改进。
快速排序算法通过多次比较和交换来实现排序,其排序流程如下: [2]
(1)首先设定一个分界值,通过该分界值将数组分成左右两部分。 [2]
(2)将大于或等于分界值的数据集中到数组右边,小于分界值的数据集中到数组的左边。此时,左边部分中各元素都小于或等于分界值,而右边部分中各元素都大于或等于分界值。 [2]
(3)然后,左边和右边的数据可以独立排序。对于左侧的数组数据,又可以取一个分界值,将该部分数据分成左右两部分,同样在左边放置较小值,右边放置较大值。右侧的数组数据也可以做类似处理。 [2]
(4)重复上述过程,可以看出,这是一个递归定义。通过递归将左侧部分排好序后,再递归排好右侧部分的顺序。当左、右两个部分各数据排序完成后,整个数组的排序也就完成了。 [2] ——百度百科
它被认为是效率比较高的算法,它运用了“微元”的思想,用二分法将之分立比较。
这里有个快速排列法的动图:太美了
而关于分界值,你可以随机确定,你也可以算出被排列元素的中间值,值得注意的是,当分界值正好为中间值时,排序效率最高速度最快,当然这比较难找(算)到,你也可以随机确定,即使这个值很不幸是极值,它的性能也十分优越!
下面我们用 quicksort 尝试对十个元素的数组进行排序,注意:因为分界值的不确定(当然直接采取中位数是个明智的做法),quicksort 并不总是最优越的.
//quicksort
#include<iostream>
using namespace std;
void quick(int[],int,int);
int main()
{
int n;
int a[]={50,2,4,9,1,8,22,41,77,1};
n=sizeof(a)/4;
cout<<"the orign:"<<endl;
for(int i=0;i<n;i++) {//原始顺序输出
cout << a[i];
cout<<" ";
}
cout<<endl;
quick(a,0,n-1);
cout<<"the after"<<endl;
for(int i=0;i<n;i++) {
cout << a[i];
cout<<" ";
}
return 0;
}
void quick(int a[],int left,int right)
{
int stand=a[right],l=left,r=right,temp;
while(l<r)//目标:将较小的一半数据分到左边,将较大的数据分到右边,达到了第一步二分法;
{
temp=a[l],a[l]=a[r],a[r]=temp;
while(l<r&&a[r]>stand)--r;//
while(l<r&&a[l]<=stand)++l;
}
temp=a[left],a[left]=a[r],a[r]=temp;//此时,a[r]是分界值,也是左半数据的"端点"值,这样重复上面的过程
if(left<r-1) quick(a,left,r-1);
if(r+1<right) quick(a,r+1,right);//递归,我们认为,分界元是已经完成排序的坐标!,然后操作1/4部分数据,1/8.....直到完成排序!
}
也许有点难懂,我们分开一部分一部分来分析程序:
我们整个算法就是不断的将大的数据分到右边,小的数据分到左边,然后不断的寻找分界元(点),然后二分(我们当然不会随机选取分界元,我们选用最有效率的二分法,就是从中间切开),再将这两部分数据重复以上步骤(将大的数据分到右边,小的数据分到左边,然后不断的寻找分界元),直到最终排序完成.
//quicksort
#include<iostream>
using namespace std;
void quick(int[],int,int);
int main()
{
int n;
int a[]={50,2,4,9,1,8,22,41,77,1};
n=sizeof(a)/4;
quick(a,0,n-1);
for(int i=0;i<n;i++) {
cout << a[i];
cout<<" ";
}
return 0;
}
void quick(int a[],int left,int right)
{
int stand=a[right],l=left,r=right,temp;
while(l<r)//目标:将较小的一半数据分到左边,将较大的数据分到右边,
{
temp=a[l],a[l]=a[r],a[r]=temp;
while(l<r&&a[r]>stand)--r;//
while(l<r&&a[l]<=stand)++l;
}
}
"/Users/Menou16/C++ Learning/cmake-build-debug/untitled5"
the orign:
50 2 4 9 1 8 22 41 77 10
the after
10 2 4 9 1 8 22 41 77 50
进程已结束,退出代码为 0
淦,怎么只交换了首末元素!
但是从内存视图来看,l和r已经=5了,因为整个数组的交换已经悄然完成,中间值也已经确定!
之后,运行整个代码的这一段
temp=a[left],a[left]=a[r],a[r]=temp;//此时,a[r]是分界值,也是左半数据的"端点"值,这样重复上面的过程
if(left<r-1) quick(a,left,r-1);
if(r+1<right) quick(a,r+1,right);//递归,我们认为,分界元是已经完成排序的坐标!,然后操作1/4部分数据,1/8.....直到完成排序!
因为找出了中间元分开了两段,接着对着两段分别重复上述步骤即可(递归)
精妙绝伦!详细过程不再赘述
最终结果:
the orign:
50 2 4 9 1 8 22 41 77 10
the after
1 2 4 8 9 10 22 41 50 77
进程已结束,退出代码为 0