論秋招中的排序(排序法彙總-------上篇)

  論秋招中的排序(排序法彙總-------上篇)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的取值是多少,應取多少個不一樣的值,都沒法確切知道,只能憑經驗來取(即不一樣的步長致使算法效率不一樣)。

相關文章
相關標籤/搜索