【坐在馬桶上看算法】小哼買書

        以前講了三種經常使用的經典排序。排序算法還有不少,例如選擇排序、計數排序、基數排序、插入排序、歸併排序和堆排序等等。堆排序是基於二叉樹的排序,之後再說吧。先分享一個超酷的排序算法的視頻。
 
 
        再來看一個具體的例子《小哼買書》來看看三個排序在應用上的區別和侷限性。 小哼的學校要創建一個圖書角,老師派小哼去找一些同窗作調查,看看同窗們都喜歡讀哪些書。小哼讓每一個同窗寫出一個本身最想讀的書的ISBN號(你知道嗎?每本書都有惟一的ISBN號,不信話你去找本書翻到背面看看)。固然有一些好書會有不少同窗都喜歡,這樣就會收集到不少重複的ISBN號。小哼須要去掉其中重複的ISBN號,即每一個ISBN號只保留一個,也就說一樣的書只買一本(學校真是夠摳門的)。而後再把這些ISBN號從小到大排序,小哼將按照排序好的ISBN號去書店去買書。請你協助小哼完成「去重」與「排序」的工做。
 

 

        輸入有2行,第1行爲一個正整數,表示有n個同窗參與調查(n<=100)。第2行有n個用空格隔開的正整數,爲每本圖書的ISBN號(假設圖書的ISBN號在1~1000之間)。

 

        輸出也是2行,第1行爲一個正整數k,表示須要買多少本書。第2行爲k個用空格隔開的正整數,爲從小到大已排好序的須要購買的圖書ISBN號。
        例如輸入
10
20 40 32 67 40 20 89 300 400 15
        則輸出
8
15 20 32 40 67 89 300 400
 
 
        最後,程序運行的時間限制爲:1秒。
        解決這個問題的方法大體有兩種,第一種方法:先將這n個圖書的ISBN號去重,再進行從小到大排序並輸出。第二種方法:先從小到大排序,輸出的時候再去重。這兩種方法均可以。
        先來看第一種方法。經過第一節的學習咱們發現桶排序稍加改動正好能夠起到去重的效果,所以咱們可使用桶排序的方法來解決此問題。
#include <stdio.h>
int main()
{
    int a[1001],n,i,t;
    for(i=1;i<=1000;i++)
         a[i]=0; //初始化
   
    scanf("%d",&n); //讀入n
    for(i=1;i<=n;i++) //循環讀入n個圖書的ISBN號
    {
        scanf("%d",&t); //把每個ISBN號讀到變量t中
        a[t]=1; //標記出現過的ISBN號
    }
   
    for(i=1;i<=1000;i++) //依次判斷1~1000這個1000個桶
    {
         if(a[i]==1)//若是這個ISBN號出現過則打印出來
             printf("%d ",i);
    }
    getchar();getchar();
    return 0;
}
 
        這種方法的時間複雜度是就是桶排序的時間複雜度爲O(N+M)。
 
        第二種方法咱們須要先排序再去重。排序咱們能夠用冒泡排序或者快速排序。
        20 40 32 67 40 20 89 300 400 15
 
        將這10個數從小到大排序以後爲 15 20 20 32 40 40 67 89 300 400。
接下來,要在輸出的時候去掉重複的。由於咱們已經排好序,所以相同的數都會緊挨在一塊兒。只要在輸出的時候,預先判斷一下當前這個數a[i ]與前面一個數a[i-1]是否相同。若是相同則表示這個數以前已經輸出過了,不一樣再次輸出。不一樣則表示這個數是第一次出現須要,則須要輸出這個數。
#include <stdio.h>
int main()
{
    int a[101],n,i,j,t;
   
    scanf("%d",&n);   //讀入n
    for(i=1;i<=n;i++) //循環讀入n個圖書ISBN號
    {
        scanf("%d",&a[i]);
    }
   
   //開始冒泡排序
    for(i=1;i<=n-1;i++)
    {
        for(j=1;j<=n-i;j++)
        {
            if(a[j]>a[j+1])
            {  t=a[j]; a[j]=a[j+1]; a[j+1]=t;  }
        }
    }
    printf("%d ",a[1]); //輸出第1個數
    for(i=2;i<=n;i++) //從2循環到n
    {
         if( a[i] != a[i-1] ) //若是當前這個數是第一次出現則輸出
             printf("%d ",a[i]);
    }
    getchar();getchar();
    return 0;
}

 

        這種方法的時間複雜度由兩部分組成,一部分是冒泡排序時間複雜度是O(N2),另外一部分是讀入和輸出都是O(N),所以整個算法的時間複雜度是O(2*N+N2)。相對於N2來講,2*N能夠忽略(咱們一般忽略低階),最終該方法的時間複雜度是O(N2)。
 
        接下來咱們還須要看下數據範圍。每一個圖書ISBN號都是1~1000之間的整數,而且參加調查的同窗人數不超過100,即n<=100。以前已經說過,在粗略計算時間複雜度的時候,咱們一般認爲計算機每秒鐘大約運行10億次(固然實際狀況要更快)。所以以上兩種方法均可以在1秒鐘內計算出解。若是題目圖書ISBN號範圍不是在1~1000之間,而是-2147483648~2147483647之間的話,那麼第一種方法就不可行了,由於你沒法申請出這麼大數組來標記每個ISBN號是否出現過。另外若是n的範圍不是小於等於100而是小於等於10萬,那麼第二種方法的排序部分也不能使用冒泡排序。由於題目要求的時間限制是1秒,使用冒泡排序對10萬個數的排序,計算機須要運行100億次,須要10秒鐘,須要替換爲快速排序,快速排序只須要100000×log2100000≈100000×17≈170萬次,這還不到0.0017秒。是否是很神奇,一樣的問題使用不一樣的算法居然有如此之大的時間差距,這就是算法的魅力!
 
        咱們來回顧一下本章三種排序算法的時間複雜度。桶排序是最快的,它的時間複雜度是O(N+M);冒泡排序是O(N2);快速排序是O(NlogN)。
 
碼字不容易啊,轉載請標明出處^_^.
相關文章
相關標籤/搜索