程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> 三種線性時間排序算法 - C++實現

三種線性時間排序算法 - C++實現

編輯:C++入門知識

引言

 


注:由於沒有啟用任何公式編輯器,為表示方便:以下涉及時間復雜度表示時,其漸近符號用以下符號代替:

 


先來看一個定理:任意一個比較排序算法在最壞情況下,都需要做 $(nlgn)次的比較。其可用決策樹模型加以證明,詳見:《算法導論》第8章8.1節。

該定理指出了比較排序的時間復雜度下界,即沒有比較更少的了。

故以下介紹的三種算法均不是基於比較的排序算法,其均對輸入數據作了某種假設,即利用了具體數據的特點。

 


一、計數排序

 


1、問題描述:假設數組中的n個元素中的每一個都是介於0到k之間的整數,對其進行排序。此處k為某個正整數。

2、基本思想:對每一個輸入元素x,利用它的所屬范圍大小為[0, k] ,確定出數組中小於x 的元素個數。由於x為正整數,則可以直接把x直接放到它在最終輸出數組中的位置上。

3、算法描述:

輸入:A[1...n], 輸入數組;C[0...k],提供臨時存儲區,初值:0   ---O(k)

輸出:B[1...n],存放排序結果


(1)求出輸入數組A[]1...n]中,其值等於A[j]的元素個數,存放於對應的C[1...k]中:C[A[i]] = C[A[i]] + 1   ---O(n)

(2)求出輸入數組A[]1...n]中,其值小於或等於A[j]的元素個數,迭代地存放於對應的C[1...k]中:C[j] = C[j-1] + C[j]   ---O(K)

(3)經過前兩步之後,C[i]中的值表示為:小於等於 i 的元素個數,即 為 i 在A[1...n]中的最終位置,將其存放於B[1...n]中:B[C[A[j]]] = A[j], C[A[j]] = C[A[j]] - 1 (有可能存在想相等的元素)   --- O(n)

4、時間復雜度:O(k)  + O(n) + O(k) + O(n),則總的運行時間為:@(k+n),在實踐中,當 k=O(n)時,其運行時間即為:@(n).

5、算法實現:


[cpp] 
#include <stdio.h>  
const int K = 5; 
const int N = 6; 
int input[N+1] = {-1, 2, 3, 4, 2 ,1, 5}; 
int output[N+1]; 
int count[K]={0}; 
 
void print(int array[], int n) 

    for(int i = 1; i <=n; i++) 
    { 
        printf("%d ", array[i]); 
    } 
    printf("\n"); 

void countSort(int input[], int output[], int n) 

    int i; 
    for(i = 1; i <= n; i++) 
    {//equal to input[i]  
        count[input[i]] = count[input[i]] +1; 
    } 
    for(i = 1; i <= K; i++) 
    {//less ro equal to i  
        count[i] = count[i-1] + count[i]; 
    } 
    for(i = n; i >= 1; i--) //form large to small to make sorting stable  
    { 
        output[count[input[i]]] = input[i]; 
        count[input[i]]--; 
    } 

 
int main()   

    countSort(input, output, N); 
    print(output, N); 
    return 0;   
}   

#include <stdio.h>
const int K = 5;
const int N = 6;
int input[N+1] = {-1, 2, 3, 4, 2 ,1, 5};
int output[N+1];
int count[K]={0};

void print(int array[], int n)
{
 for(int i = 1; i <=n; i++)
 {
  printf("%d ", array[i]);
 }
 printf("\n");
}
void countSort(int input[], int output[], int n)
{
 int i;
 for(i = 1; i <= n; i++)
 {//equal to input[i]
  count[input[i]] = count[input[i]] +1;
 }
 for(i = 1; i <= K; i++)
 {//less ro equal to i
  count[i] = count[i-1] + count[i];
 }
 for(i = n; i >= 1; i--) //form large to small to make sorting stable
 {
  output[count[input[i]]] = input[i];
  count[input[i]]--;
 }
}

