在这个浮躁的社会,很多人只是粘贴一份算法,跑一边,就算懂了,我们应该沉下心来

1.概述

  排序是计算机程序设计中的一个重要操作,它的功能是将一个数据记录(或记录)的任意序列,重新排列成一个按关键字有序的序列。

  为了方便描述,我们先确切定义排序:

  假设含n个记录的序列为{R1,R2,R3,...,Rn},其相应的关键字序列为{K1,K2,K3,...,Kn},要确定一种序列,该序列的关键字满足非递减(或非递增)关系,这种操作称之为排序。

  若n个记录的序列中的任意两个记录排序前后的顺序一致则称这种排序是稳定的。

  例如:原序列中Ri排在Rj之前,1<=i<=n,1<=j<=n,i!=j,排序之后依旧Ri排在Rj之前,这就是稳定排序,反之,不稳定排序。

  如果排序只在RAM中进行则称之为内部排序,如果涉及到了外部存储器(比如磁盘、固态硬盘、软盘和闪存等)则称之为外部排序。

  为什么会有这两种方式呢,很简单,如果处理的数据极大,一次不能全部读入内存,则需要借助外部存储器进行排序。

2.插入排序

  2.1直接插入排序

    最简单的排序方式,基本操作是将一个记录插入到已排好序的有序表中,从而得到一个新的、记录长度加1的有序表。

    简单证明:

      假设含n个记录的序列为{R1,R2,R3,...,Rn},其相应的关键字序列为{K1,K2,K3,...,Kn},初始取该序列的第一个记录为有序序列。

      第一步取该序列的第二个记录,插入有序序列的适当位置,使之有序;

      假设第n-2步之后的原有序序列有序;

      第n-1步时选取含n个记录的序列为{R1,R2,R3,...,Rn}的第n个记录,插入有序序列的适当位置,使之有序,则第n-1步之后的有序序列仍然有序。

      证毕。

    那么如何插入有序序列的适当位置呢?

      含n个记录的序列为{R1,R2,R3,...,Rn}中选取第i个记录时,前面i-1个记录已经有序,则首先比较第i个记录与第i-1个记录的大小,如果第i个记录小于第i-1记录,则交换,再从第i-2个记录向前搜索,在搜索过程中,只要遇到大于第i个记录的记录,就将当前记录后移一个位置,直至某个记录小于或者等于第i个记录或者向前搜索到了第0个位置。

    其算法如下:

         

insertSort

      算法时间复杂度:

        排序的基本操作为比较关键字大小和移动记录,当排序序列为逆序且关键字不重复时时间复杂度最大。简单证明一下:

        当选中第i个记录,要对其操作时,最长不过搜索到第0个位置,也就是比较i次,移动i+1次;

        如果有n个记录则要进行n-1次的选记录,如果每次搜索到第0个位置,也就是每次比较最多次,移动最多次,那么总得加起来就是总的比较了最多次,总的移动了最多次。前面一句话体现了一个算法策略,贪心算法。这个算法不一定正确,我们用反证法证明在这里的正确性。

        假设这样的构造过程得到的总的移动次数,总的比较次数不是最多,也就是存在更大的数值,

        我们每次都是选择最多的,如果存在更大的数值,则说明某次选择的不是最多的,这与我们的构造过程是矛盾的,所以假设不成立。

        那么如何确定每次比较最多次,移动最多次的序列为逆序序列呢?

        很简单,如果不是每次的都是逆序,那么一定存在某次选择未搜索到第0个位置就停止前向搜索了,因为找到了一个不小于(或者不大于)第i个记录的记录。

        证毕。

        最大移动次数:

          

        最大比较次数:

          

  2.2其他插入排序

      折半插入排序,由于插入排序的基本操作是在一个有序表中进行查找和插入,那么我们可以用折半查找来实现查找操作。

      算法的正确性证明与直接插入排序一致,只是查找的过程不同。

      

