1. 程式人生 > >各種排序方法的介紹與比較

各種排序方法的介紹與比較

來源:我是碼農,轉載請保留出處和連結!

本文連結:http://www.54manong.com/?id=210

前記:這一章中主要對資料排序相關的概念和方法進行了講解,今天的拓展資源就對排序的基本概念、幾種常見排序方法的演算法及優缺點、插入排序的演算法和C語言實現等,同學們多瞭解一下。

排序:是計算機內經常進行的一種操作,其目的是將一組“無序”的記錄序列調整為“有序”的記錄序列。

內部排序:若整個排序過程不需要訪問外存便能完成,則稱此類排序問題為內部排序;內部排序的過程是一個逐步擴大記錄的有序序列長度的過程。

外部排序:若參加排序的記錄數量很大,整個序列的排序過程不可能在記憶體中完成,則稱此類排序問題為外部排序。

  內排序的方法有許多種,按所用策略不同,可歸納為五類:插入排序、選擇排序、交換排序、歸併排序和分配排序。

  其中,插入排序主要包括直接插入排序和希爾排序兩種;選擇排序主要包括直接選擇排序和堆排序;交換排序主要包括氣(冒)泡排序和快速排序。

一、氣泡排序

  已知一組無序資料a[1]、a[2]、……a[n],需將其按升序排列。首先比較a[1]與a[2]的值,若a[1]大於a[2]則交換兩者的值,否則不變。再比較a[2]與a[3]的值,若a[2]大於a[3]則交換兩者的值,否則不變。再比較a[3]與a[4],以此類推,最後比較a[n-1]與a[n]的值。這樣處理一輪後,a[n]的值一定是這組資料中最大的。再對a[1]~a[n-1]以相同方法處理一輪,則a[n-1]的值一定是a[1]~a[n-1]中最大的。再對a[1]~a[n-2]以相同方法處理一輪,以此類推。共處理n-1輪後a[1]、a[2]、……a[n]就以升序排列了。

優點:穩定,比較次數已知;

缺點:慢,每次只能移動相鄰兩個資料,移動資料的次數多。

二、選擇排序

  已知一組無序資料a[1]、a[2]、……a[n],需將其按升序排列。首先比較a[1]與a[2]的值,若a[1]大於a[2]則交換兩者的值,否則不變。再比較a[1]與a[3]的值,若a[1]大於a[3]則交換兩者的值,否則不變。再比較a[1]與a[4],以此類推,最後比較a[1]與a[n]的值。這樣處理一輪後,a[1]的值一定是這組資料中最小的。再將a[2]與a[3]~a[n]以相同方法比較一輪,則a[2]的值一定是a[2]~a[n]中最小的。再將a[3]與a[4]~a[n]以相同方法比較一輪,以此類推。共處理n-1輪後a[1]、a[2]、……a[n]就以升序排列了。

優點:穩定,比較次數與氣泡排序一樣;

缺點:相對之下還是慢。

三、插入排序

  已知一組升序排列資料a[1]、a[2]、……a[n],一組無序資料b[1]、b[2]、……b[m],需將二者合併成一個升序數列。首先比較b[1]與a[1]的值,若b[1]大於a[1],則跳過,比較b[1]與a[2]的值,若b[1]仍然大於a[2],則繼續跳過,直到b[1]小於a陣列中某一資料a[x],則將a[x]~a[n]分別向後移動一位,將b[1]插入到原來a[x]的位置這就完成了b[1]的插入。b[2]~b[m]用相同方法插入。(若無陣列a,可將b[1]當作n=1的陣列a)

優點:穩定,快;

缺點:比較次數不一定,比較次數越少,插入點後的資料移動越多,特別是當資料總量龐大的時候,但用連結串列可以解決這個問題。