int main() 
{
 countSort(input, output, N);
 print(output, N);
    return 0; 

 

二、基數排序

 


1、問題描述:給定n個d位數,每一位可能的取值有k中,對其進行排序。如,4個三位數:123,367,124,756,此時n=4, d=3,k值為10.

2、基本思想:首先按最低有效位數字進行排序,收集結果;然後按次低位有效位數字排序,收集其結果,依次類推,重復這個過程,直到對所有的d位數字都進行 了排序。

看個例子(來自算法導論8.3的示例,P100):

 \
 


注意:對每一位的排序必須是一個穩定的排序,否則排序可能不正確。如上圖在對最高位排序時,起初436在457的前面,由於最高位都是4,故此次排序兩個數的最高位相等,如果不是穩定的排序,結果457可能會排到436的前面,這樣結果就不對了。而穩定的排序則可以保證排完後,436仍然在457的前面。

3、算法描述:

輸入數組:A[1...n]

RADIX-SORT(A, d)

    for i <- 1 to d

         do use a stable sort to sort array A on digit i  //可以選擇計數排序

4、時間復雜度:由一中的計數排序可知,對每一位的排序時間為:@(k+n),此處共執行d遍,故時間復雜度:@(d*(k+n)),當d為常數、k=O(n)時,基數排序有線性運行時間:@(n)。更一般且更具體的分析可以參加《算法導論》的引理8.3和8.4,P101,其詳細分析了:如何將每個關鍵字分解成若干位以及那些情況下時間復雜度最佳。此處只介紹一些結論與如何實現之。

5、算法實現:


[cpp] 
#include <stdio.h>  
#include <math.h>  
const int N = 7; 
const int D = 3; 
const int K = 10; 
int count[K] = {0}; 
//int input[N+1] = {-1, 329, 457, 657, 839, 436, 720, 355};  
int output[D+1][N+1]={{-1, 329, 457, 657, 839, 436, 720, 355}}; 
 
void print(int array[], int n) 

    for(int i = 1; i <=n; i++) 
    { 
        printf("%d ", array[i]); 
    } 
    printf("\n"); 

int getDigit(int number, int d) 

    if(d > D)return -1; 
    return number%(int)pow(10,d) / (int)pow(10,d-1);  

void countSort(int input[], int output[], int n, int d) 

    int i; 
    int digit; 
    for(i = 1; i <= n; i++) 
    { 
        digit = getDigit(input[i],d); 
        count[digit] = count[digit] +1; 
    } 
    for(i = 1; i <= K; i++) 
    { 
        count[i] = count[i-1] + count[i]; 
    } 
    for(i = n; i >= 1; i--) //form large to small to make sorting stable  
    { 
        digit = getDigit(input[i],d); 
        output[count[digit]] = input[i]; 
        count[digit]--; 
    } 

void initDataStruct(int count[]) 

    for(int i= 0; i < K; i++) 
    { 
        count[i] = 0; 
    } 

void radixSort(int output[][N+1], int n, int d) 

    for(int i = 1; i <= d; i++) 
    { 
        countSort(output[i-1], output[i], n, i); 
        initDataStruct(count); 
    } 

 
int main()   

    radixSort(output, N, D); 
    print(output[D], N); 
        return 0;   

#include <stdio.h>
#include <math.h>
const int N = 7;
const int D = 3;
const int K = 10;
int count[K] = {0};
//int input[N+1] = {-1, 329, 457, 657, 839, 436, 720, 355};
int output[D+1][N+1]={{-1, 329, 457, 657, 839, 436, 720, 355}};

void print(int array[], int n)
{
 for(int i = 1; i <=n; i++)
 {
  printf("%d ", array[i]);
 }
 printf("\n");
}
int getDigit(int number, int d)
{
 if(d > D)return -1;
 return number%(int)pow(10,d) / (int)pow(10,d-1);
}
void countSort(int input[], int output[], int n, int d)
{
 int i;
 int digit;
 for(i = 1; i <= n; i++)
 {
  digit = getDigit(input[i],d);
  count[digit] = count[digit] +1;
 }
 for(i = 1; i <= K; i++)
 {
  count[i] = count[i-1] + count[i];
 }
 for(i = n; i >= 1; i--) //form large to small to make sorting stable
 {
  digit = getDigit(input[i],d);
  output[count[digit]] = input[i];
  count[digit]--;
 }
}
void initDataStruct(int count[])
{
 for(int i= 0; i < K; i++)
 {
  count[i] = 0;
 }
}
void radixSort(int output[][N+1], int n, int d)
{
 for(int i = 1; i <= d; i++)
 {
  countSort(output[i-1], output[i], n, i);
  initDataStruct(count);
 }
}

int main() 
{
 radixSort(output, N, D);
 print(output[D], N);
        return 0; 
}

 

三、桶排序

 


1、問題描述:假設輸入數組有一個隨機過程產生,該過程將元素均勻地分布在區間 [0, 1)上,對這個輸入數組進行排序

2、基本思想:類似於散列表中解決散列沖突的拉鏈法,一個桶本質就是一個鏈表,將相同關鍵字的元素會被放入同一個桶中。由於輸入數組中的元素均勻且獨立均勻分布在 [0, 1)上,所以一般不會有很多個元素被分到同一個桶中去。散列完成後,先對各個桶中的元素進行排序,然後按次序把各桶中的元素收集出來即得到一個有序的數組。

3、算法描述:

輸入:A[1...n];B[0...n-1]:輔助數組鏈表,用於散列元素


輸出:排序好的A[1...n]

(1)把區間 [0, 1) 劃分成 n個相同大小的子區間,或稱為桶(可用輔助數組鏈表B[0...n-1) 實現之)。

(2)已知,散列函數為:f(x) = floor(n*x) , 向下取整,則數組元素A[i] 分配至 桶(鏈表)B[floor(n*A[i])]中    ---@(n)

(3)分別對每個桶B[i]中的元素進行排序(可用插入排序實現之)    ---n*O(2-1/n)

(4)按次序收集各桶中的結果。

4、時間復雜度:根據3中的算法描述部分可知,除了第(3)步外,其他各部分在最壞情況下的時間都是O(n)。運用數理統計的知識可以求得,第(3)步中的對每個桶進行排序,運行時間的期望值為:2-1/n(詳見:《算法導論》8.4節,P103),則第(3)步的總時間:n*(2-1/n),故桶排序的期望運行時間:@(n) + n*(2-1/n) =@(n)

5、算法實現:


[cpp]
#include <stdio.h>  
#include <math.h>  
 
const int N =10; 
double input[N+1] = {-1, 0.78, 0.17, 0.39, 0.26, 0.72, 0.94, 0.21, 0.12, 0.23, 0.68}; 
typedef struct BucketNode 

    double data; 
    struct BucketNode* next; 
}* bucketList, bucketNode; 
bucketList bucketArr[N]; 
 