bInsertSort

       

       算法时间复杂度:

         虽然查找过程的时间复杂度为

            ,  当i足够大时

         但是最大移动次数不变,所以算法时间复杂度依旧为

            

  2.3希尔排序

    先将整个待排序列分割成若干个子序列,分别进行插入排序,等整个序列基本有序,再对全体进行一次直接插入排序。

    它的证明涉及到数学上一些尚未解决的难题,有兴趣可以查看相关论文,一般不用这个算法内部排序。

3.交换排序

   3.1冒泡排序           

      首先将第1个位置的关键字和第2个记录的关键字比较,若为逆序,即第一个位置的关键字比第二个位置的关键字大,则将两个记录交换,然后比较第2个位置的关键字和第3个位置的关键字的大小,若为逆序,则交换记录,以此类推。

       简单证明:

        假设含n个记录的序列为{R1,R2,R3,...,Rn},其相应的关键字序列为{K1,K2,K3,...,Kn},

        第1步从第1个记录开始,依次与最近的后面位置的记录比较关键字大小,若为逆序,则交换,当第n-1个记录与第n个记录比较关键字 之后,关键字最大的记录一定在第n个位置(为什么呢?你想嘛,每次都把关键字较大的往后移,比较完后当然是最大的在最后面了),有序序列为第n个记录

        假设第n-2步从第1个记录开始到第1个记录结束,每个记录与最近的后面位置的记录比较关键字大小,若为逆序,则交换,比较完并且将关键字较大的记录加入有序序列后,有序序列为第3个记录到n个记录

         第n-1步时,由于无序序列只有前面两个,比较一次,直接加入有序序列,加入后,有序序列为第1个记录到第n个记录。

        证毕。

      

bubbleSort

          算法时间复杂度:

         若含n个记录的序列{R1,R2,R3,...,Rn}按关键字大小比较,为逆序,则总的时间复杂度最高。

         ,所以总的时间复杂度为

            

    3.2快速排序

      通过一趟排序将待排的记录分割成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。

      这里体现了一个算法策略--分治法。将一个问题划分为一个个子问题,再分别求解子问题,最后合并起来就是原问题的解。

      具体实现:首先任意选取一个记录,通常是第一个记录,作为枢轴pivot。定义两个指针low和high,分别指向起始段和末端。high从所指的位置开始向前搜索找到第一个关键字小于pivot的记录,然后low所指位置起向后搜索,找到第一个关键字大于pivot的记录,再交换low和high所指向的位置的记录(low<high),重复这一步骤,直至low等于high。

      

qSort

        算法时间复杂度分析:

        ,

        从快速排序的过程我们可以得到下面的方程:

        最坏情况:

            T(n)=T(n-1)+n    (n>=2,n∈N+,为什么是这个范围呢,等于1的时候直接退出,不用比较;只有一个记录比较啥)

            T(1)=0,T(2)=2;

            T(n)为总的问题的工作量,n为划分过程的工作量。算法的时间复杂度分析,一般关心的是关键语句的执行次数,这里的比较操作是关键语句。说明叫关键语句呢,执行次数最多的语句就叫关键语句。

            为什么划分过程中的工作量为n呢,必须的啊,无论你怎么玩,每次都要将枢轴与枢轴所在序列的所有记录的关键字比较大小,直至low等于high。也就是总共进行了n次比较。

            T函数中的自变量表示该问题下的序列长度。

            最坏情况,也就是,你每次苦逼的比较了所有的数,但是每次都只得到一个子序列,仅比序列长度少1。

            这个递推方程是不是很熟悉,无论你用递归树还是累加法都可以轻而易举得到

              

           所以最坏情况的时间复杂度为O(n2)。

      最好情况:

          T(n)=2T(n/2)+n

          每次得到两个相等的子序列。为什么呢?假设每次划分后的两个子序列分别为a和b,a+b表示这两个子序列在划分过程中的总工作量,想想大名鼎鼎的柯西不等式,你就知道当且仅当a=b时最小原式最小。

          由主定理可以轻松得到最好的时间复杂度,不过,知其然知其所以然更好,我们就走走推导过程。

          

          所以最好的时间复杂度为O(nlog2n)

4.测试

  完整代码

    

    

#include<stdio.h>
#include<stdlib.h>
#include<conio.h>
#define  MaxSize 100
typedef struct _recordType
{
    int key;
    int name;
}recordType;
typedef struct _seqList
{
    recordType r[MaxSize + 1];
    int length;
}seqList;
void insertSort(seqList *l)
{
    for (int i = 2; i <= l->length; i++)
        /*
        初始选择第1个元素为有序序列,第一步选取该序列的第2个元素
        */
    {
        if (l->r[i].key < l->r[i - 1].key)    /*
                                            首先比较第i个元素与第i-1个元素的大小,如果第i个元素小于第i-1元素,则交换
                                            */
        {

            l->r[0] = l->r[i];
            l->r[i] = l->r[i - 1];

            int j = 0;

            for (j = i - 2; l->r[0].key < l->r[j].key; --j)
                /*
                再从第i-2个元素向前搜索,在搜索过程中,只要遇到大于第i个元素的元素,就将当前元素后移一个位置,直至某个元素小于或者等于第i个元素或者向前搜索到了第0个位置
                */
            {
                l->r[j + 1] = l->r[j];
            }
            l->r[j + 1] = l->r[0];

        }
    }
}
void bInsertSort(seqList *l)
{
    int low = 0;
    int high = 0;
    int m = 0;
    for (int i = 2; i <=l->length; i++)
        /*
        初始选择第1个元素为有序序列,第一步选取该序列的第2个元素
        */
    {
        l->r[0] = l->r[i];
        low = 1;
        high = i - 1;
        while (low <= high)
            /*
            折半查找
            */
        {
            m = (low + high) / 2;
            if (l->r[0].key < l->r[m].key)high = m - 1;
            else
                low = m + 1;
        }
        for (int j = i - 1; j >= high + 1; --j)
        {
            l->r[j + 1] = l->r[j];
        }
        /*
        找到对应插入位置,将对应插入位置到第i-1个位置的元素后移
        */
        l->r[high + 1] = l->r[0];
    }
}
void bubbleSort(seqList *l)
{
    recordType tmp;
    bool swap = false;
    for (int i = 0; i < l->length-1; i++)/*

    总共进行n-1步
                                       */
    {
        swap = false;
        for (int j = 1; j <l->length - i; ++j)
            /*
            每一步从第1个元素开始到第n-i-1元素结束,每个元素与最近的后面位置比较
            关键字大小,若为逆序则交换
            */
        {
            if (l->r[j].key > l->r[j + 1].key)
            {
                swap = true;
                tmp = l->r[j + 1];
                l->r[j + 1] = l->r[j];
                l->r[j] = tmp;
            }
        }
        if (swap == false)break;
        /*
        如果在比较关键字大小时没有记录移动,则说明前面的记录已经按关键字有序

        */
    }
}
int partition(seqList *l, int low, int high)
{
    l->r[0] = l->r[low];
    /*
    在某趟快速排序中,首先任意选取一个记录,通常是第一个记录,作为枢轴pivot
    */
    recordType tmp;
    while (low < high)
        /*
        如果low==high,说明某趟快速排序已经搜索完所有记录并找到了枢轴的恰当位置
        使得枢轴左边的记录的关键字都小于等于枢轴右边记录的关键字都大于等于枢轴

        */
    {

        while ((low < high)&&(l->r[0].key > l->r[low].key))++low;
        while ((low < high)&&(l->r[0].key < l->r[high].key))--high;
        if (low <high)
        {
            tmp = l->r[low];
            l->r[low] = l->r[high];
            l->r[high] = tmp;
        }
        low = low + 1;
        high = high - 1;
    }

    return low;
}
void qSort(seqList *l, int low, int high)
{
    /*

    将某个序列递归的分为多个子序列,直至子问题的规模不超过零
    */
    if (low < high)
    {
        int pivotPos = partition(l, low, high);/*将序列l->[low...high]一分为二*/

        qSort(l, pivotPos + 1, high);/*对高子表递归排序*/
        qSort(l, low, pivotPos - 1);/*对低子表递归排序*/
    }
}
int sum(seqList *l,int n)
{
    if (n > 0)
        return sum(l, n - 1) + l->r[n].key;
    else
        return 0;
}
int main(void)
{
    freopen("in.txt", "r", stdin);
    seqList *l = (seqList*)malloc(sizeof(seqList));
    scanf("%d", &l->length);
    for (int i = 1; i <= l->length; i++)
    {
        scanf("%d", &l->r[i].key);
    }
    insertSort(l);
    printf("Straight Insertion Sort:\n");
    for (int i = 1; i <= l->length; i++)
    {
        printf("%d     ", l->r[i].key);
    }
    printf("\n");
    fseek(stdin, 0, 0);
    scanf("%d", &l->length);
    for (int i = 1; i <= l->length; i++)
    {
        scanf("%d", &l->r[i].key);
    }
    bInsertSort(l);
    printf("Binary Insertion Sort:\n");
    for (int i = 1; i <= l->length; i++)
    {
        printf("%d     ", l->r[i].key);
    }
    printf("\n");

    fseek(stdin, 0, 0);
    scanf("%d", &l->length);
    for (int i = 1; i <= l->length; i++)
    {
        scanf("%d", &l->r[i].key);
    }
    bubbleSort(l);
    printf("Bubble Sort:\n");
    for (int i = 1; i <= l->length; i++)
    {
        printf("%d     ", l->r[i].key);
    }
    printf("\n");

    fseek(stdin, 0, 0);
    scanf("%d", &l->length);
    for (int i = 1; i <= l->length; i++)
    {
        scanf("%d", &l->r[i].key);
    }
    qSort(l, 1, l->length);
    printf("Quick Sort:\n");
    for (int i = 1; i <= l->length; i++)
    {
        printf("%d     ", l->r[i].key);
    }
    printf("\n");
    printf("%d\n", sum(l, l->length));

    getch();
}

