數據結構與算法系列(2)基礎排序算法

 

 

 

基礎排序算法算法

——.NET數據結構與算法系列之二編程

追憶20131113數組

前言數據結構

在計算機中實現存儲數據最廣泛的兩種操做就是排序和查找。這是從計算機產業初始就已經確認的了。這意味着排序和查找也是計算機科學領域最值得研究的兩種操做。本書提到的許dom

多數據結構的主要設計目的就是爲了使排序和/或查找更加簡單,同時也是爲了數據在結構內的存儲更加有效。性能

本章會介紹有關數據排序和查找的基礎算法。這些算法僅依賴數組做爲數據結構,並且所採用的「高級」編程技術只是遞歸。本章還介紹了用來非正式分析不一樣算法之間速度與效率的方法,此方法貫穿全書。學習

 

1.排序算法測試

人們在平常生活中所接觸到的絕大多數數據都是通過排序的。好比,按照字母順序查詢字典中的定義。或者按照名字的字母順序在電話本中查詢電話號碼。再或者郵局會按照下列幾個大數據

步驟對郵件進行排序分發:即首先按照郵政編碼,而後再按照街道名稱,最後還要按照姓名。排序在數據處理中是十分基礎的過程,於是值得認真學習研究。正如先前提到的那樣,這裏對不一樣排序算法的操做有很是少許的分析研究。儘管已經對一些很是古老的算法作了改進,可是仍然應該先學習幾種簡單的排序算法。這些簡單算法就是插入排序算法、冒泡排序算法以及選擇排序算法。這些算法的每一種都很容易理解和實現。對於任意狀況而言這些算法不是最好的全面算法,可是對於少許數據集合或者其餘特殊狀況而言,它們是可用的最好算法。this

 

1.1數組類測試環境

爲了檢驗這些算法,首先須要構造一個能夠實現並測試算法的測試環境。這裏將構造一個類來封裝數組處理的一些常規操做,即元素插入操做,元素存取訪問操做,以及顯示數組內容的操組。下面就是程序的代碼:

 

在保留CArray 類以便開始檢測排序和查找算法以前,仍是先來討論一下如何在CArray 類對象內實際存儲數據的問題。爲了更有效地說明不一樣排序算法是如何運行的,數組內數據需

要隨機放置。最好的實現方法就是使用隨機數生成器來給數組的每一個元素進行賦值。在C#中用Random 類能夠產生隨機數。這種類型的對象能夠產生隨機數。爲了實例化Random 對象,須要給這個類的構造器傳遞一個種子。這裏把這個種子看做是隨機數生成器所能產生的隨機數範圍的上界。

下面另外看一個用CArray 類來存儲數的程序,並且採用了隨機數生成器來選擇存儲到數組內的數據:

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Threading.Tasks;

 

namespace Chapter2

{

    class Program

    {

        static void Main(string[] args)

        {

            CArray nums = new CArray(10);

 

            Random rnd = new Random(100);

 

            for (int i = 0; i <10; i++)

            {

                nums.Insert(rnd.Next(0,100));

            }

 

            nums.DisplayElements();

 

            Console.ReadKey();

        }

    }

}

 

 

1. 2冒泡排序

首先要討論的排序算法就是冒泡排序。冒泡排序是可用的最慢排序算法之一,可是它也是最容易理解和實現的排序算法之一,因此這裏把它做爲最早介紹的排序算法。

這種排序算法的得名是因爲數值「像氣泡同樣」從序列的一端浮動到另外一端。假設如今要把一列數按升序方式進行排序,即較大數值浮動到列的右側,而較小數值則浮動到列的左側。

這種效果能夠經過下列操做來實現:屢次遍歷整個列,而且比較相鄰的數值,若是左側的數值大於右側數值就進行交換。

        //冒泡排序

public void BubbleSort()

        {

            int temp;

            int num = 0;

 

            for (int outer = upper; outer >= 1; outer--)

            {

                num++;

                for (int inner = 0; inner <= outer - 1; inner++)

                {

                    if (arr[inner] > arr[inner + 1])

                    {

                        temp = arr[inner];

                        arr[inner] = arr[inner + 1];

                        arr[inner + 1] = temp;

                    }

                }

 

                Console.WriteLine(string.Format("\n{0}次排序", num));

                this.DisplayElements();

            }

        }

clip_image002
這段代碼有幾個地方須要注意。首先,交換數組元素的代碼是寫在主程序中的一行,而沒有用子程序。若是屢次調用交換子程序,就可能會下降排序的速度。既然交換代碼只有短短三

行的長度,因此不把代碼放在子程序內也不會影響代碼的清晰度。更加須要注意的是程序中最外層的循環是從數組的末尾處開始,而且向數組的開始處移動。若是回顧上圖過程就會知道,數組內最大值就在數組末尾的適當位置上。這意味着數組的索引比外層循環的值更大,並且它們已經在恰當的位置上了,於是算法不須要再訪問這些數值了。內層循環從數組的第一個元素開始,而且在幾乎達到數組最後位置的時候結束。內層循環會對用inner inner+1 標識的兩個相鄰位置的數值進行比較,而且在必要時交換它們的數值。

1.3選擇排序

這種排序是從數組的起始處開始,把第一個元素與數組中其餘元素進行比較。而後,將最小的元素放置在第0 個位置上,接着再從第1 個位置開始再次進行排序操做。這種操做會一直到除最後一個元素外的每個元素都做爲新循環的起始點操做事後才終止。

在選擇排序算法中使用了兩層循環。外層循環從數組的第一個元素移動到數組最後一個元素以前的元素,而內層循環則從數組的第二個元素移動到數組的最後一個元素,而且查找比當前外層循環所指元素更小的數值。在內循環遍歷一遍以後,就會把數組內最小值賦值到數組中合適的位置上。

實現SelectionSort 算法的代碼以下所示:

//選擇排序

        public void SelectionSort()

        {

            int min, temp;

           

            for (int outer = 0; outer <= upper; outer++)

            {

                min = outer;

           

                for (int inner = outer + 1; inner <= upper; inner++)

                {

                    if (arr[min] > arr[inner] )

                        min = inner;

                }

               

                temp = arr[outer];

                arr[outer] = arr[min];

                arr[min] = temp;

 

                Console.WriteLine(string.Format("\n{0}次排序", outer+1));

                this.DisplayElements();

            }

        }

         clip_image004


1.4
插入排序

插入排序算法相似於人們一般按照數字順序或者字母順序進行排序的方法。假如我要求全班同窗上交填有本人姓名、學號以及簡短自我介紹的索引卡片。而學生們交回來的卡片是隨機排列的。若是要把卡片按照字母排序排列,就能夠構建出一張座次表了。因此,我把這些卡片帶回了辦公室,而且清理出了辦公桌。緊接着我拿出了第一張卡片。卡片上的名字是Smith。我把它放在辦公桌最左側的位置上,而後又拿出了第二張卡片。這張是Brown。因而,我把Smith 的卡片移動到右側,而且把Brown 的卡片放到Smith 原來的位置上。下一張卡片是Williams。不須要移動任何其餘的卡片就能夠把它放在最右側的位置上。接下來的卡片是Acklin。它須要放置在隊列的開始處,因此其餘全部的卡片都必須向右移動一個位置以便騰出空間放Acklin。這就是插入排序算法的工做原理。

插入排序的代碼以下所示,跟着的是對此算法工做原理的解釋說明:

        //插入排序

        public void InsertionSort()

        {

            int inner, temp;

 

            for (int outer = 1; outer <= upper; outer++)

            {

                temp = arr[outer];

                inner = outer;

 

                while (inner > 0 && arr[inner - 1] >= temp)

                {

                    arr[inner] = arr[inner-1];

                    inner-=1;

                }

                arr[inner] = temp;

               

                Console.WriteLine(string.Format("\n{0}次排序", outer));

                this.DisplayElements();

            }

        }

 

插入排序算法有兩層循環。外層循環會逐個遍歷數組元素,而內層循環則會把外層循環所選擇的元素與該元素在數組內的下一個元素進行比較。若是外層循環選擇的元素小於內層循環選擇的元素,那麼數組元素都向右移以便爲內層循環元素留出位置,這就像前面例子描述的那樣。如今就來看看選擇排序是如何處理前面實例中用來排序的數據集合的。下面是程序的輸出結果:

clip_image006

 

這個輸出清楚地代表插入排序不是經過交換來處理的,而是經過把較大的數組元素向右移動來爲數組左側較小元素留出空間的方式進行操做的。

 

2.基礎排序算法的時間比較

上述三種排序算法在複雜度和理論上都是十分類似的,因此在互相進行比較的時候應該操做近似。這裏用Timing 類來比較三種算法,根據它們對龐大數據集合進行排序時所花費的時間斷定出是否有算法會不同凡響。爲了進行測試,這裏用到基本代碼和以前爲了說明每種算法的工做原理而使用的代碼徹底同樣。可是,在下面這些測試中,爲了說明三種算法是如何處理較小數據集合和較大數據集合的,數組的大小是有變化的。時間測試程序要分別運行處理元素量爲100100010000至更多的幾種狀況。下面是代碼:

      //基礎排序算法的時間比較

    class Test

    {

        public static void RunTest(int numItems)

        {

            Timing sortTime = new Timing();

            Random rnd = new Random(100);

            CArray theArray = new CArray(numItems);

           

            //選擇排序

            for (int i = 0; i < numItems; i++)

                theArray.Insert(rnd.Next(1, int.MaxValue) * 100);

            sortTime.StartTime();

            theArray.SelectionSort();

            sortTime.StopTime();

            Console.WriteLine("Time for Selection sort: " + sortTime.Result().TotalMilliseconds);

            theArray.Clear();

           

            //冒泡排序

            for (int i = 0; i < numItems; i++)

                theArray.Insert(rnd.Next(1, int.MaxValue) * 100);

            sortTime.StartTime();

            theArray.BubbleSort();

            sortTime.StopTime();

            Console.WriteLine("Time for Bubble sort: " + sortTime.Result().TotalMilliseconds);

            theArray.Clear();

           

            //插入排序

            for (int i = 0; i < numItems; i++)

                theArray.Insert(rnd.Next(1, int.MaxValue) * 100);

            sortTime.StartTime();

            theArray.InsertionSort();

            sortTime.StopTime();

            Console.WriteLine("Time for Insertion sort: " + sortTime.Result().TotalMilliseconds);

        }

}

clip_image008

 

結果大概如上所示,實在是等不及啦,沒等後兩個測試出來,就把它給閉了!你們見諒!

儘管選擇排序始終比其餘兩種算法快出許多倍,可是全部這三種排序算法的性能仍是至關低的。準確地說,這些算法沒有一種在對龐大數據集合進行排序時是理想選擇。可是存在能高效處理龐大數據集合的排序算法。在後面的內容裏將會和你們一塊兒探討。

 

小結

本章討論了針對數據排序的三種算法,即選擇排序、冒泡排序以及插入排序。全部這三種算法都是很是容易實現的,並且它們均可以很好地處理少許的數據集合。選擇排序是三種算法中效率最高的,其次是冒泡排序和插入排序。正如本章末尾看到的那樣,這三種算法沒有一種是十分適合龐大數據集合的。(好比,多於萬個元素的數據集合)。

 

源程序下載:DataStructAndAlgorithm.zip 

      參考書箱:<<數據結構與算法>>

相關文章
相關標籤/搜索