void print(bucketList bucketArr[]) 
{    
    for(int i = 0; i < N; i++) 
    { 
        bucketNode* pb = bucketArr[i]; 
        while(pb) 
        { 
            printf("%e ", pb->data); 
            pb = pb->next; 
        } 
        printf("\n"); 
         
    } 
    printf("\n"); 

 
void initBucketList(bucketList bucketArr[]) 

    for(int i = 0; i < N; i++) 
    { 
        bucketArr[i] = NULL; 
    }    

bool insertBucketWithSorting(bucketList& bucket, double data) 
{//sorting while inserting  
    bucketNode* pb = new bucketNode; 
    if(NULL == pb) 
        return false; 
    pb->data = data; 
    if(NULL == bucket || pb->data < bucket->data) 
    {//insert before the first element  
        pb->next = bucket; 
        bucket = pb;         
        return true; 
    } 
 
    bucketNode* ptemp = bucket; 
    bucketNode* ptempn = bucket->next; 
    while(ptempn) 
    {//insert after the first element(that is in the middle)  
        if(pb->data < ptempn->data) 
        { 
            pb->next = ptempn; 
            ptemp->next = pb; 
            break; 
        } 
        ptemp = ptempn; 
        ptempn = ptempn->next; 
    } 
    if(NULL == ptempn) 
    {//insert after the last element  
        ptemp->next = pb; 
        pb->next = NULL; 
    } 
 
    return true; 

 
void destroyBucketList(bucketList bucketArr[]) 

    for(int i = 0; i < N; i++) 
    { 
        while(bucketArr[i]!= NULL) 
        { 
            bucketNode* pb = bucketArr[i]; 
            bucketArr[i] = bucketArr[i]->next ; 
            delete pb; 
            pb = NULL; 
        } 
    } 
     

void bucketSort(double input[], bucketList bucketArr[],int n) 
{    
    for(int i = 1; i <= n; i++) 
    { 
        int index = (int)floor(input[i]*n); 
        insertBucketWithSorting(bucketArr[index], input[i]); 
    }    
 
  } 
 
int main()   

    initBucketList(bucketArr); 
    bucketSort(input, bucketArr, N); 
    print(bucketArr); 
 
    destroyBucketList(bucketArr); 
 
    return 0;   
}   

#include <stdio.h>
#include <math.h>

const int N =10;
double input[N+1] = {-1, 0.78, 0.17, 0.39, 0.26, 0.72, 0.94, 0.21, 0.12, 0.23, 0.68};
typedef struct BucketNode
{
 double data;
 struct BucketNode* next;
}* bucketList, bucketNode;
bucketList bucketArr[N];

void print(bucketList bucketArr[])

 for(int i = 0; i < N; i++)
 {
  bucketNode* pb = bucketArr[i];
  while(pb)
  {
   printf("%e ", pb->data);
   pb = pb->next;
  }
  printf("\n");
  
 }
 printf("\n");
}