测试文本:

  

测试结果

  

时间: 2024-10-10 01:57:31

在这个浮躁的社会,很多人只是粘贴一份算法,跑一边,就算懂了,我们应该沉下心来的相关文章

走出浮躁的泥沼:浮躁的社会原因 控制好自己的物欲

http://www.nowamagic.net/librarys/veda/detail/2265现在这个社会,大家都很浮躁.简单说,因为他是消费者. 具体的逻辑推理是这样的: 1. 现在的社会是一个“富裕社会”(Affluent Society),物质极大丰富,大部分的生产力都被用来生产消费品. 2. “富裕社会”的存在和发展,依赖于大量的商品不断被生产出来,然后被消费掉.如果生产和消费停滞,“富裕社会”就会崩溃. 3. 为了促进消费,就必须鼓励每个公民进行消费,必须把消费变成人的本能和欲望

很多人,被这句话害了一辈子

点击上方蓝字关注我们,夜读与你一起砥砺前行! 21君:小时候,你可听过“这孩子特聪明,就是学习不用心,他要是认真起来,学习成绩肯定好.”这句话么?是对你说的,还是对你身边的人说的?很多人都觉得这是一个夸奖,但其实给孩子这样的一个想法,其实是害了孩子.自作聪明的人,往往还没有学会正视自己与他人的努力和付出,就已经喜欢了那种轻盈获胜的姿态.为了姿态好看,他们宁可输,也不想通过努力制胜.来源:衷曲无闻(zhongquwuwen) 8:48 很多人,被这句话害了一辈子 来自21世纪经济报道   特别鸣谢

为什么很多人会特别执着于对错?

作者:刘未鹏 原文地址:https://weibo.com/1882579600/G0a85vAhg?filter=hot&type=comment 大佬博客:http://mindhacks.cn/ 为什么很多人会特别执着于对错?我觉得有两个深刻的原因: 在很多家庭中是没有足够明确的个人边界的,但人又天然需要维护自己的权利,当不能说「你管不着」的时候,就只能用「我是对的,你是错的」来挡住对方的入侵. 人性永远会寻找出口,当一种文化不允许人去谈权利,那人必然会发明出新的对策来,这种对策就是讲对错

