你所能用到的數據結構(二)


      周末開始更新了,首先感謝各位對我寫的東西還能保持興趣,先回答幾個留言中的一個問題和我對無損編碼那一節的一個留言的一個看法,第一個是推薦算法書,首先,我不是什么高手和大牛,所以當不起“推薦”這個詞。我見過很多人,對於這個問題我覺得很多人都會說出《算法導論》,但是我不完全這么認為,我始終認為人和人是不一樣的,《算法導論》肯定是一本經典的書,但是學習知識的目的是要學懂,比誰的能力大不是比誰看的經典書籍多,而是比誰懂得多。所以如果讓我推薦的話,我覺得要分三種情況,第一種,你有很深的數學基礎,高中特別喜歡做數學題,我覺得可以嘗試看看《算法導論》甚至是傳說中的《計算機程序設計藝術》,第二種就是高中並不對數學很感興趣,但是大學確實很喜歡編程但是對於數學看多了覺得頭會昏昏的人,其實我特別覺得好的是《算法概論》和《算法設計與分析基礎》,除了這兩本,我覺得看一些《離散數學》對於培養算法的基本思維方式真很有幫助,第三種是對於數學理解不是那么強的,我覺得不妨看看《編程之美》這類的有實際例子不會那么抽象的書,培養出興趣,有了興趣,后面只是時間問題了。

      第二個是對無損編碼有一個朋友說的這是“時間換空間”,從編程上說確實是時間換空間,但是這些一般都是在有實際應用背景下的,比如我的所謂研究方向是“可逆水印”,這個玩意兒必須要靠無損編碼做前期處理,所以這個看法是正確的,但是卻不是最需要考慮的,因為這個有應用背景。

      好了,開始這次的內容,不同於很多數據結構先從列表和堆棧說起,我先幾個和數據結構結合不太強的排序算法開始,因為我覺得在學這一系列之前對算法的效率建立一個認識是必要的。

二、從幾種“屌絲”的排序算法開始

      先介紹幾種簡單的時間效率是O(N*N)的排序算法,雖然這些排序算法比較初級,但是任何高級的東西都是從初級的東西演化推理出來的,不學小學的123你也不可能學會高數的微積分。所以說這些算法雖然“屌絲”,但是沒有這些“屌絲”哪來的“高帥富”?可以證明的出來,通過交換的排序算法效率最快只能到O(N*logN)。

      除了上面的這個,我還想強調的一點是在有的書里面提到的排序算法是否是穩定的這個概念,所謂有序就是說待排序數列中相等的值在經過排序算法之后是否會交換位置,比如一組數據(2,0,2),在排序之后自然是(0,2,2),如果這個排序算法是穩定的,那么(0,2,2)中第一個出現的2應該是(2,0,2)中的第一個2,也就是待排序數列中的兩個2的相對位置不會發生交換。關於穩定的定義可以參看百度的這個http://baike.baidu.com/view/547325.htm?fromTaglist 這個看似不起眼的知識點就是昨天微軟校園招聘倒數第二題的考點,不得不說大公司出的題目牛逼。

     選擇排序是最為符合人類自然思維的一種排序方式,不斷找到數列中的最小值,將其放在目前最小的位置上,簡單方便快捷,不得不說是最“屌絲”的排序算法,沒有什么特殊的技巧,完全靠兩個循環,代碼如下所示:

    

