【資料結構】十一種排序演算法C++實現
阿新 • • 發佈:2018-12-13
練習了十一種排序演算法的C++實現:以下依次為,冒泡、選擇、希爾、插入、二路歸併、快排、堆排序、計數排序、基數排序、桶排序,可建立sort.h和main.cpp將程式碼放入即可執行。如有錯誤,請指出更正,謝謝交流。
// sort.h # include <iostream> # include "chain.h" using namespace std; typedef struct ChainNode { int value; struct ChainNode *next; //ChainNode() {} ChainNode(int value, ChainNode * next) { this->value = value; this->next = next; } } CNode, *Chain; void swap(int &a, int &b) { int temp = a; a = b; b = temp; } void blob(int arr[], int len) { for (int i = len - 1; i > 0; i--) for (int j = 0; j < i; j++) if (arr[j] > arr[j + 1]) swap(arr[j], arr[j + 1]); } void select(int arr[], int len) { for (int i = 0; i < len; i++) { int min = i; for (int j = i; j < len; j++) if (arr[j] < arr[min]) min = j; swap(arr[i], arr[min]); } } void shell(int arr[], int len) { // https://zh.wikipedia.org/wiki/%E5%B8%8C%E5%B0%94%E6%8E%92%E5%BA%8F int i, j, step; for (step = len >> 1; step > 0; step >>= 1) // 初始步長為n/2,每次對步長取半直到步長達到1 for (i = step; i < len; i++) { // 從第step個數開始,依次將後面的數與前面的step步長數進行比較 int temp = arr[i]; // 將大於當前值的希爾序列中的值,不斷地進行後移,最終將當前值放到希爾序列的合適位置 for (j = i - step; j >= 0 && arr[j] > temp; j -= step) // 初始為當前位置的第前step個數,比較arr[j + step] 與 arr[j] arr[j + step] = arr[j]; // 希爾序列中前面的值,往後移(第一次會沖掉初始值temp) arr[j + step] = temp; } } void insert(int arr[], int len) { for (int i = 1; i < len; i++) { int temp = arr[i]; int j; for (j = i - 1; j >= 0 && temp < arr[j]; j--) arr[j + 1] = arr[j]; arr[j + 1] = temp; } } void merge(int arr[], int temp[], int start, int end) { if (start >= end) return; int mid = (start + end) / 2; int start1 = start, end1 = mid; int start2 = mid + 1, end2 = end; merge(arr, temp, start1, end1); merge(arr, temp, start2, end2); int k = start; // 從start起始位置開始合併進temp中 while (start1 <= end1 && start2 <= end2) { // 對兩個陣列同時處理,小的放到temp中 temp[k++] = arr[start1] < arr[start2] ? arr[start1++] : arr[start2++]; } while (start1 <= end1) // 歸併的兩個陣列不一定長,如果start1剩下,則合併進temp temp[k++] = arr[start1++]; while (start2 <= end2) // 如果第二個陣列剩下,將其合併進temp temp[k++] = arr[start2++]; for (int k = start; k <= end; k++) // 拷貝回原陣列 arr[k] = temp[k]; } void quick(int arr[], int start, int end) { if (start > end) return; int left = start, right = end; int mid = arr[left]; // 先在左邊第一個數挖個坑 while (left < right) { while (left < right && arr[right] >= mid) // 從右邊開始找小於中間值得第一個數 right--; if (left < right) { arr[left] = arr[right]; // 將‘右邊的小於中間值的那個數’ 填入到 ‘左邊的坑(初始為第一個)’ left++; // 左邊坑被填好之後,處理下一個,往右挪一下 } while (left < right && arr[left] <= mid) left++; if (left < right) { arr[right] = arr[left]; // 將‘左邊大於中間中的數’ 填入到 ‘右邊被挖出來的坑’ right--; // 右邊坑被填好之後,處理下一個,往做挪一下 } } arr[left] = mid; // //退出時,left等於right。將x填到這個坑中。這個坑也是中間數。 quick(arr, start, left - 1); // 中間的數不需要再排序 quick(arr, left + 1, end); } void max_heapify(int arr[], int start, int end) { // 初始化dad和son int dad = start; int son = dad * 2 + 1; while (son <= end) { // 在不停地向下調整過程中,son不越過堆尾的前提下 if (son + 1 <= end && arr[son + 1] > arr[son]) // 在不越過堆尾的前提下,找出最大的孩子節點的位置 son++; if (arr[dad] > arr[son]) // 如果父節點值大於最大孩子節點值,則返回 return; else { swap(arr[dad], arr[son]); // 交換父子節點值 dad = son; // 重新賦值dad和son,進一步的向下調整 son = dad * 2 + 1; } } } void heap(int arr[], int len) { // https://zh.wikipedia.org/wiki/%E5%A0%86%E6%8E%92%E5%BA%8F // 建立初始大根堆,只有這裡考慮的是數量號len,其餘地方全部考慮索引號 for (int i = len / 2 - 1; i >= 0; i--) // 從最後一個父節點,依次向前開調整,len/2 - 1 可以確定最後一個父節點的索引位置。這裡使用節點數量len確定索引位置i,而不是索引數len-1(67確定索引2,(位置Wie3)) max_heapify(arr, i, len - 1); // 逆序生成堆排序結果。依次取出堆頂元素,與最近一個尾部交換,重新調整大根堆,此時只需要調整堆頂一個元素即可 for (int i = len - 1; i > 0; i--) { swap(arr[0], arr[i]); // 拿下堆頂元素,與堆尾元素交換 max_heapify(arr, 0, i - 1); // 調整當前堆尾之前的所有元素為大根堆,只需要向下調整0號位置 } } void count(int arr[], int len) { // https://zh.wikipedia.org/wiki/%E8%AE%A1%E6%95%B0%E6%8E%92%E5%BA%8F // 找到陣列中的最大值最小值 int min, max; min = max = arr[0]; for (int i = 0; i < len; i++) { if (arr[i] > max) max = arr[i]; else if (arr[i] < min) min = arr[i]; } // 確定最大值,最小值之間的範圍差,並初始化相應大小的陣列 int lenC = max - min + 1; int *arrC = new int[lenC]; for (int k = 0; k < lenC; k++) arrC[k] = 0; // 遍歷原始陣列,將對應的新計數陣列上的值+1,每個值可以看作是一個桶 for (int i = 0; i < len; i++) { int index = arr[i] - min; arrC[index] += 1; } // 遍歷計數陣列,輸出上面關聯的數值(桶裡的所有個數的數值) for (int k = 0, i = 0; k < lenC; k++) { if (arrC[k] != 0) { for (int j = 0; j < arrC[k]; j++) arr[i++] = k + min; // 這裡的k+min就是真實值 } } } int getMaxBit(int arr[], int len) { int max = arr[0]; for (int i = 0; i < len; i++) { if (arr[i] > max) { max = arr[i]; } } int maxBit = 0; while (max >= 10) { max /= 10; maxBit++; } return ++maxBit; } void radix(int arr[], int len) { // 求最大位數,個位為1,十位為2... int maxBit = getMaxBit(arr, len); cout << maxBit << endl; int *temp = new int[len]; // 從小到達,遍歷每一位進行排序 int radix = 1; for (int i = 0; i < maxBit; i++) { // 計數器清空 int buckets[10] = { 0 }; // 遍歷所有資料,將其放入到桶中 for (int j = 0; j < len; j++) { int d = arr[j] / radix % 10; buckets[d] ++; } // 修訂bucket陣列中值,索引位0的數值,代表0桶內有多少個數,索引位1上的值,代表1+0上的桶內一共有多少個數 // 使得bucket中的陣列值,變成其索引所對應基數的偏移量,注意,預設均加了0號索引上的數量。 for (int j = 1; j < 10; j++) { buckets[j] = buckets[j] + buckets[j - 1]; cout << buckets[j] << endl; } // 將bucket中的數值儲存到temp陣列中 for (int j = len - 1; j >= 0; j--) { int d = arr[j] / radix % 10; // 找到桶索引,即基數 temp[buckets[d] - 1] = arr[j]; // 桶索引中的數從1開始計數的,比如索引0上1個數,其值為1,但實際temp中索引從0開始。所以減1. buckets[d]--; // 儲存一個,則對應的基數上數值個數減一。這裡如果有6、7、8三個數,先賦值給index為7的位置,然後6,然後5. } // 將temp陣列賦值給原陣列,此時,完成低位上的排序有序 for (int j = 0; j < len; j++) arr[j] = temp[j]; radix *= 10; } delete []temp; } void bucket(int arr[], int len) { // 初始化及設定固定空桶數 int bucketNum = 10; CNode **bucketTable = new CNode *[bucketNum]; // 分配動態二維(指標)陣列,bucketNum個CNode*陣列 // vector<vector<CNode *> > bucketTable(bucketNum,vector<CNode *>(1)?)); // vector方法 for (int i = 0; i < bucketNum; i++) // 為指標陣列的每個元素,分配一個元素 bucketTable[i] = new CNode(0, NULL); // 初始化桶內元素計數,用第一個CNode? for (int i = 0; i < len; i++) { // 計算當前元素對映後的桶號 int index = arr[i] / bucketNum; CNode *p = bucketTable[index]; // 將資料放到對應的空桶中, 並進行排序 CNode *pNode = new CNode(arr[i], NULL); // 新增陣列中的元素到桶中 if (p->value == 0) { // 該桶中還沒有資料 bucketTable[index]->next = pNode; (bucketTable[index]->value)++; // 桶中數量加1 } else { // 連結串列結構的插入排序 while (p->next != NULL && p->next->value <= pNode->value) p = p->next; pNode->next = p->next; p->next = pNode; (bucketTable[index]->value)++; } } // 拼接不為空的桶中的資料,得到結果 for (int i = 0; i < bucketNum; i++) { CNode *p = bucketTable[i]->next; // bucketTable[i]這個CNode中包含了計數,其第一個next為第一個元素 while (p) { cout << p->value << ","; p = p->next; } } for (int i = 0; i < bucketNum; i++) { delete[bucketNum]bucketTable[i]; // 先釋放指標陣列的每個元素指向的陣列 bucketTable[i] = NULL; } delete[bucketNum]bucketTable; // 再釋放該指標陣列 bucketTable = NULL; // 建立二維動態陣列 // https://blog.csdn.net/samuelcoulee/article/details/8674388 } // main.cpp # include <iostream> # include "sort.h" using namespace std; void testSort() { int temp[8]; int arr[] = { 7, 9, 1, 2, 6, 0, 5 , 8 }; int len = sizeof(arr) / sizeof(int); for (int i = 0; i < len; i++) cout << arr[i] << ", "; // blob(arr, len); // select(arr, len); // insert(arr, len); // merge(arr, temp, 0, len-1); // quick(arr, 0, len - 1); // heap(arr, len); // shell(arr, len); // count(arr, len); // radix(arr, len); bucket(arr, len); for (int i = 0; i < len; i++) cout << arr[i] << ", "; } int main() { testSort(); }