为什么很多人吐槽谭浩强的C语言程序设计?

这个观点在网络上已经论战了很长时间,出现这种情况还是历史遗留问题,老谭出的C语言教材几乎是国内的第一版,由于中英翻译问题或者对编程文化理解的差异性导致出来的书多多少少存在一些纰漏.不能只是记住其缺点,老谭书最主要在那个年代起到了普及C语言的作用,这已经足够了,如同做一个产品,首先解决的是有没有问题,最后才是细节完善阶段.最后,如果大家如果在自学遇到困难,想找一个C++的学习环境,可以加入我们的C++学习圈,点击我加入吧,会节约很多时间,减少很多在学习中遇到的难题. 为什么很多人吐槽谭浩强的C语言

如何选择编程入门语言?一个让很多人纠结的问题

用心分享,共同成长 没有什么比你每天进步一点点更实在了 本文已经收录至我的github,欢迎大家踊跃star 和 issues. https://github.com/midou-tech/articles 点关注,不迷路!!! 为什么会出这样一篇文章? ?本来想写点最近学习的东西,但是最近好多粉丝再问我一个问题. image-20200105164345384 于是我决定把我这个过来人的一些建议给到你们,希望大家少一些不必要的迷茫,多花点时间去学习该学习的东西,去做一些重要的事情. 刚开始学习

上传伪技术~很多人都以为判断了后缀,判断了ContentType,判断了头文件就真的安全了。是吗?

今天群里有人聊图片上传,简单说下自己的经验 0.如果你的方法里面是有指定路径的,记得一定要过滤../,比如你把 aa文件夹设置了权限,一些类似于exe,asp,php之类的文件不能执行,那么如果我在传路径的时候,前面加了一个../呢,这样这种服务器端的限制就跳过了.(DJ音乐站基本上都有这个问题,以及用某编辑器的同志) 1.常用方法:这种就是根据后缀判断是否是图片文件,需要注意的是这种格式:文件:1.asp;.jpg  1.asp%01.jpg  目录: 1.jpg/1.asp 1.jpg/1.

51cto 真好!我又回到了这里!! 看到很多人还在!小废物、小松等等小伙伴还在吗?

呵呵,好几年过去了,发生了好多故事!  现在大家都有很多的改变吧? 作为同龄人的小废物等人现在过得怎么样了? 大家都还好吗?? 51cto 真好!我又回到了这里!! 看到很多人还在!小废物.小松等等小伙伴还在吗?,布布扣,bubuko.com

为什么知乎上很多人都反对创业?(上战场的士兵如果先拿枪打打靶练练枪法,研究研究战术之后,战损比肯定要更好看一点)

为什么知乎上很多人都反对创业? 经常看到一些排名靠前的答案,内容无非规劝他人不要创业,要脚踏实地,认为党和国家大众创业万众创新的政策方针都是胡扯?难道知乎上的知识分子们竟比国家的智囊更有远见?还是他们的认识层次达不到? 著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处.作者:唐缺链接:https://www.zhihu.com/question/37559307/answer/72755062来源:知乎 -------------------------------------

很多人都在埋怨没有遇到好的团队,但好的团队不可能凭空出现,一流的团队不能仅靠团队成员努力,作为Leader,要有可行的规划,并坚定地执行、时势地调整(转)

<西游记>中的唐僧团队历经千难万险,终于求得真经,目标明确.分工合理为这支队伍最终走向成功奠定了基础.唐僧从一开始,就为这个团队设定了西天取经的目标,虽然经历各种挫折与磨难,但目标从未动摇.悟空探路.八戒牵马.沙僧挑担,几位徒弟一起肩负着保护唐僧的任务.虽然性格迥异.各有缺点,但目标分解合理及成员分工合作,最终风雨同舟,取得真经. <西游记>的故事引申到实际团队技术管理中,也一样有借鉴意义,本文作者为CTO俱乐部会员.湖北同城一家网络科技有限公司开发总监杨斌,他结合自己多年经历谈谈