選擇排序
 1 void SelectionSort(int a[],int length)
 2 {
 3     for(int i=0;i<length;i++)
 4     {
 5         cout<<"Sort time number "<<(i+1)<<":";
 6                int min=i;        
 7         int j=i;
 8         for(;j<length;j++)
 9         {
10             if(a[min]>a[j])
11                min=j;
12         }
13         int tmp=a[min];
14         a[min]=a[i];
15         a[i]=tmp;
16 
17         for(int k=0;k<length;k++)
18             cout<<a[k]<<" ";
19         cout<<endl;
20 
21     }
22 }

    運行結果如下所示:

   

    從結果中可以看到,第一輪排序中,最小的元素3被安排在了第一個位置上,第二輪5被安排在次小的位置上,以此類推。選擇排序算法是不是穩定的呢?選擇排序明顯不是有序的,就像連接里面寫的那樣,因為你在交換的過程中,如果出現相同的元素,無法保證交換的過程中元素被交換的位置是否在第二次出現該元素之后。

    接下來當然是冒泡排序,這種排序算法通過不斷比較相鄰兩個數的大小並交換位置來達到排序的目的,這種算法比較符合人類的自然思維,這樣就自然需要兩個循環,第一個循環是遍歷整個數組,第二個循環是不停地未排序的數列中兩個相鄰的元素,每一輪之后,最大的元素都可以排在目前最大的位置(當然這取決於你的比較部分的代碼是如何寫的),所以這個算法也很容易寫出來。

     

冒泡排序
 1 void bubble(int a[],int length)
 2 {
 3     int temp=0;
 4     for(int j=0;j<length;j++)
 5     { 
 6        cout<<"Sort time number "<<(j+1)<<":";
 7       
 8        for (int i=0;i<length-1;i++)
 9         if (a[i]>a[i+1])
10         { 
11             temp=a[i];
12             a[i]=a[i+1];
13             a[i+1]=temp;
14         }
15         for(int k=0;k<length;k++)
16          cout<<a[k]<<" ";
17         cout<<endl;
18     }
19 
20 } 

     其結果如下圖所示:

    

     這個簡單的排序算法卻反應出了排序算法的一個最初級的思路就是比較和交換,別小看任何不起眼的概念,因為后面大牛們對於排序算法的改進就是通過相近辦法減少比較和交換,或者是不做相鄰元素的比較。可以看到每一趟排序之后,當前最大的元素都到了當前最大的位置上,冒泡排序算法是穩定的排序算法,但是前提是你不會在比較的過程中使用<=或者>=這種符號,交換兩個相等的元素是沒有意義的,加上算法一般是到大數據集中才顯示出作用,這些環境下一個重要的因子就是要快,這種交換會無謂的增大程序的開銷,所以一般在排序算法中不會用到=的交換,你也就不用鑽牛角尖的說排序算法的穩定性會取決於用於比較的符號。

      除了冒泡排序,插入排序我覺得是最符合人的排序自然思維方式的拓展,這個算法的思想基本就是不停地排出有序的數列,比如10個數,先將前兩個數的順序排好,再看第三個數,然后將這三個數排好,不斷吸收元素,不斷的排序,這樣會保持了待排序數列的相對穩定性,也保持了算法排序的穩定性。其實現代碼如下:

插入排序
 1 void InsertSort(int numbers[],int array_size)
 2 {
 3     int j;
 4     for(int p=1;p<array_size;p++)
 5     {
 6         cout<<"Sort time number "<<p<<":";
 7         int tmp=numbers[p];
 8         
 9         for(j=p;j>0&&tmp<numbers[j-1];j--)
10         {
11              numbers[j]=numbers[j-1];
12         }
13         numbers[j]=tmp;
14        for(int k=0;k<array_size;k++)
15          cout<<numbers[k]<<" ";
16         cout<<endl;
17     }
18 }

      實現結果如下圖所示:

    

     可以看到這種排序算法,吸收,排序的特點,最開始是兩個元素的數組編程有序的,第二次是3個元素變成的,如此往復。

     這三種最簡單的排序算法是排序算法的入門,如果想測試其效率,可以使用我上一節的代碼,這里我還要說一句的是,我寫的這三段代碼參數的命名都是不一樣的,這是因為我是好幾天寫的,這也反過來說明了在軟件開發中有一個規范的重要性,試想如果是很多人一起開發軟件,一樣的參數意思,你今天想到用着表示,明天想到用那個表示,對於別人這是一種莫大的痛苦。

    


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM