我們都說程序就是數據加算法,即數據和對數據進行操作的流程。
而對計算機中所存儲的數據進行的最普遍的兩種操作就是排序和查找。
現在我們就使用C#語言實現三種基礎的排序算法——冒泡排序、選擇排序、插入排序。
首先我們定義一個能夠支持我們創建自定義排序規則的數據結構。我們使用C#的類來作為我們實現的對象,在這個類中維護着一個數組。
其定義如下代碼:
1 using System; 2 3 namespace DataStructuresandAlgorithms 4 { 5 6 // 用來承載我們實驗的自定義類 7 public class CArray 8 { 9 // 核心的數組,用於對數據的存儲和操作 10 private int[] arr; 11 // 用於標識當前對象所存儲的值的個數 12 private int elements; 13 // 用於向外界返回此實例最大存儲數據個數 14 // 因為數組的大小固定,所以此值在實例被初始化后就是固定的 15 // 此屬性用於驗證,但我們不是很需要 16 //readonly int size; 17 18 // 構造函數,參數即為該實例內部維護的數組的長度 19 public CArray(int size) 20 { 21 arr = new int[size]; 22 //this.size = size; 23 elements = 0; 24 } 25 26 // 此方法用於向對象內存儲數據 27 public int Insert(int item) 28 { 29 arr[elements] = item; 30 return elements++; 31 } 32 33 // 此方法用於顯示當前實例存儲了哪些數據 34 public void DisplayElements() 35 { 36 foreach (int item in arr) 37 Console.Write(item + " "); 38 } 39 40 // 此方法用於完全清除當前實例存儲的數據 41 public void Clear() 42 { 43 for (int i = 0; i < arr.Length; i++) 44 arr[i] = 0; 45 elements = 0; 46 } 47 48 // 冒泡排序 49 public void BubbleSort() 50 { 51 // TDO 暫不實現 52 } 53 // 選擇排序 54 public void SelectSort() 55 { 56 // TDO 暫不實現 57 } 58 // 插入排序 59 public void InsertSort() 60 { 61 // TDO 暫不實現 62 } 63 } 64 }
有了我們自定義的一個類來作為數據結構,我們就可以實現排序了。
排序算法都是浮雲,大家需要從本質上理解:就是把多個值分成兩個來比較,經過多次的划分和比較后確定這些值的排列順序。
冒泡排序:通過相鄰兩個值的大小比較(多次),保證數值排序為從小到大的排序方法。
效果及實現原理:外層的數列個數次的循環,每次循環會將最大的數值“冒泡”到右端,因為是相鄰的兩兩比較。
具體實現:雙重循環,外層數列個數次,保證每一個數值都處於正確位置(因為每循環一次,至少會確定一個數值的正確位置),內層從數列個數次逐漸減少次數,因為每一次外層循環會確定最大值、倒數第二大值……我們在下一次循環就可以忽略掉他們。
下圖為冒泡排序過程中每進行完一次外層循環數列的排序情況:
下圖為全部循環的部分結果:
很明顯:冒泡排序是通過逐個確定最大值來對數列進行排序的。
我們使用代碼來說明這個問題(下面就是冒泡排序的一般寫法):
1 // 冒泡排序 2 public void BubbleSort() 3 { // 外層為數列數值個數次的循環 4 for (int i = elements - 1; i >= 1; i--) 5 { // 內層次數主義遞減 6 for (int j = 0; j <= i - 1; j++) 7 { // 兩兩比較,如果后者比前者大則交換位置 8 // 最終會呈現大的數值逐個“冒泡”到數列右端 9 if (arr[j + 1] < arr[j]) 10 { 11 int temp = arr[j]; 12 arr[j] = arr[j + 1]; 13 arr[j + 1] = temp; 14 } 15 } 16 } 17 }
后面我會將整個代碼和貼出,其中的冒泡排序經過了一定程度的優化。
選擇排序:通過逐個確定最小值進行的排序。
和冒泡排序不同,這種排序思路更為清晰:拿出一個值和數列中的其他值比較,完成整次比較后,它如果是最小的,就放到最左邊,如果在比較途中發現比它更小的值,那么該更小的值會作為新的比較對象。
直接上代碼可能大家更容易理解:
1 // 選擇排序 2 public void SelectSort() 3 { // 外層數值個數次循環,保證排序結果正確 4 for (int i = 0; i < elements; i++) 5 { // 內層循環次數遞減,因為每次外循環結束后都會確定一個最小值 6 // 再一次循環時就可以忽略那個值了 7 8 // 假定第二個位置(第二次及以后的循環會排除已確定的最值)上的值是最小值 9 // 將索引記錄下來,最后會用到,此值也會被動態修改(如果循環過程中發現新的最值) 10 int index = i; 11 for (int j = i + 1; j < elements; j++) 12 { //進行循環,發現新的最小值后則更新最小值,並記錄索引 13 if (arr[index] > arr[j]) 14 index = j; 15 } 16 // 最值找到后將最小值放到最左邊(忽略已經確定的最值) 17 int temp = arr[i]; 18 arr[i] = arr[index]; 19 arr[index] = temp; 20 } 21 }
我們再看一看執行過程中數列的變化:
我們來看看更細微的變化:
由於這個數列本身的排序比較規則,不能很好地說明問題,大家可以自己試一下。
大家可能會覺得疑惑:選擇排序也可以逐個確定最大值,為什么我會將其和冒泡排序用這種方式區分呢?這個其實沒什么好說的,大家的理解不一樣,也可以理解為:
冒泡排序和選擇排序都是通過逐個確定最值來進行排序,不同的是冒泡排序是相鄰兩兩數值比較,選擇排序不是如此。
最后給大家介紹一種比較基礎的排序。
插入排序:通過使逐漸增加個數的數列段有序而最終達到排序的目的。
大家可能會覺得繞口,這不能怪我,我是文青嘛。簡單來說吧:就像我們玩紙牌或麻將牌,我們手上有多張牌時,我們會將新拿到手的牌放到最適合它的位置。比如我們有大小為1、5的紙牌,當拿到3號紙牌時,我們會將它插入到1和5中間,而如果我們拿到6紙牌,我們則會將它放到5后面。
具體到數列排序上,會造成的效果是:前2個數值有序、前3個數值有序、前4個數值有序……一直到整個數列有序。
先看排序時數列變化:
大家看看代碼就會容易理解了:
1 // 插入排序 2 public void InsertSort() 3 { // 外循環是老規矩 4 for (int i = 1; i < elements; i++) 5 { // 內層多次循環解決數列段排序問題 6 // 保存最右邊的數值(將要插入已排序數列的值) 7 int temp = arr[i]; 8 // 新索引用於區分需要排序的原始數列 9 int j = i; 10 11 // 如果在已排好序的數列段中還沒有找到比新數值小的數值 12 while (j > 0 && temp < arr[j - 1]) 13 { // 將比新數值大的數值向后移 14 arr[j] = arr[j - 1]; 15 j--; 16 } 17 // 如果在已排好序的數列段中找到了比新數值小的數值 18 // 將數值替換到此位置 19 arr[j] = temp; 20 } 21 }
我將完整代碼貼上來供大家參考:
1 using System; 2 3 namespace DataStructuresandAlgorithms 4 { 5 // 用來承載我們實驗的自定義類 6 public class CArray 7 { 8 private int[] arr; 9 private int elements; 10 11 public CArray(int size) 12 { 13 arr = new int[size]; 14 elements = 0; 15 } 16 17 public int Insert(int item) 18 { 19 arr[elements] = item; 20 return elements++; 21 } 22 23 public void DisplayElements() 24 { 25 foreach (int item in arr) 26 Console.Write(item + " "); 27 } 28 29 public void Clear() 30 { 31 for (int i = 0; i < arr.Length; i++) 32 arr[i] = 0; 33 elements = 0; 34 } 35 36 // 冒泡排序 37 public void BubbleSort() 38 { 39 int temp = 0; 40 bool jx = true; 41 for (int i = elements - 1; jx && i >= 1; i--) 42 { 43 jx = false; 44 for (int j = 0; j <= i - 1; j++) 45 { 46 if (arr[j + 1] < arr[j]) 47 { 48 temp = arr[j]; 49 arr[j] = arr[j + 1]; 50 arr[j + 1] = temp; 51 jx = true; 52 } 53 //Console.WriteLine(); 54 //DisplayElements(); 55 } 56 Console.WriteLine(); 57 DisplayElements(); 58 } 59 } 60 // 選擇排序 61 public void SelectSort() 62 { 63 for (int i = 0; i < elements; i++) 64 { 65 int index = i; 66 for (int j = i + 1; j < elements; j++) 67 { 68 if (arr[index] > arr[j]) 69 index = j; 70 71 //Console.WriteLine(); 72 //DisplayElements(); 73 } 74 int temp = arr[i]; 75 arr[i] = arr[index]; 76 arr[index] = temp; 77 Console.WriteLine(); 78 DisplayElements(); 79 } 80 } 81 // 插入排序 82 public void InsertSort() 83 { 84 for (int i = 1; i < elements; i++) 85 { 86 int temp = arr[i]; 87 int j = i; 88 while (j > 0 && temp < arr[j - 1]) 89 { 90 arr[j] = arr[j - 1]; 91 j--; 92 } 93 arr[j] = temp; 94 Console.WriteLine(); 95 this.DisplayElements(); 96 } 97 } 98 static void Main(string[] args) 99 { 100 CArray ca = new CArray(10); 101 Random rnd = new Random(100); 102 for (int i = 0; i < 10; i++) 103 ca.Insert(rnd.Next(0, 100)); 104 105 // 排序前 106 ca.DisplayElements(); 107 Console.WriteLine(); 108 109 // 排序 110 ca.BubbleSort(); 111 //ca.InsertSort(); 112 //ca.SelectSort(); 113 114 // 排序后 115 Console.WriteLine(); 116 Console.WriteLine(); 117 ca.DisplayElements(); 118 Console.Read(); 119 } 120 } 121 }
這次的基礎排序算法就為您介紹到這里。
(最后編輯時間2012-08-21 19:20:50)