【數據結構】非比較排序算法(實現計數排序和基數排序)

● 計數排序

1、算法思想:

       計數排序是直接定址法的變形。通過開闢一定大小的空間,統計相同數據出現的次數,然後回寫到原序列中。

2、步驟:

1)找到序列中的最大和最小數據,確定開闢的空間大小。

2)開闢空間,利用開闢的空間存放各數據的個數。

3)將排好序的序列回寫到原序列中。

具體實現如下:

void CountSort(int *arr, int size)
{
 assert(arr);
 int min = arr[0];
 int max = arr[0];
 int num = 0;
 for (int i = 0; i < size; ++i)//找出最大和最小數
 {
  if (arr[i] < min)
  {
   min = arr[i];
  }
  if (arr[i] > max)
  {
   max = arr[i];
  }
 }
 num = max - min + 1;//開闢的空間大小
 int *count = new int[num];
 memset(count, 0, sizeof(int)*num);//初始化count
 for (int i = 0; i < size; ++i)
 {
  count[arr[i] - min]++;//直接定址
 }
 int index = 0;
 for (int i = 0; i < num; ++i)
 {
  while (count[i]--)
  {
   arr[index] = i + min;//回寫到原序列中
   index++;
  }
 }
}

優缺點:

優勢:在對一定範圍內的整數排序時,它的複雜度爲Ο(n+k)(其中k是整數的範圍),快於任何比較排序算法

劣勢:基數排序需要開闢對應大小的空間,k較大時空間利用率不高,故適應於數據比較密集的序列。如果數據密集且沒有重複,我們可以用位圖實現。

● 基數排序

       基數排序是典型的分配類排序,分配類排序是指利用分配和收集兩種基本操作實現排序。基數排序通過反覆的進行分配與收集操作完成排序,基數排序有兩種排序方法,分別是“低位優先”和“高位優先”。在這裏我以“低位優先”排序法進行分析。

1、算法思想:

       排序時先按最低位的值對記錄進行初步排序,在此基礎上再按次低位的值進行進一步排序。以此類推,由低位到高位,每一趟都是在前一趟的基礎上,根據關鍵字key的某一位對所有記錄進行排序,直到最高位,這樣就完成了基數排序的全過程。

2、步驟:

1)由於一直到最高位結束,故需找到最大數的位數。

2)開闢空間存放一趟排序後的序列。利用矩陣的快速轉置思想,用count數組存放進行排序的位相同數字的個數,用start數組記錄每次開始的位置,對每一個元素進行快速定位。

3)回寫到原序列中。

4)重複以上步驟,直到比較到最高位爲止。

具體實現如下:

void RadixSort(int *arr, int size)
{
 assert(arr);
 int *count = new int[10];//每位的數字在0~9之間
 int *start = new int[10];
 int *tmp = new int[size];//存放每趟排序後的序列
 int MaxRadix = GetMaxRadix(arr, size);//最大數的位數
 int radix = 1;
 for (int k = 1; k <= MaxRadix; ++k)
 {
  memset(count, 0, sizeof(int)* 10);//初始化count
  memset(start, 0, sizeof(int)* 10);
  for (int i = 0; i < size; ++i)//count存放進行排序的位數相同的個數
  {
   count[(arr[i] / radix) % 10]++;
  }
  start[0] = 0;
  for (int i = 1; i < 10; ++i)//start存放數據開始位置
  {
   start[i] = start[i - 1] + count[i - 1];
  }
  for (int i = 0; i < size; ++i)//快速定位
  {
   int num = (arr[i] / radix) % 10;
   tmp[start[num]++] = arr[i];
  }
  memcpy(arr, tmp, sizeof(int)*size);//回寫
  radix *= 10;
 }
 delete[] tmp;//注意釋放tmp
}

穩定性分析

      穩定性指排序後在原序列中相同數據的相對位置不會發生改變。插入排序、冒泡排序、歸併排序、計數排序和基數排序是穩定的;快速排序、希爾排序、堆排序和選擇排序是不穩定的。

複雜度分析

 1、空間複雜度:快速排序、歸併排序、計數排序和基數排序都需要開闢空間,插入排序、希爾排序、選擇排序、堆排序、冒泡排序都不需要,空間複雜度爲O(1)。

 2、時間複雜度:

1)插入排序、希爾排序、選擇排序、冒泡排序都爲O(n^2),效率較低。選擇排序效率最低,在最好情況下時間複雜度還是O(n^2);冒泡排序和插入排序相比較,插入排序較好(eg:0 2 1 3 4 5 6 7 8 9;插入排序一次完成,而冒泡排序需要冒兩次),冒泡排序代價較大,並且插入在進行優化(希爾排序)後更能縮短排序時間。

 2)堆排序、歸併排序和快速排序都是O(n*lg(n))。

      通常都看最壞的情況,但快速排序(存在更多優化)幾乎不存在最壞情況,時間複雜度爲O(n*lg(n))。

      堆排序有一個缺點就是隻能對數組進行排序,基數排序和計數排序都存在侷限性(數據密集),歸併排序的空間複雜度爲O(n),而快速排序爲O(lg(n)),綜合可得快速排序最好。

【乾貨】

      歸併排序存在內排序和外排序。外排序其實就是指能夠對內存之外(磁盤中)數據進行排序,對於大數據的文件,不能夠直接加載到內存中進行排序,可以採取將文件劃分成小文件,將小的文件加載到內存中進行排序,然後將排好序的數據進行重寫,將兩個有序的數據文件在重新排序,就能夠排好大數據文件。依據以上思想可進行文件壓縮的實現,有興趣的可以自己試試。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章