详解各大排序方法

* 总所周知,无论是考研还是企业的面试、笔试,查找与排序都是重中之重的知识点了,今天跟大家一起来总结回顾一下排序。本文涉及到的排序有九种:直接插入排序、折半插入排序、希尔排序、冒泡排序、堆排序、简单选择排序、快速排序、归并排序、基数排序等等。以及文章结尾还会对各种排序做一个总结。其中,直接插入排序、冒泡排序、简单选择排序、快速排序将给出源代码并详细给出分析与注释,小伙伴们别急,其他的排序虽然没有给出代码,但是也会一一的根据具体实例来讲解分析的,保证小伙伴们都能轻而易举的掌握各种排序算法。*

1直接插入排序

这种排序方法是靠着插入队伍来排序,一看就是一年级的小朋友排队一样。其核心思想是:从头到尾遍历整个数组,将待排序的数一个一个的插入已经排好序的数组中。其关键点就是:将待插入的数插入到已经排好序的队列中的合适的位置。就好比一个小朋友,由于迟到想要进入到已经按身高排好的队列中,老师就要把他安排到合适的位置。
接下来来看一个9位数的数组:
62,3,49,51,79,465,123,69,25。
直接插入的过程是:首先把62这个数放到队列中去,然后在把3这个数插到队列中,由于3比62小,于是将62往后移一位,3放到62前面。然后插入49,49比62小,因此62继续往后移一位。接着插入51,51也是只比62小,因此也只需要62往后移一位。这样一直插入,直到插入到25。由于25只比3大,所以我们需要把3以后的数字全部往后面移,再把25插入到3后面。具体代码实现:

void charu(int r[],int n )//r是待排列的数组,n是待排序的数量
{
    int a,b,c;
    for(int a=1;a<n;a++)
    {
        c=r[a];//c用于存储插入数字
        b=a;
        b--;
        while(b>=0&&c<r[b])//将c定位到合适的位置并把比他大的数字往后移
        {
            r[b+1]=r[b];
            b--;
        }
        r[++b]=c;//将c插入到合适的位置
    }
}

2折半插入排序

折半插入其实是对直接插入的一种改进算法,细心的小伙伴可能会想:我在将数字插入的时候,一定要从已经排好序的数组的最后一位开始插入吗?这样看起来并不高效。于是折半插入排序就是每次插入的时候是从数组的中间开始比较。比如:已经有了3,49,51,62,79,123,465这个队列,我现在要将69这个数给插入进去,折半插入的做法是将69与上述数组的中间数62比较。由于比62大,就与后面的79进行比较,由于比79小,于是就将79以及后面的数往后移一位。代码实现也并不复杂,只需要改进前面代码的while循环。

3 冒泡排序

冒泡排序是一种很有趣的排序方法。就像是金鱼在水中吐泡泡一样,泡泡越变越大。其核心思想是:从头遍历整个数组,如果前一个数比后一个数大,那么交换值。于是最大的数就被交换到最后了,他的位置就确定了,然后再次遍历整个数组(除去已经确定位置的数),再找出最大的数将它放到最后。排序结束条件:直到待遍历的数只有一个或者这次遍历没有发生数字的交换。于是会有小伙伴问:为什么这次遍历没有数字交换那么排序就结束了?于是我们来看看,我们发生交换的条件是前一个数比后面一个数大,如果没有发生交换,那岂不是数组每个后面的数都比他前面那个数大了,不就已经排好序了吗?
具体事例:
45,4,19,782,8,456,73,15,84。
首先将45与4比较,45比4大于是交换位置,变成了4,45,19,782,8,456,73,15,84。然后再将45与19比较,依然大一些继续交换位置。接着45与782比较,发现782大一些,于是不需要交换位置。而782是整个数组中最大的数,于是782一直与后面的数交换,一直交换到数组末尾。第一趟遍历结束,然后再将接下来8个数进行遍历,这个一直遍历下去直到满足排序结束条件。我们来看代码:

void maopao(int r[],int n)//r是待排列的数组,n是待排序的数量
{
    int a,b,c=0;
    for(a=n;a>0;a--)
    {
        for(b=0;b<a;b++)
        {
            if(r[b]>r[b+1])
            {
                r[b]^=r[b+1]^=r[b]^=r[b+1];//交换r[b]与r[b+1]
                c=1;//c作为标志位,如果发生交换改变c的值
            }
        }
        if(c==0)//如果没有发生数字的交换代表已经排序好了
            return ;
    }
}

4简单选择排序

简单选择排序就跟他的名字一样简单易懂,也是大部分初学者首选的排序算法。其核心算法是:遍历数组,找到最小的值与当前的第一位交换位置,再次遍历数组(除去第一位),将最小的值与数组的第二位交换位置,接着遍历下去,直到待遍历的数只有一个。
具体事例:
6,16,54,3,49,321,999,866,45
首先遍历整个数组,3是最小的值,于是将3与6交换位置。数组变成了3,16,54,6,49,321,999,866,45。然后在遍历剩下的8个数(除去已经找到位置的3).。最小的是6,于是把6与16交换位置。数组变成3,6,54,16,49,321,999,866,45.紧接着这样遍历下去,直到待遍历的数只有一个。代码:

void jiandanxuanze(int r[],int n)
{
    int a,b,c;
    for(a=0;a<n;a++)
    {
        c=a;
        for(b=a+1;b<n;b++)//找到带排序的数组中的最小值并用c记录他的位置
        {
            if(r[c]>r[b])
                c=b;
        }
        if(c!=a)//如果c与a不同,交换他们所指的数
        {
            r[a]^=r[c]^=r[a]^=r[c];
        }
    }
}

5快速排序

快速排序是一种效率很高的排序方法,相比与前面的排序算法,他就要复杂一些了。其核心思想是:选择一个关键字,把比关键字大的数字放到关键字右边,把比关键字小的放到关键字的左边。这样关键字的位置就确定了,然后再将关键字左边的数字也看成是一个数组,重复上述步骤,把关键字右边的数字也看成一个数组,重复上述步骤。
然后我们再来谈谈细节,怎么把比关键字大的放在关键字的右边,怎么把比关键字小的放在关键字的左边。理论讲起来有些复杂,我们举一个实例。
62,3,49,51,79,465,123,69,25
我们先选取62作为关键字,首先我们需要清楚的是,我们经过一轮的变化后,62会到他正确的位置,并无论后面其他数字怎么变化,他的位置是不会变的(不懂没关系,可以先去看下面步骤)。我们用high标记最后一个数字(即25),用low标记最前面的数字(即关键字62),我们从high开始扫描,发现25是最后一个数字并且小于62,那么他应该是在62的左边的,于是我们把令low所标记的位置的数等于25,此时数组变成了
25,3,49,51,79,465,123,69,25
然后我们在反回来从low的位置开始读数,low所标记的位置的数等于62,所以不需要换位置,low++,low标记的数字变成了3,依然比62小,我们继续low++,直到标记到了指向79的位置,然后再将79放到high所指的位置(即最后一位)。此时数组变成了
25,3,49,51,79,465,123,69,79
然后我们再从high标记的数与62比较大小,一开始79比62大,不需要交换位置,high–,然后一直读下去,一直执行high–,于是在79所指的位置就会有low==high,然后关键字的位置就确定了,他就会79所指的位置,所以数组就变成了
25,3,49,51,62,465,123,69,79
然后第一次快排就结束了,接下来我们只需要把62左边的25,3,49,51,进行一下上述的步骤,把465,123,69,79进行上次步骤,一直递归循环下去,直到所指定的关键字low==high,排序结束。
所以我们再来反过头来看看这个过程,我们最终的目的是让62放到其正确的位置。这让一开始62的位置就可以腾出来给需要改变位置的数字,然后我们规定了high跟low两个位置的指针来遍历整个数组已达到目的。我们先从high的位置开始遍历,当发现比关键字小的时候送到关键字的位置,就如上面的25放到第一个位置,然后这样25这个位置就可以腾出来给Low遍历到需要改变位置的数了,这就是快排的奇妙之处。
接下来看代码把,理解上述步骤之后,代码其实就很简单了