void initBucketList(bucketList bucketArr[])
{
 for(int i = 0; i < N; i++)
 {
  bucketArr[i] = NULL;
 } 
}
bool insertBucketWithSorting(bucketList& bucket, double data)
{//sorting while inserting
 bucketNode* pb = new bucketNode;
 if(NULL == pb)
  return false;
 pb->data = data;
 if(NULL == bucket || pb->data < bucket->data)
 {//insert before the first element
  pb->next = bucket;
  bucket = pb;  
  return true;
 }

 bucketNode* ptemp = bucket;
 bucketNode* ptempn = bucket->next;
 while(ptempn)
 {//insert after the first element(that is in the middle)
  if(pb->data < ptempn->data)
  {
   pb->next = ptempn;
   ptemp->next = pb;
   break;
  }
  ptemp = ptempn;
  ptempn = ptempn->next;
 }
 if(NULL == ptempn)
 {//insert after the last element
  ptemp->next = pb;
  pb->next = NULL;
 }

 return true;
}

void destroyBucketList(bucketList bucketArr[])
{
 for(int i = 0; i < N; i++)
 {
  while(bucketArr[i]!= NULL)
  {
   bucketNode* pb = bucketArr[i];
   bucketArr[i] = bucketArr[i]->next ;
   delete pb;
   pb = NULL;
  }
 }
 
}
void bucketSort(double input[], bucketList bucketArr[],int n)

 for(int i = 1; i <= n; i++)
 {
  int index = (int)floor(input[i]*n);
  insertBucketWithSorting(bucketArr[index], input[i]);
 } 

  }

int main() 
{
 initBucketList(bucketArr);
 bucketSort(input, bucketArr, N);
 print(bucketArr);

 destroyBucketList(bucketArr);

    return 0; 


 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved