当前位置: 首页 > news >正文

论秋招中的排序(排序法汇总-------上篇)

  论秋招中的排序(排序法汇总-------上篇)

  论秋招中的排序(排序法汇总-------中篇)

  待续

  (原创,转发须注明原处)

 

  算法是个很奇妙的东西,不管在招聘的笔试还是面试中,也不管你是学的什么语言或者什么方向的编程,出题者或面试官总会问问,尤其是关于排序算法的问题,对滴,在这次秋招中,我就被面试官问了无数次(有点夸张)。下面,我就好好总结总结下,我相信,以后绝对有人用得上的,方便点。。。

  一、关于排序的概念

  对的,就是概念,要学好一个东西,必须要先搞清概念,别一上来就看题敲代码,这是初学者浮躁的表现(我当初就是这样的),总想急于求成,想快点做点东西,当时确实学到了不少零碎的知识,但随着内容的深入与扩充,发现自己学的东西变得多而杂了,于是,又花更多的时间去理清知识体系。所以,我劝大家应该有个好的起点,从开始就弄清自己到底要学啥,学了些啥,有个清晰的知识体系会有助于我更好的深入。废话就不多说了。。。

    1. 内排序与外排序:简单地说就是,如果被排序的文件或数据适合放在内存中,则该排序就是内排序;而从磁盘或磁盘上进行排序的方法,则称为外排序。二者主要的区别:内排序可以很容易地访问任意元素,而外排序则必须顺序访问元素(至少大数据块是这样的)。
    2. 稳定排序与不稳定排序:稳定排序就是就是能保证排序前2个相等的数其在序列的前后位置顺序和排序后它们两个的前后位置顺序相同;不稳定排序则就相反了。(注:选择排序、快速排序、希尔排序、堆排序不是稳定的排序算法,而冒泡排序、插入排序、归并排序和基数排序是稳定的排序算法)
    3. 时间复杂度与空间复杂度:(这2个概念就不讲了,我就列出部分排序算法的时间复杂度和空间复杂度吧)

      排序法

      最差时间分析平均时间复杂度稳定度空间复杂度
      冒泡排序O(n2)O(n2)稳定O(1)
      快速排序O(n2)O(n*log2n)不稳定O(log2n)~O(n)
      选择排序O(n2)O(n2)稳定O(1)
      二叉树排序O(n2)O(n*log2n)不一顶O(n)

      插入排序

      O(n2)O(n2)稳定O(1)
      堆排序O(n*log2n)O(n*log2n)不稳定O(1)
      希尔排序OO不稳定O(1)
    4. 排序算法所要消耗额外内存的形式
  • 在原位(即原来占有的内存)进行排序的算法,除了使用一个小堆栈或表外,不需要任何额外的内存空间
  • 使用链表表示或指针、数组索引来引用数据的排序算法,因此,需要额外的内存空间存储这个指针或索引即可
  • 需要足够的额外空间来存储要排序的数组的副本数据
排序算法访问元素的方法:可以通过访问关键字来进行比较;或者访问整个元素来移动。因此,如果需要访问的元素比较大,我们可以通过使用一个指针数组(或者索引)来操作,而不是直接对元素进行移动,最后只需要保存指针数据就可以了。

  二、算法讲解与实现部分

    1、简单选择排序

      大致过程:首先,选出数组中最小的元素,将它与数组中第一个元素进行交换,然后找出次小的元素,并将它与数组中第二个元素进行交换。按照这种方法一直进行下去,知道整个数组排序完为止。图解如下:

                

  代码如下:  

void selectsort(int a[],int length)
{
    int i, j;
    for (i = 0; i < length; i++)
    {
        int min = i;
        for (j = i + 1; j < length; j++)
            if (a[j] < a[min])
                min = j;
        if (min != i)
        {
            int t = a[i];
            a[i] = a[min];
            a[min] = t;
        }
    }
}

     缺点:比较次数多,不能利用数据的已有序的特点,对已有序的部分依赖弱

  优点:属于稳定排序,对于元素较大,但关键字又比较小的数据的排序,选择排序较适合

 

    2、插入排序

      大致思路:每次将一个待排序的元素与已排序的元素进行逐一比较,直到找到合适的位置按大小插入。

              

    代码如下:    