四、縮小增量排序

  由希爾在1959年提出,又稱希爾排序 (shell排序)。

  已知一組無序資料a[1]、a[2]、……a[n],需將其按升序排列。發現當n不大時,插入排序的效果很好。首先取一增量d(d<n),將a[1]、a[1+d]、a[1+2d]……列為第一組,a[2]、a[2+d]、a[2+2d]……列為第二組……,a[d]、a[2d]、a[3d]……列為最後一組以次類推,在各組內用插入排序,然後取d'<d,重複上述操作,直到d=1。

優點:快,資料移動少;

缺點:不穩定,d的取值是多少,應取多少個不同的值,都無法確切知道,只能憑經驗來取。

五、快速排序

  快速排序是氣泡排序的改進版,是目前已知的最快的排序方法。

  已知一組無序資料a[1]、a[2]、……a[n],需將其按升序排列。首先任取資料a[x]作為基準。比較a[x]與其它資料並排序,使a[x]排在資料的第k位,並且使a[1]~a[k-1]中的每一個數據<a[x],a[k+1]~a[n]中的每一個數據>a[x],然後採用分治的策略分別對a[1]~a[k-1]和a[k+1]~a[n]兩組資料進行快速排序。

優點:極快,資料移動少;

缺點:不穩定。

六、箱排序

已知一組無序正整數資料a[1]、a[2]、……a[n],需將其按升序排列。首先定義一個數組x[m],且m>=a[1]、a[2]、……a[n],接著迴圈n次,每次x[a]++.

優點:快,效率達到O(1)。

缺點:資料範圍必須為正整數並且比較小。

插入演算法的演算法描述:

  一般來說,插入排序都採用in-place在陣列上實現。具體演算法描述如下:

  1. 從第一個元素開始,該元素可以認為已經被排序

  2. 取出下一個元素,在已經排序的元素序列中從後向前掃描

  3. 如果該元素(已排序)大於新元素,將該元素移到下一位置

  4. 重複步驟3,直到找到已排序的元素小於或者等於新元素的位置

  5. 將新元素插入到該位置中

  6. 重複步驟2

如果比較操作的代價比交換操作大的話,可以採用二分查詢法來減少比較操作的數目。該演算法可以認為是插入排序的一個變種,稱為二分查詢排序。

示例程式碼:

示例程式碼為C語言,輸入引數中,需要排序的陣列為array[],起始索引為first,終止索引為last。示例程式碼的函式採用in-place排序,呼叫完成後,array[]中從first到last處於升序排列。

void insertion_sort(char array[], unsigned int first, unsigned int last)
{
  int i,j;
  int temp;
  for (i = first+1; i<=last;i++)
  {
  temp = array;
  j=i-1;
  //與已排序的數逐一比較, 大於temp時, 該數移後
  while((j>=first) && (array[j] > temp))
  {
  array[j+1] = array[j];
  j--;
  }
  array[j+1] = temp;
  }
  }
  這個更好:
  void InsertSort(char array[],unsigned int n)
  {
  int i,j;
  int temp;
  for(i=1;i<n;i++)
  {
  temp = array;//store the original sorted array in temp
  for(j=i ; j>0 && temp < array[j-1] ; j--)//compare the new array with temp
  {
  array[j]=array[j-1];//all larger elements are moved one pot to the right
  }
  array[j]=temp;
  }
  }
  這個是c++語言版本的插入排序。為了支援list使用了std::advance()。
  #include <iterator>
  template<typename biIter>
  void insertion_sort(biIter begin, biIter end)
  {
  typedef typename std::iterator_traits<biIter>::value_type value_type;
  biIter bond = begin;
  std::advance(bond, 1);
  for(; bond!=end; std::advance(bond, 1)) {
  value_type key = *bond;
  biIter ins = bond;
  biIter pre = ins;
  std::advance(pre, -1);
  while(ins!=begin && *pre>key) {
  *ins = *pre;
  std::advance(ins, -1);
  std::advance(pre, -1);
  }
  *ins = key;
  }
}