排序算法优化思考

近行情很不好,公司这几天的动作比较大,总的来说就是要与员工共同度过这段艰难时刻,虽然也没看到有福同享,但是有难还是得一起来担的。面对行业不景气,作为一名励志的程序猿也不能闲着,这不,为了将之前的基础知识给补上,也为了今后的进一步发展,这段时间一直在学习C的数据结构与算法,书是我浩哥推荐的,看完了近半内容,书的质量还是不错的,知识正确性至少有保证,其传授的思想也多是带着读者一步一步思考,如何改进、完善算法。这些方面我认为挺好,学了不少,也收获不小,感谢浩哥推书,行业十几年经历对我这种小萌新还是非常有借鉴作用,链接已放文末,有兴趣的道友可以拜读。

期博文总结归纳几种排序算法,分别为选择、插入、冒泡、希尔排序。老实说,在没有学习之前虽知道有这几种排序算法,但就其差别还不是非常清楚。于是有了这篇博文,对四类排序算法的思考。世界之大、算法无尽,如果仅仅是照搬算法的代码,可能花费你的一生也不一定能阅尽所有,所以在学习算法之前必须先有一个正确的观念——算法被创造出来的目的是解决问题,问题的解决方法不只有唯一一种,我们学习的也仅仅是在一定条件下的最优解。接下来讲解的几种排序算法都是比较经典且容易理解,适用小量数据排序

选择排序

介绍算法之前我们聊一聊影响算法效率的相关因素,我们不得不区分时间与空间的概念,空间即算法占用的系统内存,时间即算法从开始执行到结束执行所花费的时间,这两者在算法中非常矛盾,一般的,如果想要算法执行时间短,算法就会越复杂,占用的内存空间就越多,二者不可兼得,所以在算法设计之初,就必须要提前规划牺牲其中一部分。具体来说影响时间效率的本质是算法中的内部循环,算法中的变量、指针、结构体则影响算法的占用内存,所以提高算法的效率需要从两者入手。选择排序中对从小到大排序设计思路是遍历一遍所有数据,将最小数据选出来放在首位,接着遍历一遍剩余数据,选出次小数据并放入第二位,以此类推,最后的最大数据就被放在了数组尾部。算法思路非常简单,代码量也不大,但是牺牲了执行的时间,其执行时间与N^2成正比。

void selection(int *a,int n)        //选择排序 先选出最小的元素与前面元素交换
{
    int i,j,temp;
    for(i = 0;i < n-1;i++)
    {
        int min = i;
        for(j = i+1;j < n;j++)
            if(a[j] < a[min])
                min = j;
        {
            temp = a[min];
            a[min] = a[i];
            a[i] = temp; 
        }
    }
}

上面是这几天学到的一种选择排序的优化算法,其设计思路没有改变,对第二个for循环进行了优化,对比在第二个for循环中交换数据,极大提高了执行效率,将数据交换的次数减少到了一次。

插入排序

我们打扑克的时候,为了便于出牌,我们会将手中扑克按照从小到大或是从大到小的顺序依次排列,插入排序算法的设计思想也是如此,在数组左边依次排序,对右边出现的数据依次与其左边的数据做比较,直至确认其位置。算法实现非常直接,但是不高效,对比常用与优化之后的用例,优化方案用三种方法来对算法进行改善。(i)首先将数组中最小的元素放在第一位 (ii)在内部循环中只做一个简单赋值,而不是执行交换操作 (iii)当元素插入到正确位置就终止内循环。
常用:

void insertion(int *a,int n)        //两两互换 
{
    int i,j,temp;
    for(i = 1;i < n;i++)
        for(j = i;j > 0;j--)
            if(a[j-1] > a[j])
            {
                temp = a[j];
                a[j] = a[j-1];
                a[j-1] = temp;
            }
}

优化:

void insertions(int *a,int n)        //两两互换 
{
    int i,j,temp;
    for(i = n-1;i > 0;i--)            //将最小元素放在第一位
        if(a[i] < a[i-1])
        {
                temp = a[i];
                a[i] = a[i-1];
                a[i-1] = temp;
        }
    for(i = 2;i < n;i++)
    {
        j = i;
        temp = a[i];
        while(temp < a[j-1])        //循环找出a[i]元素在左边排序中的位置
        {
            a[j] = a[j-1];
            j--;
        }
        a[j] = temp;
    }
}

冒泡排序

泡排序非常简单,其算法思想为:遍历文件,如果邻近的两个元素大小顺序不对,就将两者交换,重复这样的操作直至所有数据排好序。可以发现冒泡算法与常规的插入算法类似,其主要差别在于冒泡算法从右往左移动,插入是从左向右移动。

void bubble(int *a,int n)
{
    int i,j,temp;
    for(i = 0;i < n-1;i++)
        for(j = n-1;j > i;j--)
            if(a[j-1] < a[j])   //大数往前冒泡
            {
                temp = a[j];
                a[j] = a[j-1];
                a[j-1] = temp;
            }
}

一般来说,排序算法的运行时间是和算法执行的比较次数,元素移动或交换的次数成正比的。在选择排序中,平均使用${N2}/2$次的比较,N次交换,插入排序中,平均使用${N2}/4$次比较与${N2}/4$次的交换,冒泡排序中,平均使用${N2}/2$次比较,${N2}/2$次交换。可以发现上述三种算法执行时间都是与N2相关。

希尔排序

尔排序是插入排序的扩展,其本质还是插入排序,希尔排序改进了插入排序只能进行相邻比较的缺陷,允许一定步长间的比较与元素交换,如固定步长4,a[0]与a[4]比较交换,a[4]与a[8]可以比较交换,最终选出所有元素中以4为间隔的元素中的最值放入a[0],接着使步长为1,依次使用插入排序。研究表明就不同步长序列,其算法执行时间不同,对于步长满足3h+1规律的,其算法执行时间与N^(3/2)成正比。较上述三类排序,希尔排序算法明显改善了执行时间。

void shellsort(int *a,int n)
{
    int i,h;
    for(h = 0;h <= (n-1)/9;h = h*3+1);  //首次增量值
    for(;h > 0;h /= 3)
        for(i = h;i < n;i = i+h)
        {
            int j = i,item = a[i]; 
            while((item < a[j-h])&&j >= h)
            {
                a[j] = a[j-h];
                j -= h;
            }
            a[j] = item;
        }
}

当然,算法间的执行效率比较都是建立在N无穷大时,N越大,四类算法间的时间差距越明显

创作不易,白嫖不好,各位的支持和认可,就是我创作的最大动力,我们下篇文章见!

清风 | 文 【原创】

如果本篇博客有任何错误,请批评指教,不胜感激 !
[浩哥博客]:https://coolshell.cn/articles/4102.html

猜你喜欢

转载自www.cnblogs.com/wdg-blog/p/12727024.html
今日推荐