void kuaipai(int R[],int low,int high)//low跟high跟上述的意义一样
{
    int a,b,c;
    a=low;
    b=high;
    c=R[a];
    if(a<b)
    {
    while(a<b)//如果a<b代表没有遍历结束
    {
        while(R[b]>=c&&b>a)//从high开始一直找到比关键字小的数
        {
            b--;
        }
        if(R[b]<c)//放到关键字左边
        {
            R[a]=R[b];
        }
        while(c>=R[a]&&a<b)//从low开始一直找到比关键字大的数
        {
            a++;
        }
        if(R[a]>c)//放到关键字右边
        {
           R[b]=R[a];
        }
    }
    R[a]=c;//把关键字放到正确位置
    {kuaipai(R,low,a-1);}
    {kuaipai(R,a+1,high);}//递归
    }
}

6希尔排序

希尔排序的话直接上实例,这样简单易懂。
62,3,49,51,79,465,123,69,25
首先我们把它按顺序分成五组:
第一组:62,465
第二组:3,123
第三组:49,69
第四组:51,25
第五组:79
然后对每一字进行直接插入排序法排序,排序之后的五组变成了:
第一组:62,465
第二组:3,123
第三组:49,69
第四组:25,51
第五组:79
得到数组:62,3,49,25,79,465,123,69,51
然后再把这组数据按顺序分为三组,得到的分组就为:
第一组:62,25,123
第二组:3,79,69
第三组:49,465,51
紧接着继续对这三组数据进行直接插入排序。排序后分组为:
第一组:25,62,123
第二组:3,69,79
第三组:49,51,465
然后得到新的排序数组
25,3,49,62,69,51,123,79,465
这样看起来已经是基本有序,然后在对数组进行一次直接插入排序,
得到最后的数组:3,25,49,51,62,69,79,123,465
希尔排序的前面那些分组,如分为五组,分为三组,都是可以自己选取的,但是要求从大到小选取,最后进行直接插入排序。希尔排序的效率跟你的选择分组有关。

7堆排序

首先我们需要了解的是堆与栈是两种不同的数据结构。栈是一一种先进后出的数据结构,而堆是一种完全二叉树,他有一个特点就是根节点要不是最大的,要不就是最小的。所以我们只需要把当前数组变成堆,然后每次读取堆的根节点,然后读把根节点删掉,重新调整为堆,再重复以上步骤,直到堆中没有数据了,那我们就得到了一个排序数组。

8归并排序 ##

归并排序是继快排之后,一种面试比较常见的排序算法。
归并归并,就是一项一项的归并嘛!
我们也是直接上实例:
对于下面数组:
45,4,19,782,8,456,73,15,84
首先我们把它分为8组,由于每组只有一个元素,不需要排序。
然后把第一组第二组合并,第三组第四组合并,第五组第六组合并,第七组第八组合并。并且排序,就可以得到如下四组数:
第一组:4,45
第二组:19,782
第三组:73,456
第四组:15,84
接着把第一组第二组合并,把第三组第四组合并,并进行排序
第一组:4,19,45,782
第二组:15,73,84,456
接着把这两组合并为一组并排序:
4,15,19,45,73,84,456,782
这是归并排序,一次又一次的归并。

最近太忙了- -
还有两种排序有时间再去写了。我在这先先帮大家总结一下。(直接盗用算法爱好者的一张图了,因为总结的很好)
这里写图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值