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

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

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

  待续算法

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

 

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

  1、关于排序的概念大数据

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

    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)

      插入排序code

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

  2、算法讲解与实现部分htm

    一、简单选择排序

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

                

  代码以下:  

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; } } }

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

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

 

    二、插入排序

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

              

    代码以下:    

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;
    }
}

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

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

    

    三、冒泡排序

      大体思路:遍历待排序的数据,若是邻近的两个元素大小顺序不对,就将两者进行交换操做,重复此操做直到全部数据排序好为止。图解以下:

      

    代码以下:

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次交换操做 

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

 

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

      大体思路:是直接插入排序算法的一种更高效的改进版本。它容许非相邻的元素进行交换来提升执行效率。先取一个小于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的取值是多少,应取多少个不一样的值,都没法确切知道,只能凭经验来取(即不一样的步长致使算法效率不一样)。

相关文章
相关标签/搜索