void insertion(int a[], int length)
{
    int i, j;
    for (i = 1; i < length; i++)
        for (j = i; j > 0; j--)
            if (a[j - 1] > a[j])
            {
                int t = a[j - 1];
                a[j - 1] = a[j];
                a[j] = t;
            }
}

    改进下:

void insertion(int a[], int length)
{
    for (int i = 1; i < length; i++)//从第2个数据开始插入  
    {
        int j = i - 1;
        int t = a[i];//记录要插入的数据  
        while (j >= 0 && a[j] > t)//从后向前,找到比其小的数的位置 
            a[j + 1] = a[j--];//向后挪动  
        if (j != i - 1)//存在比其小的数  
            a[j + 1] = t;
    }
}

    优点:稳定,并且对数据的初始排列顺序很敏感,如果数据量很大时,且关键字已经部分排序,则插入排序速度比较快

    缺点:对于顺序结构的数据,比较次数不一定,比较次数越少,插入点后的数据移动越多,特别是当数据总量庞大的时候。

    

    3、冒泡排序

      大致思路:遍历待排序的数据,如果邻近的两个元素大小顺序不对,就将二者进行交换操作,重复此操作直到所有数据排序好为止。图解如下:

      

    代码如下:

void bubble(int a[], int length)
{
    for(int i=0;i<length-1;i++)//进行(n-1)次
        for(int j=length-1;j>i;j--)
            if(a[j-1]>a[j]) 
            {
                int t = a[j];
                a[j] = a[j - 1];
                a[j - 1] = t;
            }
}

     改进:

void bubble(int a[], int length)
{
    //如果a[0..i]已是有序区间,上次的扫描区间是a[i..length],
    //记上次扫描时最后一次执行交换的位置为pos,
    //则pos在i与length之间,则a[i..pos]区间也是有序的,
    //否则这个区间也会发生交换;所以下次扫描区间就可以由a[i..length] 
    //缩减到[pos..n]
    int pos = 0, tpos = 0;
    for (int i = 0; i < length - 1; i++)
    {
        pos = tpos;
        for (int j = length - 1; j > pos; j--)
        {
            if (a[j - 1] > a[j])
            {
                int t = a[j];
                a[j] = a[j - 1];
                a[j - 1] = t;
                tpos = j;
            }
        }
        //如果经过一次扫描后位置没变则表示已经全部有序
        if (pos == tpos)
            break;
    }
}

     附上双向冒泡(抖动冒泡):

void ImpButtle(int a[], int length)
{
    //先让冒泡排序由左向右进行,再来让冒泡排序由右往左进行"抖动"排序
    int left = 0, right = length - 1, i, flag=0;
    while (left < right)
    {
        for(i=left;i<right;i++)
            if (a[i] > a[i + 1])
            {
                int t = a[i];
                a[i] = a[i + 1];
                a[i + 1] = t;
                flag = i;
            }
        right = flag;
        for(i=right;i>left;i--)
            if (a[i - 1] > a[i])
            {
                int t = a[i - 1];
                a[i - 1] = a[i];
                a[i] = t;
                flag = i;
            }
        left = flag;
    }
}

   注:插入排序不能预知各个数据在数组中的最终位置,选择排序不会改变已排序好的数据的位置。插入排序每步中要平均移动已排序好的数据的一般长度,选择排序和冒泡排序每步都要访问所有尚未排序的数据,其中,冒泡排序将所有顺序不对的相邻的元素进行交换,而选择排序每步中只交换一次。

  选择排序:N^2/2次比较操作   N次交换操作;插入排序:平均情况下,大约N^2/4次比较操作   N^2/4次交换操作,在最坏情况下都需要2倍的数量;冒泡排序:在平均和最坏情况下,执行大约N^2/2次比较操作和N^2/2次交换操作 

   对于数据项较大,关键字较小的数据,选择排序的运行时间是线性的

 

    4、希尔排序(缩小增量排序)

      大致思路:是直接插入排序算法的一种更高效的改进版本。它允许非相邻的元素进行交换来提高执行效率。先取一个小于n的整数d1作为第一个增量,把文件的全部记录分组。所有距离为d1的倍数的记录放在同一个组中。先在各组内进行直接插入排序;然后,取第二个增量d2<d1重复上述的分组和排序,直至所取的增量 =1( < …<d2<d1),即所有记录放在同一组中进行直接插入排序为止。图解如下:

              

    代码如下:

 

