1. 程式人生 > 其它 >資料結構詳解

資料結構詳解

  1 using System;
  2 using System.Collections.Generic;
  3 using System.Linq;
  4 using System.Text;
  5 using System.Threading.Tasks;
  6 
  7 namespace ConsoleApp1
  8 {
  9     /// <summary>
 10     /// O(1)常數階 < O(logn)對數階 < O(n)線性階 < O(n~2)平方階 < O(n~3)(立方階) < O(2~n) (指數階)
 11     ///
由數學函式可知時間複雜度描述的是演算法的執行次數隨元素個數的增長幅度 12 /// </summary> 13 class Program 14 { 15 //時間複雜度: 16 //先分別計算程式中每條語句的執行次數,然後用總的執行次數間接表示程式的執行時間, 17 //一個演算法所程式設計序執行時間的多少,用的並不是準確值(事實上也無法得出),而是根據合理方法得到的預估值, 18 //即時間複雜度描述的是演算法的執行次數隨元素個數的增長幅度。 19 //對於一個演算法(或者一段程式)來說,其最簡頻度往往就是最深層次的迴圈結構中某一條語句的執行次數,
20 //例如2n+1當n無窮大時化簡為n,實際上就是a++語句的執行次數O(n);同樣2n~2+2n+1當n無窮大時簡化為n~2,實際上就是最內層迴圈中num++語句的執行次數O(n~2)。 21 //用無窮大的思想去理解時間複雜度的表示方法。 22 23 //空間複雜度: 24 //和時間複雜度類似,一個演算法的空間複雜度,也常用大O記法表示。 25 //要知道每一個演算法所編寫的程式,執行過程中都需要佔用大小不等的儲存空間,例如: 26 //程式程式碼本身所佔用的儲存空間; 27 //程式中如果需要輸入輸出資料,也會佔用一定的儲存空間;
28 //程式在執行過程中,可能還需要臨時申請更多的儲存空間。 29 //程式自身所佔用的儲存空間取決於其包含的程式碼量,如果要壓縮這部分儲存空間,就要求我們在實現功能的同時,儘可能編寫足夠短的程式碼。 30 //程式執行過程中輸入輸出的資料,往往由要解決的問題而定,即便所用演算法不同,程式輸入輸出所佔用的儲存空間也是相近的。 31 //事實上,對演算法的空間複雜度影響最大的,往往是程式執行過程中所申請的臨時儲存空間。不同的演算法所編寫出的程式,其執行時申請的臨時儲存空間通常會有較大不同。 32 //如果隨著輸入值n的增大,程式申請的臨時空間成線性增長,則程式的空間複雜度用O(n)表示。 33 //如果隨著輸入值n的增大,程式申請的臨時空間成n2關係增長,則程式的空間複雜度用O(n2)表示。 34 35 //資料結構: 36 //資料結構是指相互之間存在一種或多種特定關係的資料元素的集合。 37 //資料結構分為邏輯結構(樹型一對多關係(二叉樹、平衡二叉樹AVL、紅黑樹、B樹、B+樹等)、圖型多對多關係)和儲存結構(線型一對一(順序(陣列)、連結(單鏈表、雙鏈表、迴圈連結串列)、索引(棧、佇列)、雜湊即鍵值(字典)))。 38 //特別提示千萬不要以為陣列這種結構是記憶體直接存在的,記憶體直接存在的只有變數和指標,所以陣列也是SDK幫我們實現的一種用來儲存資料的邏輯結構,可以嘗試自己實現一下單鏈表。 39 //儲存結構是根據元素在記憶體中的相對位置(記憶體變數)和地址指標封裝成的一種結構, 40 //邏輯結構是利用儲存結構和記憶體變數值封裝成的具有邏輯關係的資料結構。 41 //可以自己研究一下SDK框架帶的各種集合(如List<T>、ArrayList<T>、LinkedList<T>)是使用什麼資料結構實現的分裝。 42 43 static void Main(string[] args) 44 { 45 int[] arr = new int[10] { 5,2,1,4,8,3,12,55,31,16}; 46 int[] tt = heapSort(arr); 47 } 48 49 #region 氣泡排序 O(n)-O(n~2) 穩定(如果兩個元素相等次序不會改變) 50 static int[] bubbleSort(int[] arr) 51 { 52 var len = arr.Length; 53 for (var i = 0; i < len - 1; i++) 54 { 55 for (var j = 0; j < len - 1 - i; j++) 56 { 57 if (arr[j] > arr[j + 1]) 58 { // 相鄰元素兩兩對比 59 var temp = arr[j + 1]; // 元素交換 60 arr[j + 1] = arr[j]; 61 arr[j] = temp; 62 } 63 } 64 } 65 return arr; 66 } 67 #endregion 68 69 #region 堆排序 O(nlogN)-O(nlogN) 底數根據切分值而定,這裡為2分法,不穩定(如果兩個元素相等次序可能會改變) 70 //堆排序是指利用堆這種資料邏輯結構思想所設計的一種排序演算法。堆積是一個近似完全二叉樹的結構,並同時滿足堆積的性質:即子結點的鍵值或索引總是全部小於(或者全部大於)它的父節點。 71 static void buildMaxHeap(int[] arr,int len) 72 { // 建立大頂堆 73 for (int i = (int)Math.Floor((decimal)len / 2); i >= 0; i--) 74 { 75 heapify(arr, i, len); 76 } 77 } 78 79 static void heapify(int[] arr, int i,int len) 80 { // 堆調整 81 var left = 2 * i + 1; 82 var right = 2 * i + 2; 83 var largest = i; 84 if (left < len && arr[left] > arr[largest]) 85 { 86 largest = left; 87 } 88 if (right < len && arr[right] > arr[largest]) 89 { 90 largest = right; 91 } 92 if (largest != i) 93 { 94 swap(arr, i, largest); 95 heapify(arr, largest, len); 96 } 97 } 98 99 static void swap(int[] arr, int i, int j) 100 { 101 var temp = arr[i]; 102 arr[i] = arr[j]; 103 arr[j] = temp; 104 } 105 106 static int[] heapSort(int[] arr) 107 { 108 int len = arr.Length; 109 110 //先將陣列轉為大頂堆結構 111 buildMaxHeap(arr, len); 112 113 //接下來對堆結構arr進行排序 114 for (var i = arr.Length - 1; i > 0; i--) 115 { 116 swap(arr, 0, i); //每次生成大頂堆後,把堆頂的最大元素值放到陣列的最後,然後一次執行後面的元素,每執行一次大頂堆即可找出一個最大值 117 heapify(arr, 0, i); 118 } 119 120 return arr; 121 } 122 #endregion 123 } 124 }

氣泡排序示意圖:

堆排序示意圖:

堆結構示意圖: