C++编写经典算法之一:插入排序InsertionSort

“插入排序”是数列排序的算法之一。
其思路引点来自于我们平时打扑克牌的习惯。

“我们在整理扑克牌时,往往会倾向于将无序的扑克牌升序或降序的排列,其方法在于拿起一张牌,与其他牌对比,如果是升序排列,那就与左边的牌进行对比,将其放在比此牌大且比此牌小的位置,重复这个过程,就会得到一个有序的牌组。”

  1. 算法思路
    在这里插入图片描述
    首先,得到一个随机的数列。

在这里插入图片描述
左端的数字已完成排序。

在这里插入图片描述
然后,取出那些尚未操作的左端的数字,将其与已经操作的左侧的数字进行比较。如果左边的数字较大,交换两个数字。重复此操作,直到出现一个较小的数字或者数字到达左端。

在这里插入图片描述
这种情况下,由于5大于3,所以交换了数字,数字到达了左端,停止数字移动。

在这里插入图片描述

这样,“3”已经完成了排序。

在这里插入图片描述
和之前同样取出左端的数字,与左边的数字进行比较。

在这里插入图片描述

由于“5”大于“4”,所以交换了数字。由于“3”小于“4”,出现了更小的数字,所以“4”停止移动。

在这里插入图片描述

这样,“4”完成了排序。

在这里插入图片描述

重复上述操作,直至所有的数字完成排序。

  1. 动画演示
    在这里插入图片描述
  2. 代码清单及其测试结果
#include <iostream>
#include <ctime>
template <class T>

int getSizeOfArray(T& is){
    return sizeof(is)/ sizeof(is[0]);
}

void insertionSort(int *is,int size){
    for(int i=1;i<size;i++){
        int currentIndex = i;
        for(int j=currentIndex-1;j>=0;j--){
            if(is[j]>is[currentIndex]){
                int cup = 0;
                cup = is[j];
                is[j] = is[currentIndex];
                is[currentIndex] = cup;
                currentIndex = j;
            }
        }
    }
}

int main() {
    using namespace std;

    clock_t startTime,endTime;

    int is[] = {2,3,5,1,0,8,6,9,7};
    int size = getSizeOfArray(is);

    cout<< "原数列:";

    for(int i = 0;i<size;i++)
    {
        cout<< is[i] << " ";
    }

    cout<< "\n" << "选择性排序后:";

    startTime = clock();//计时开始
    insertionSort(is,size);
    endTime = clock();//计时结束

    for(int i = 0;i<size;i++)
    {
        cout<< is[i] << " ";
    }

    cout << "\n"<<"The run time is: " <<(double)(endTime - startTime) / CLOCKS_PER_SEC << "s" << endl;
    return 0;
}

在这里插入图片描述

  1. 另一种思路

我们通过动画演示和上述的代码清单可以发现,是利用不断的比较交换来实现排序的过程。但,其实,我们还有另外一种实现方法,并不会改变“插入排序”的核心思路,这个与我们平时打牌最相似,话不多说,直接看代码。

#include <iostream>
#include <ctime>
template <class T>

int getSizeOfArray(T& is){
    return sizeof(is)/ sizeof(is[0]);
}

void moveArray(int *is,int currentIndexOfI,int currentIndexOfJ){
    for(int i = currentIndexOfI;i>currentIndexOfJ;i--){
        is[i] = is[i-1];
    }
}

void insertionSort2(int *is,int size){
    for(int i=1;i<size;i++){
        int locationIndex = -1;
        for(int j=i-1;j>=0;j--){
            if(is[i]<is[j]){
                locationIndex = j;
            }
        }
        if(locationIndex!=-1){
            int cup;
            cup = is[i];
            moveArray(is,i,locationIndex);
            is[locationIndex] = cup;
        }
    }
}

int main() {
    using namespace std;

    clock_t startTime,endTime;

    int is[] = {2,3,5,1,0,8,6,9,7};
    int size = getSizeOfArray(is);

    cout<< "原数列:";

    for(int i = 0;i<size;i++)
    {
        cout<< is[i] << " ";
    }

    cout<< "\n" << "选择性排序后:";

    startTime = clock();//计时开始
    insertionSort2(is,size);
    endTime = clock();//计时结束

    for(int i = 0;i<size;i++)
    {
        cout<< is[i] << " ";
    }

    cout << "\n"<<"The run time is: " <<(double)(endTime - startTime) / CLOCKS_PER_SEC << "s" << endl;
    return 0;
}

在这里插入图片描述

通过观察,可以明显发现,这是一种将数组片段推移的实现方法,就和我们平时打牌一样,从中间发现一张较小的牌,直接找到位置,将其插入,并将右边的牌整体向右移动。

  1. 实现方法对比

在数据量小的时候,在相同的数列情况下,不同实现方式(即3小节与4小节)算法运行的时间是几乎相同的。下面看看大整数数列下,谁的性能更好。

随机数范围:r属于[0,100]

样本量(单位:个)10050010002000500010000100000
实现方法一(单位:s)2.2e-050.0005210.0023550.0072070.049690.15413.5
实现方法二(单位:s)2.2e-050.0005180.0018110.0085450.0513750.16515.3

我们不难发现,在样本数小于5,000时,似乎实现方法二更为省时,但样本数一旦大于5,000,很明显实现方法一更为快速。

  1. 算法分析

如果目标是把n个元素的序列升序排列,那么采用插入排序存在最好情况和最坏情况。最好情况就是,序列已经是升序排列了,在这种情况下,需要进行的比较操作需(n-1)次即可。最坏情况就是,序列是降序排列,那么此时需要进行的比较共有n(n-1)/2次。插入排序的赋值操作是比较操作的次数加上 (n-1)次。平均来说插入排序算法的时间复杂度为O(n^2)。因而,插入排序不适合对于数据量比较大的排序应用。但是,如果需要排序的数据量很小,例如,量级小于千,那么插入排序还是一个不错的选择。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值