文章目錄
- 3.2.1排序概述
- 3.2.2冒泡排序
- 3.2.3插入排序
第三章 數組結構
儲存相關資料項目組是大多數軟體應用程式的一項基本要求,這可以通過使用數組這種主要方式來實現。數組在各種開發語言中大同小異,數組的聲明和初始化非常方便,在某些情況下可以代替複雜的結構和類,並且體積小,訪問速度快,資源利用高,受廣大開發人員的喜歡,合理使用數組可以使我們的開發事半功倍。下面主要就是向大家詳細介紹數組的概念和各種使用方法。
本章的學習重點:
◆ 排序概念
◆ 冒泡排序
◆ 插入排序
3.2排序
C#排序演算法一般都涉及到迴圈,以及賦值。通過排序,能進行簡單的統計與分類,具有極其重要的價值。這裡將介紹兩種不同的C#排序演算法代碼,希望對大家有所協助。
3.2.1排序概述
排序是電腦內經常進行的一種操作,其目的是將一組“無序”的記錄序列調整為“有序”的記錄序列。良好排序方法可以有效提高排序速度,提高排序效果。排序主要分兩種:內部排序和外部排序。
若整個排序過程不需要訪問外存便能完成,則稱此類排序問題為內部排序;
反之,若參加排序的記錄數量很大,整個序列的排序過程不可能在記憶體中完成,則稱此類排序問題為外部排序。
內部排序的過程是一個逐步擴大記錄的有序序列長度的過程。
內排序的方法有許多種,按所用策略不同,可歸納為五類:插入排序、選擇排序、交換排序、歸併排序和分配排序。
其中,插入排序主要包括直接插入排序和希爾排序兩種;選擇排序主要包括直接選擇排序和堆排序;交換排序主要包括氣(冒)泡排序和快速排序。
3.2.2冒泡排序
在許多程式設計中,我們需要將一個數列進行排序,以方便統計,常見的排序方法有冒泡排序,二叉樹排序,選擇排序等等。而冒泡排序一直由於其簡潔的思想方法和比較高的效率而倍受青睞。
基礎思想:將相鄰記錄的關鍵碼進行比較,若前面記錄的關鍵碼大於後面記錄的關鍵碼,則將它們交換,否則不交換。設待排序的順序表List中有n個記錄,初始時子序列中只有一個記錄List[0],第一次排序時,把List[1]與List[0]比較大小,若List[0]<=List[1],說明不需要排序,否則交換List[0]與List[1]的值,第二次排序的時候,List[2]與List[1]比較大小,若List[2]<=List[1],則交換List[1]與List[2]的值。
那麼第一趟排完後就會確定最大的數已經放在最後一個了,然後第二次排的話就會少迴圈一次,所以冒泡要二重迴圈,每一次都會少比較一次。
原始數組: 21 54 76 12 34 44 12 15 28 3 10 68
第一次交換: 21 54 76 12 34 44 12 15 3 28 10 68
第二次交換: 21 54 76 12 34 44 12 3 15 28 10 68
第三次交換: 21 54 76 12 34 44 3 12 15 28 10 68
第四次交換: 21 54 76 12 34 3 44 12 15 28 10 68
第五次交換: 21 54 76 12 3 34 44 12 15 28 10 68
第六次交換: 21 54 76 3 12 34 44 12 15 28 10 68
第七次交換: 21 54 3 76 12 34 44 12 15 28 10 68
第八次交換: 21 3 54 76 12 34 44 12 15 28 10 68
第九次交換: 3 21 54 76 12 34 44 12 15 28 10 68
第一趟排序: 3 21 54 76 12 34 44 12 15 28 10 68
第十次交換: 3 21 54 76 12 34 44 12 15 10 28 68
第十一次交換: 3 21 54 76 12 34 44 12 10 15 28 68
第十二次交換: 3 21 54 76 12 34 44 10 12 15 28 68
第十三次交換: 3 21 54 76 12 34 10 44 12 15 28 68
第十四次交換: 3 21 54 76 12 10 34 44 12 15 28 68
……
第二趟排序: 3 10 21 54 76 12 34 44 12 15 28 68
第三趟排序: 3 10 12 21 54 76 12 34 44 15 28 68
……
最後一趟排序: 3 10 12 12 15 21 28 34 44 54 68 76
下面我們來看一個範例:
01 using System;
02 public class TestBubbleSort
03 {
04 public static void Main(string[] args)
05 {
06 int[] list = new int[] { 2, 6, 3, 8, 2, 7, 3 }; //定義int類型的數組
07 BubbleSort(list); //進行排序
08 for (int i = 0; i < list.Length; i++) //迴圈輸出排序後的數組
09 {
10 Console.WriteLine(list[i]); //輸出結果
11 }
12 }
13 public static void BubbleSort(int[] list) //冒泡排序演算法
14 {
15 for (int i = 0; i < list.Length; i++) //控制第幾趟交換
16 {
17 for (int j = i; j < list.Length; j++) //控制第幾趟的第幾次交換
18 {
19 if (list[i] < list[j]) //比較數組前後項的大小,如果小,則交換
20 {
21 int temp = list[i]; //建立一個臨時變數儲存資料
22 list[i] = list[j]; //把後面的資料放在前面
23 list[j] = temp; //把前面的資料放在後面
24 }
25 }
26 }
27 }
28 }
上述代碼中,由於在排序過程中總是小數往前放,大數往後放,相當於氣泡往上升,所以稱作冒泡排序。設想被排序的數組R[1..N]垂直豎立,將每個資料元素看作有重量的氣泡,根據輕氣泡不能在重氣泡之下的原則,從下往上掃描數組R,凡掃描到違反本原則的輕氣泡,就使其向上"漂浮",如此反覆進行,直至最後任何兩個氣泡都是輕者在上,重者在下為止。
若記錄序列的初始狀態為"正序",則冒泡排序過程只需進行一趟排序,在排序過程中只需進行n-1次比較,且不移動記錄;反之,若記錄序列的初始狀態為"逆序",則需進行n(n-1)/2次比較和記錄移動。因此冒泡排序總的時間複雜度為(n*n)。
優點:穩定,比較次數已知;
缺點:慢,每次只能移動相鄰兩個資料,移動資料的次數多。
3.2.3插入排序
基本思路:順序地將待排序的記錄按其關鍵碼的大小插入到已排序的記錄子序列的適當位置。設待排序的順序表List中有n個記錄,初始時子序列中只有一個記錄List[0],第一次排序時,把List[1]與List[0]比較大小,若List[0]<=List[1],說明不需要排序,否則把位置改變過來,第二次排序的時候,List[2]與List[1]比較大小,如果List[2]比List[1]小,再和List[0]比,然後插入到合適的位置。
原始數組: 23 53 73 11 34 44 11 15 28 3 10 66
↓
第一次插入: 23 53 73 11 34 44 11 15 28 3 10 66
↓
第二次插入: 23 53 73 11 34 44 11 15 28 3 10 66
↓--------------|
第三次插入: 11 23 53 73 34 44 11 15 28 3 10 66
↓--------|
第四次插入: 11 23 34 53 73 44 11 15 28 3 10 66
↓--------|
第五次插入: 11 23 34 44 53 73 11 15 28 3 10 66
↓----------------------------|
第六次插入: 11 11 23 34 44 53 73 15 28 3 10 66
↓----------------------|
第七次插入: 11 11 15 23 34 44 53 73 28 3 10 66
……
最後結果: 3 10 11 11 15 23 28 34 44 53 66 73
下面我們來看一個範例:
01 using System;
02 public class TestInsertionSort
03 {
04 public static void Main(string[] args)
05 {
06 int[] list = new int[] { 2, 6, 3, 8, 2, 7, 3 }; //定義int類型的數組
07 InsertionSort(list); //進行排序
08 for (int i = 0; i < list.Length; i++) //迴圈輸出排序後的數組
09 {
10 Console.WriteLine(list[i]); //輸出結果
11 }
12 }
13 public static void InsertionSort(int[] list) //插入排序演算法
14 {
15 for (int i = 1; i < list.Length; i++) //從第一個元素開始,該元素可認為已排序
16 {
17 int t = list[i];
18 int j = i;
19 while ((j > 0) && (list[j - 1] > t)) //在已排序的元素序列中從後向前掃描
20 {
21 list[j] = list[j - 1]; //將新元素插入到該位置中
22 --j;
23 }
24 list[j] = t;
25 }
26 }
27 }
上述代碼中,如果比較操作的代價比交換操作大的話,可以採用二分尋找法來減少比較操作的數目。該演算法可以認為是插入排序的一個變種,稱為二分尋找排序。
一般來說,插入排序都採用in-place在數組上實現。具體演算法描述如下:
1. 從第一個元素開始,該元素可以認為已經被排序
2. 取出下一個元素,在已經排序的元素序列中從後向前掃描
3. 如果該元素(已排序)大於新元素,將該元素移到下一位置
4. 重複步驟3,直到找到已排序的元素小於或者等於新元素的位置
5. 將新元素插入到該位置中
6. 重複步驟2
如果目標是把n個元素的序列升序排列,那麼採用插入排序存在最好情況和最壞情況。最好情況就是,序列已經是升序排列了,在這種情況下,需要進行的比較操作需(n-1)次即可。最壞情況就是,序列是降序排列,那麼此時需要進行的比較共有n(n-1)/2次。插入排序的賦值操作是比較操作的次數加上(n-1)次。平均來說插入排序演算法複雜度為(n*2)。因而,插入排序不適合對於資料量比較大的排序應用。但是,如果需要排序的資料量很小,例如,量級小於千,那麼插入排序還是一個不錯的選擇。
優點:穩定,快;
缺點:比較次數不一定,比較次數越少,插入點後的資料移動越多,特別是當資料總量龐大的時候,但用鏈表可以解決這個問題。