void Shellsort(int a[], int length)
{
    for(int gap=length/2;gap>0;gap/=2)//此处步长每次减半
        for(int i=gap;i<length;i++)//各组中分别进行插入排序
            for (int j = i - gap; j >= 0 && a[j] > a[j + gap]; j -= gap)
            {
                int t = a[j];
                a[j] = a[j + gap];
                a[j + gap] =t;
            }
}

    优点:快,数据移动少; 
    缺点:不稳定,d的取值是多少,应取多少个不同的值,都无法确切知道,只能凭经验来取(即不同的步长导致算法效率不同)。

转载于:https://www.cnblogs.com/czx1/p/7890646.html

相关文章:

  • Anaconda 安装tensorflow(GPU)
  • sudo的用法记录
  • 查看文件源代码功能实现
  • chomperwu
  • 在SpringMVC中使用拦截器(interceptor)拦截CSRF攻击
  • 一、网络的基本概念
  • 利用LVS-NAT和DR模型分别负载均衡一个php应用
  • VII Python(9)socket编程
  • zookeeper系列(七)实战分布式命名服务
  • Animations的使用
  • 利用Python生成随机4位验证码
  • 测试标准学习
  • 一些不常见的css知识
  • 第四课——MFC应用程序框架
  • 列表、元祖概述
  • 4月23日世界读书日 网络营销论坛推荐《正在爆发的营销革命》
  • - C#编程大幅提高OUTLOOK的邮件搜索能力!
  • CAP理论的例子讲解
  • ERLANG 网工修炼笔记 ---- UDP
  • Java精华积累:初学者都应该搞懂的问题
  • JDK9: 集成 Jshell 和 Maven 项目.
  • PyCharm搭建GO开发环境(GO语言学习第1课)
  • React 快速上手 - 06 容器组件、展示组件、操作组件
  • Sass Day-01
  • webpack项目中使用grunt监听文件变动自动打包编译
  • 互联网大裁员:Java程序员失工作,焉知不能进ali?
  • 时间复杂度与空间复杂度分析
  • 微服务框架lagom
  • 一份游戏开发学习路线
  • media数据库操作,可以进行增删改查,实现回收站,隐私照片功能 SharedPreferences存储地址:
  • AI又要和人类“对打”,Deepmind宣布《星战Ⅱ》即将开始 ...
  • #宝哥教你#查看jquery绑定的事件函数
  • (02)Cartographer源码无死角解析-(03) 新数据运行与地图保存、加载地图启动仅定位模式
  • (06)Hive——正则表达式
  • (Demo分享)利用原生JavaScript-随机数-实现做一个烟花案例
  • (NSDate) 时间 (time )比较
  • (solr系列:一)使用tomcat部署solr服务
  • (备忘)Java Map 遍历
  • (附源码)ssm基于微信小程序的疫苗管理系统 毕业设计 092354
  • (附源码)计算机毕业设计高校学生选课系统
  • (一)C语言之入门:使用Visual Studio Community 2022运行hello world
  • (一)Spring Cloud 直击微服务作用、架构应用、hystrix降级
  • (一一四)第九章编程练习
  • .gitignore文件---让git自动忽略指定文件
  • .MSSQLSERVER 导入导出 命令集--堪称经典,值得借鉴!
  • .Net - 类的介绍
  • .Net Core与存储过程(一)
  • .net oracle 连接超时_Mysql连接数据库异常汇总【必收藏】
  • .net 程序发生了一个不可捕获的异常
  • .net 打包工具_pyinstaller打包的exe太大?你需要站在巨人的肩膀上-VC++才是王道
  • .NET 将混合了多个不同平台(Windows Mac Linux)的文件 目录的路径格式化成同一个平台下的路径
  • .NET/C# 利用 Walterlv.WeakEvents 高性能地定义和使用弱事件
  • .net6Api后台+uniapp导出Excel
  • .NET国产化改造探索(三)、银河麒麟安装.NET 8环境
  • .secret勒索病毒数据恢复|金蝶、用友、管家婆、OA、速达、ERP等软件数据库恢复