2021-7-17 有数组,必排序(炉边小坐)

关于数组,我想直接套用我的一篇文章的内容:

一维数组

定义方式:

数据类型符    数组变量名[整型常量];

  1. 数组定义时,必须指定数组的大小(长度),大小必须是整型常量,不能是变量或变量表达式。
  2. 数组定义后,系统将给其分配一定大小的内存单元。数组所占内存单元= 数组大小 × sizeof(元素类型)short int a[20]; 则数组a所占内存单元的大小为:20 * sizeof(short) = 20 * 2 = 40(字节)。
  3. 占用内存中连续的存储单元,其中第一个数组元素的地址是数组的首地址。

一维数组的引用

数组变量名[下标];记得需要传地址操作时,数组整体可以用首项地址/数组名

而需要对数组特定元素或者从特定元素开始传址操作时,记得加取地址符号&或者使用首地址指针加减

(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

 

 

 

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值