亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

C++線性時間的排序算法分析

 更新時間:2014年08月14日 11:27:22   投稿:shichen2014  
這篇文章主要介紹了C++線性時間的排序算法分析,是非常經典的非比較排序算法,對于C++程序員有很大的借鑒價值,需要的朋友可以參考下

前面的文章已經介紹了幾種排序算法,如插入排序(直接插入排序,折半插入排序,希爾排序)、交換排序(冒泡排序,快速排序)、選擇排序(簡單選擇排序,堆排序)、2-路歸并排序(可以參考前一篇文章:各種內部排序算法的實現)等,這些排序算法都有一個共同的特點,就是基于比較。

本文將介紹三種非比較的排序算法:計數排序,基數排序,桶排序。它們將突破比較排序的Ω(nlgn)下界,以線性時間運行。

一、比較排序算法的時間下界

所謂的比較排序是指通過比較來決定元素間的相對次序。

“定理:對于含n個元素的一個輸入序列,任何比較排序算法在最壞情況下,都需要做Ω(nlgn)次比較?!?br /> 也就是說,比較排序算法的運行速度不會快于nlgn,這就是基于比較的排序算法的時間下界。

通過決策樹(Decision-Tree)可以證明這個定理,關于決策樹的定義以及證明過程在這里就不贅述了。讀者可以自己去查找資料,這里推薦大家看一看麻省理工學院公開課:算法導論的《MIT公開課:線性時間排序》。

根據上面的定理,我們知道任何比較排序算法的運行時間不會快于nlgn。那么我們是否可以突破這個限制呢?當然可以,接下來我們將介紹三種線性時間的排序算法,它們都不是通過比較來排序的,因此,下界Ω(nlgn)對它們不適用。

二、計數排序(Counting Sort)

計數排序的基本思想就是對每一個輸入元素x,確定小于x的元素的個數,這樣就可以把x直接放在它在最終輸出數組的位置上,例如:

 

算法的步驟大致如下:

①.找出待排序的數組中最大和最小的元素

②.統(tǒng)計數組中每個值為i的元素出現的次數,存入數組C的第i項

③.對所有的計數累加(從C中的第一個元素開始,每一項和前一項相加)

④.反向填充目標數組:將每個元素i放在新數組的第C(i)項,每放一個元素就將C(i)減去1

C++代碼如下:

/************************************************************************* 
  > File Name: CountingSort.cpp 
  > Author: SongLee 
 ************************************************************************/ 
#include<iostream> 
using namespace std; 
 
/* 
 *計數排序:A和B為待排和目標數組,k為數組中最大值,len為數組長度 
 */ 
void CountingSort(int A[], int B[], int k, int len) 
{ 
  int C[k+1]; 
  for(int i=0; i<k+1; ++i) 
    C[i] = 0; 
  for(int i=0; i<len; ++i) 
    C[A[i]] += 1; 
  for(int i=1; i<k+1; ++i) 
    C[i] = C[i] + C[i-1]; 
  for(int i=len-1; i>=0; --i) 
  { 
    B[C[A[i]]-1] = A[i]; 
    C[A[i]] -= 1; 
  } 
} 
 
/* 輸出數組 */ 
void print(int arr[], int len) 
{ 
  for(int i=0; i<len; ++i) 
    cout << arr[i] << " "; 
  cout << endl; 
} 
 
/* 測試 */ 
int main() 
{ 
  int origin[8] = {4,5,3,0,2,1,15,6}; 
  int result[8]; 
  print(origin, 8); 
  CountingSort(origin, result, 15, 8); 
  print(result, 8); 
  return 0; 
}

當輸入的元素是0到k之間的整數時,時間復雜度是O(n+k),空間復雜度也是O(n+k)。當k不是很大并且序列比較集中時,計數排序是一個很有效的排序算法。計數排序是一個穩(wěn)定的排序算法。

可能你會發(fā)現,計數排序似乎饒了點彎子,比如當我們剛剛統(tǒng)計出C,C[i]可以表示A中值為i的元素的個數,此時我們直接順序地掃描C,就可以求出排序后的結果。的確是這樣,不過這種方法不再是計數排序,而是桶排序,確切地說,是桶排序的一種特殊情況。

三、桶排序(Bucket Sort)

桶排序(Bucket Sort)的思想是將數組分到有限數量的桶子里。每個桶子再個別排序(有可能再使用別的排序算法)。當要被排序的數組內的數值是均勻分配的時候,桶排序可以以線性時間運行。桶排序過程動畫演示:Bucket Sort,桶排序原理圖如下:

 

C++代碼如下:

/************************************************************************* 
  > File Name: BucketSort.cpp 
  > Author: SongLee 
 ************************************************************************/ 
#include<iostream> 
using namespace std; 
 
/* 節(jié)點 */ 
struct node 
{ 
  int value; 
  node* next; 
}; 
 
/* 桶排序 */ 
void BucketSort(int A[], int max, int len) 
{ 
  node bucket[len]; 
  int count=0; 
  for(int i=0; i<len; ++i) 
  { 
    bucket[i].value = 0; 
    bucket[i].next = NULL; 
  } 
   
  for(int i=0; i<len; ++i) 
  { 
    node *ist = new node(); 
    ist->value = A[i]; 
    ist->next = NULL; 
    int idx = A[i]*len/(max+1); // 計算索引 
    if(bucket[idx].next == NULL) 
    { 
      bucket[idx].next = ist; 
    } 
    else /* 按大小順序插入鏈表相應位置 */ 
    { 
      node *p = &bucket[idx]; 
      node *q = p->next; 
      while(q!=NULL && q->value <= A[i]) 
      { 
        p = q; 
        q = p->next; 
      } 
      ist->next = q; 
      p->next = ist; 
    } 
  } 
 
  for(int i=0; i<len; ++i) 
  { 
    node *p = bucket[i].next; 
    if(p == NULL) 
      continue; 
    while(p!= NULL) 
    { 
      A[count++] = p->value; 
      p = p->next; 
    } 
  } 
} 
 
/* 輸出數組 */ 
void print(int A[], int len) 
{ 
  for(int i=0; i<len; ++i) 
    cout << A[i] << " "; 
  cout << endl; 
} 
 
/* 測試 */ 
int main() 
{ 
  int row[11] = {24,37,44,12,89,93,77,61,58,3,100}; 
  print(row, 11); 
  BucketSort(row, 235, 11); 
  print(row, 11); 
  return 0; 
} 

四、基數排序(Radix Sort)

基數排序(Radix Sort)是一種非比較型排序算法,它將整數按位數切割成不同的數字,然后按每個位分別進行排序?;鶖蹬判虻姆绞娇梢圆捎肕SD(Most significant digital)或LSD(Least significant digital),MSD是從最高有效位開始排序,而LSD是從最低有效位開始排序。

當然我們可以采用MSD方式排序,按最高有效位進行排序,將最高有效位相同的放到一堆,然后再按下一個有效位對每個堆中的數遞歸地排序,最后再將結果合并起來。但是,這樣會產生很多中間堆。所以,通常基數排序采用的是LSD方式。

LSD基數排序實現的基本思路是將所有待比較數值(正整數)統(tǒng)一為同樣的數位長度,數位較短的數前面補零。然后,從最低位開始,依次進行一次排序。這樣從最低位排序一直到最高位排序完成以后, 數列就變成一個有序序列。需要注意的是,對每一個數位進行排序的算法必須是穩(wěn)定的,否則就會取消前一次排序的結果。通常我們使用計數排序或者桶排序作為基數排序的輔助算法?;鶖蹬判蜻^程動畫演示:Radix Sort

C++實現(使用計數排序)如下:

/************************************************************************* 
  > File Name: RadixSort.cpp 
  > Author: SongLee 
 ************************************************************************/ 
#include<iostream> 
using namespace std; 
 
// 找出整數num第n位的數字 
int findIt(int num, int n) 
{ 
  int power = 1; 
  for (int i = 0; i < n; i++) 
  { 
    power *= 10; 
  } 
  return (num % power) * 10 / power; 
} 
 
// 基數排序(使用計數排序作為輔助) 
void RadixSort(int A[], int len, int k) 
{ 
  for(int i=1; i<=k; ++i) 
  { 
    int C[10] = {0};  // 計數數組 
    int B[len];    // 結果數組 
 
    for(int j=0; j<len; ++j) 
    { 
      int d = findIt(A[j], i); 
      C[d] += 1; 
    } 
 
    for(int j=1; j<10; ++j) 
      C[j] = C[j] + C[j-1]; 
 
    for(int j=len-1; j>=0; --j) 
    { 
      int d = findIt(A[j], i); 
      C[d] -= 1; 
      B[C[d]] = A[j]; 
    } 
     
    // 將B中排好序的拷貝到A中 
    for(int j=0; j<len; ++j) 
      A[j] = B[j]; 
  } 
} 
 
// 輸出數組 
void print(int A[], int len) 
{ 
  for(int i=0; i<len; ++i) 
    cout << A[i] << " "; 
  cout << endl; 
} 
 
// 測試 
int main() 
{ 
  int A[8] = {332, 653, 632, 5, 755, 433, 722, 48}; 
  print(A, 8); 
  RadixSort(A, 8, 3); 
  print(A, 8); 
  return 0; 
}

基數排序的時間復雜度是 O(k·n),其中n是排序元素個數,k是數字位數。注意這不是說這個時間復雜度一定優(yōu)于O(nlgn),因為n可能具有比較大的系數k。

另外,基數排序不僅可以對整數排序,也可以對有多個關鍵字域的記錄進行排序。例如,根據三個關鍵字年、月、日來對日期進行排序。

相關文章

  • 詳細聊聊c語言中的緩沖區(qū)問題

    詳細聊聊c語言中的緩沖區(qū)問題

    緩沖區(qū)又稱為緩存,它是內存空間的一部分,也就是說在內存空間中預留了一定的存儲空間,這些存儲空間用來緩沖輸入或輸出的數據,這部分預留的空間就叫做緩沖區(qū),這篇文章主要給大家介紹了關于c語言中緩沖區(qū)問題的相關資料,需要的朋友可以參考下
    2021-11-11
  • 一文讓你徹底明白C++中的const

    一文讓你徹底明白C++中的const

    這篇文章主要給大家介紹了關于C++中const的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-11-11
  • C++ 操作系統(tǒng)內存分配算法的實現詳解

    C++ 操作系統(tǒng)內存分配算法的實現詳解

    本文主要介紹了在動態(tài)分區(qū)管理方式下采用不同的分配算法實現主存分配和實現主存回收,旨在幫助學生理解在動態(tài)分區(qū)管理方式下應怎樣實現主存空間的分配和回收。感興趣的可以了解一下
    2021-11-11
  • C++中的局部變量、全局變量、局部靜態(tài)變量、全局靜態(tài)變量的區(qū)別

    C++中的局部變量、全局變量、局部靜態(tài)變量、全局靜態(tài)變量的區(qū)別

    本文主要介紹了C++中的局部變量、全局變量、局部靜態(tài)變量、全局靜態(tài)變量的區(qū)別。具有很好的參考價值,下面跟著小編一起來看下吧
    2017-02-02
  • 解決VC++編譯報錯error C2248的方案

    解決VC++編譯報錯error C2248的方案

    這篇文章主要介紹了解決VC++編譯報錯error C2248的方案的相關資料,需要的朋友可以參考下
    2015-11-11
  • C語言文件操作實現數據持久化(幫你快速了解文件操作函數)

    C語言文件操作實現數據持久化(幫你快速了解文件操作函數)

    持久數據其實就是將數據保存到數據庫,下面這篇文章主要給大家介紹了關于C語言文件操作實現數據持久化(幫你快速了解文件操作函數)的相關資料,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下
    2022-11-11
  • C++分析如何用虛析構與純虛析構處理內存泄漏

    C++分析如何用虛析構與純虛析構處理內存泄漏

    虛析構和純虛析構共性:可以解決父類指針釋放子類對象,都需要有具體的函數實現;虛析構和純虛析構區(qū)別:如果是純虛析構,該類屬于抽象類,無法實例化對象
    2022-08-08
  • C++實現簡單迷宮游戲

    C++實現簡單迷宮游戲

    這篇文章主要為大家詳細介紹了C++實現簡單迷宮游戲,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-01-01
  • C++實現簡易通訊錄管理系統(tǒng)

    C++實現簡易通訊錄管理系統(tǒng)

    這篇文章主要為大家詳細介紹了C++實現簡易通訊錄管理系統(tǒng),文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-06-06
  • 全局靜態(tài)存儲區(qū)、堆區(qū)和棧區(qū)深入剖析

    全局靜態(tài)存儲區(qū)、堆區(qū)和棧區(qū)深入剖析

    在C++中,內存可分為系統(tǒng)數據區(qū),自由存儲區(qū),文本區(qū),const數據區(qū),全局靜態(tài)區(qū),堆區(qū)和棧區(qū)
    2012-11-11

最新評論