常用的排序算法 插入-冒泡-选择-归并-快排

今天天气不错,最近看了一下大神们的排序总结,我也给自己总结一下吧。

说明:代码中为了说明算法的流程,对于输入的参数没有进行检验。(事实上是需要的),并且默认排序为非递减数组(升序)

内部排序和外部排序

  1. 根据排序过程中涉及的存储器不同,可以讲排序方法分为两大类:一类是内部排序,指的是待排序的几率存放在计算机随机存储器中进行的排序过程;另一类的外部排序,指的是排序中要对外存储器进行访问的排序过程。
  2. 内部排序是排序的基础,在内部排序中,根据排序过程中所依据的原则可以将它们分为5类:插入排序、交换排序、选择排序、归并排序和基数排序;根据排序过程的时间复杂度来分,可以分为三类:简单排序、先进排序、基数排序。
    评价排序算法优劣的标准主要是两条:一是算法的运算量,这主要是通过记录的比较次数和移动次数来反应;另一个是执行算法所需要的附加存储单元的的多少。(来自度娘)

常见排序的复杂度图(网搜)

对于每种算法的来历我就不废话了,详情可以百度,bing, google。。。。
- 插入排序
- 冒泡排序
- 选择排序
- 归并排序
- 快速排序

1.插入排序

插入排序,第一个元素作为有序数组的第一个元素,从数组的第二个元素开始,依次将元素放入已经有序的数组中,如果小于则向前移动,如果大于则停止放在当前的位置。
e.g. * 5 4 3 2 1* 是原始数组
初始化 5 待进入数字4
第一次排序完成 4 5
第二次排序完成 3 4 5
第三次排序完成 2 3 4 5
第四次排序完成 1 2 3 4 5 搞定

//插入排序
//size_t是std命名空间下的 unsigned int
template <typename T>
void Insertion_Sorting(T *arrayT, size_t length)
{
    for (unsigned int i = 1; i < length; i++)
    {
        int j = i - 1;//有序数组的最后一个元素
        T key = arrayT[i];//待排序元素
        while (j >= 0 && arrayT[j] > key)//保证不越界 当j循环到-1时,由于[[短路求值]],不会运算arrayT[-1]  
        {
            arrayT[j + 1] = arrayT[j];//如果小于则大数向后移动
            j--;//因为j-- 所以循环结束不满足条件的时候  j可能<0 或者 arrayT[j] < key
        }
        arrayT[j + 1] = key; //这里需要+1 原因是while 里面的j--
    }
} 

时间复杂度为O(n^2)

2.冒泡排序

冒泡排序从第一个开始,如果大于后面的数字就往后移一位,如果遇到比自己大的则不交换,但是将大的作为下次移动的对象。(循环是从n 到1, 即从末尾到第一个)
e.g. * 4 3 5 2 1* 是原始数组

第一次排序完成 3 4 2 1 5
第二次排序完成 3 2 1 4 5
第三次排序完成 2 1 3 4 5
第四次排序完成 1 2 3 4 5 搞定

template <typename T>
void Bubble_Sort(T *arrayT, size_t length)
{
    for (unsigned int i = 0; i != length - 1; i++)
    {
        for (unsigned int j = 0; j + i != length - 1; j++)
        {
            if (arrayT[j] > arrayT[j + 1])//如果大于后面的数就交换位置,否则从后一个数接着比较
            {
                T temp = arrayT[j];
                arrayT[j] = arrayT[j + 1];
                arrayT[j + 1] = temp;
            }
        } 
    }
}

冒泡排序最好的时间复杂度为O(n)
冒泡排序总的平均时间复杂度为 O(n^2)

3.选择排序

选择排序就是一趟又一趟找到最小(大)的放在最小(大)的位置即可。。。

e.g. * 4 3 5 2 1* 是原始数组
第一次排序完成 1 3 5 2 4
第二次排序完成 1 2 5 3 4
第三次排序完成 1 2 3 5 4
第四次排序完成 1 2 3 4 5 搞定

template <typename T>
void Selection_Sort(T *arrayT, size_t length)
{
    for (unsigned int i = 0; i < length; i++)
    {
        int min = i;
        for ( unsigned int j = i + 1; j < length; j++)
        {
            if (arrayT[min] > arrayT[j])
            {
                min = j;//记录最小值
            }
        }
        T temp = arrayT[i];
        arrayT[i] = arrayT[min];
        arrayT[min] = temp;
    }
}

4.归并排序

选择排序就是一趟又一趟找到最小(大)的放在最小(大)的位置即可。。。

e.g. * 5 4 3 2 1* 是原始数组
[0-4]拆分
[0-2][3-4] 合并[0-4] 第四次合并
左边
[[0-1][2-2]] 合并[0-2] 第二次合并
[[ [0-0] [1-1]] 合并[0-1] 第一次合并
右边
[[3-3][4-4]] 合并[3-4] 第三次合并

[[5 4] 3] [2 1]]
第一次排序完成 [4 5] 3 2 1
第二次排序完成 [3 4 5] 2 1
第三次排序完成 3 4 5 [1 2]
第四次排序完成 [1 2 3] [4 5] 搞定

 template <typename T>
void Merge(T *sourceArray, T *temp, int Start_Index, int Mid_Index, int End_Index)
{
    int i = Start_Index, j = Mid_Index + 1, k = Start_Index;
    while (i != Mid_Index + 1 && j != End_Index + 1)//数组 前半部分和后半部分合并
    {
        if (sourceArray[i] > sourceArray[j])
        {
            temp[k++] = sourceArray[j++];
        }
        else
        {
            temp[k++] = sourceArray[i++];
        }
    }
    while (i != Mid_Index + 1)//剩余的元素
    {
        temp[k++] = sourceArray[i++];
    }
    while (j != End_Index + 1)//剩余的元素
    {
        temp[k++] = sourceArray[j++];
    }
    for (int i = Start_Index; i != End_Index + 1; i++)//拷贝回去
    {
        sourceArray[i] = temp[i];
    }
}

template <typename T>
void Merge_Sort(T *sourceArray, T *temp, int Start_Index, int End_Index)
{
    if (Start_Index < End_Index)
    {
        int Mid_Index = (Start_Index + End_Index) / 2;
        Merge_Sort(sourceArray, temp, Start_Index, Mid_Index);//尾巴一直除以2
        Merge_Sort(sourceArray, temp, Mid_Index + 1, End_Index);//头一直除以2
        Merge(sourceArray, temp, Start_Index, Mid_Index, End_Index);
    }
}

5.快速排序

快速排序就是每次选取一个枢轴值,利用二分法将当前数组中大于枢轴值得元素放在后面,否则放在前面。在小于和大于的区间再进行划分区域再排序。

如果是逆序数组,快排会退化为O(n^2)

e.g. * 5 4 3 2 1* 是原始数组
第一次排序完成 —- 枢轴值是5,1 4 3 2 5
第二次排序完成 —- 枢轴值是1 , 1 4 3 2 5
第三次排序完成 —- 枢轴值是4,1 2 3 4 5
第四次排序完成 —- 枢轴值是3,1 2 3 4 5 搞定
e.g. * 2 4 3 5 1 * 是原始数组
第一次排序完成 —- 枢轴值是2, 1 2 3 5 4
第二次排序完成 —- 枢轴值是 3 , 1 2 3 5 4
第二次排序完成 —- 枢轴值是 5 , 1 2 3 4 5 搞定

template <typename T>
void Quick_Sort(T *arrayT, int Start_Index, int End_Index)
{
    if (Start_Index < End_Index)//注意这里是小于!!!!!!!
    {
        int first = Start_Index;//左边界
        int last = End_Index;//右边界
        T key = arrayT[first];//枢轴值
        while (first < last)//左右边界  相遇的地方
        {
            while (first < last && arrayT[last] >= key)//从右边找比枢轴值所小的下标
            {
                last--;
            }
            arrayT[first] = arrayT[last];//将小值放在小位置
            while (first < last && arrayT[first] <= key)//从左边选择比枢轴值大的下标
            {
                first++;
            }
            arrayT[last] = arrayT[first];//将大值放在大位置
        }
        arrayT[first] = key;//枢轴值的位置
        Quick_Sort(arrayT, Start_Index, first - 1);//左边再排序
        Quick_Sort(arrayT, first + 1, End_Index);//右边再排序
    }
}

快排还有好多升级版,三分快排,随机枢值快排,大家有兴趣可以查找相关资料哟。。。

测试结果

看到这里的都是真爱。。。。排序算法那么多,这次只写了寥寥的几个,有时间会接着写一些常用的东西的。

全文的代码放在这里了大家来找BUG呀!
戳我—带你去整个代码的地方,欢迎来给我找BUG
测试环境 CodeBlocks 16.01

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值