各種排序算法總結

明天就要去參加阿里巴巴的實習生筆試了,雖然沒想着能進去,但是態度還是要端正的,也沒什麼可以準備的,複習複習排序吧。

1 插入排序

void InsertSort(int a[], int n)

{

      for (int i=1; i<n; ++i) {

            int key = a[i];

            int j = i - 1;

            while(j>=0 && a[j]>key) {

                  a[j+1] = a[j];

                  --j;

            }

            a[j+1] = key;

      }

}

插入排序是穩定的排序,平均和最壞時間複雜度是O(n^2)。最好的時間複雜度是O(n),對應於全部排好序的情況。

 

2 冒泡排序

void BubbleSort(int a[], int n)

{

      for (int i=1; i<n; ++i) {

            for(int j=0; j<n-i; ++j) {

                  if(a[j]>a[j+1]) {

                       inttemp = a[j];

                       a[j] = a[j+1];

                       a[j+1] = temp;

                  }

            }

      }

}

冒泡排序是穩定的排序,平均和最壞時間複雜度是O(n^2)。

 

3 選擇排序

每一趟從待排序的數據元素中選出最小(或最大)的一個元素,順序放在已排好序的數列的最後,直到全部待排序的數據元素排完。選擇排序是不穩定的排序方法。

void SelectSort(int a[], int n)

{

      for (int i=0; i<n-1; ++i) {

            for(int j=i+1; j<n; ++j) {

                  if(a[i]>a[j]) {

                       inttemp = a[i];

                       a[i] = a[j];

                       a[j] = temp;

                  }

            }

      }

}

選擇排序是不穩定的,因爲,在一趟選擇,如果當前元素比一個元素小,而該小的元素又出現在一個和當前元素相等的元素後面,那麼交換後穩定性就被破壞了。舉個例子,序列5 8 5 2 9,我們知道第一遍選擇第1個元素5會和2交換,那麼原序列中2個5的相對前後順序就被破壞了平均和最壞時間複雜度是O(n^2)。

 

4 希爾排序(縮小增量排序)

該方法的基本思想是:先將整個待排元素序列分割成若干個子序列(由相隔某個“增量”的元素組成的)分別進行直接插入排序,然後依次縮減增量再進行排序,待整個序列中的元素基本有序(增量足夠小)時,再對全體元素進行一次直接插入排序。因爲直接插入排序在元素基本有序的情況下(接近最好情況),效率是很高的,因此希爾排序在時間效率上比前兩種方法有較大提高。

void ShellSort(int a[], int n)

{

      for (int gap=n/2; gap>0; gap/=2) {

            for(int i=0; i<gap; ++i) {

                  for(int j=i+gap; j<n; j+=gap) {

                       if(a[j]<a[j-gap]) {

                             int temp = a[j];

                             int k = j-gap;

                             while (k>=0&&a[k]>temp) {

                                   a[k+gap] = a[k];

                                   k -= gap;

                             }

                             a[k+gap] = temp;

                       }

                  }

            }

      }

}

 

void ShellSortImproved(int a[], int n)

{

      for (int gap=n/2; gap>0; gap/=2) {

            for(int j=gap; j<n; ++j) {

                  if(a[j]<a[j-gap]) {

                       inttemp = a[j];

                       intk = j - gap;

                       while(k>=0 && a[k]>temp) {

                             a[k+gap] = a[k];

                             k -= gap;

                       }

                       a[k+gap] = temp;

                  }

            }

      }

}

希爾排序是不穩定的。由於多次插入排序,我們知道一次插入排序是穩定的,不會改變相同元素的相對順序,但在不同的插入排序過程中,相同的元素可能在各自的插入排序中移動,最後其穩定性就會被打亂,所以shell排序是不穩定的。使用希爾增量時,最壞運行時間是O(n^2),使用Hibbard增量時,最壞運行時間是O(n^3/2)。

 

5. 堆排序

void MaxHeapify1(int a[], int n, int i)

{

      int l = LEFT(i);

      int r = RIGHT(i);

      int largest;

      if (l<n&& a[l]>a[i])

            largest= l;

      else

            largest= i;

      if (r<n&& a[r]>a[largest])

            largest= r;

      if (largest!=i) {

            swap(a[i], a[largest]);

            MaxHeapify1(a, n, largest);

      } else

            return;

}

 

void MaxHeapify2(int a[], int n, int i)

{

      int clargest;

      while (1){

            c= LEFT(i);

            if (c>=n)

                  break;

            if (a[i]<a[c])

                  largest= c;

            else

                  largest= i;

            if (c+1<n) {

                  if(a[largest]<a[c+1])

                       largest= c + 1;

            }

            if (largest!=i) {

                  swap(a[i], a[largest]);

                  i= largest;

            } else

                  break;

      }

}

 

void HeapSort(int a[], int n)

{

      for (int i=n/2-1; i>=0;--i)

            MaxHeapify1(a, n, i);

      for (int i=n-1; i>0; --i) {

            swap(a[i], a[0]);

            MaxHeapify1(a, i, 0);

      }

}

堆排序是原地排序,但是不是穩定排序。時間複雜度O(nlogn)。

 

6 歸併排序

void Merge(int a[], int p, int q, int r)

{

      int n1 = q - p + 1;

      int n2 = r - (q+1) +1;

 

      int *L = new int[n1+1];

      int *R = new int[n2+1];

 

      for (int i=0; i<n1; ++i)

            L[i] = a[p+i];

      for (int i=0; i<n2; ++i)

            R[i] = a[q+1+i];

      L[n1] = INT_MAX;//哨兵

      R[n2] = INT_MAX;

      int i = 0, j = 0;

      for (int k=p; k<=r; ++k) {

            if (L[i]<=R[j])

                  a[k] = L[i++];

            else

                  a[k] = R[j++];

      }

      delete[] L;

      delete[] R;

}

 

void MergeSort(int a[], int p, int r)

{

      if (p<r) {;

            int q = ((r-p)>>1) + p;

            MergeSort(a, p, q);

            MergeSort(a, q+1, r);

            Merge(a, p, q, r);

      }

}

合併過程中我們可以保證如果兩個當前元素相等時,我們把處在前面的序列的元素保存在結果序列的前面,這樣就保證了穩定性。所以,歸併排序也是穩定的排序算法。時間複雜度是O(nlogn)。可以在空間複雜度O(1)的條件下實現歸併排序

 

7. 快速排序

被快速排序所使用的空間,依照使用的版本而定。使用原地(in-place)分區的快速排序版本,在任何遞歸調用前,僅會使用固定的額外空間。然而,如果需要產生O(log n)嵌套遞歸調用,它需要在他們每一個存儲一個固定數量的信息。因爲最好的情況最多需要O(log n)次的嵌套遞歸調用,所以它需要O(log n)的空間。最壞情況下需要O(n)次嵌套遞歸調用,因此需要O(n)的空間。

void Median3(int a[], int p, int r)

{

      int median = p + ((r-p)>>1);

      if (a[p]>a[median])

            swap(a[p], a[median]);

      if (a[p]>a[r])

            swap(a[p], a[r]);

      if (a[median]>a[r])

            swap(a[median], a[r]);

      swap(a[median], a[r]);

}

 

int Partition1(int a[], int p, int r)

{

      Median3(a, p, r);

      int x = a[r];

      int i = p - 1;

      for (int j=p; j<=r-1; ++j) {

            if (a[j]<=x) {

                  ++i;

                  swap(a[i], a[j]);

            }

      }

      swap(a[i+1], a[r]);

      return i+1;

}

 

int Partition2(int a[], int p, int r)

{

      Median3(a, p, r);

      int i = p-1, j = r;

      int x = a[r];

      for (;;) {

            while(a[++i]<x);

            while(a[--j]>x);

            if (i<j)

                  swap(a[i], a[j]);

            else

                  break

      }

      swap(a[i], a[r]);

      return i;

}

 

void QuickSort(int a[], int p, int r) {

      if (p<r) {

            int q = Partition2(a, p, r);

            QuickSort(a, p, q-1);

            QuickSort(a, q+1, r);

      }

}

快速排序是不穩定的排序,最差時間複雜度是O(n^2),平均時間複雜度是O(nlogn)。

 

8. 桶排序

void BucketSort(int a[], int n)

{

      int *count = new int[1000];

      memset(count, 0, sizeof(int)*1000);

      for (int i=0; i<n; ++i) {

            ++count[a[i]];

      }

      int k = 0;

      for (int i=0; i<1000; ++i){

            while(count[i]--){

                  a[k++] = i;

            }

      }

}

如果count有M個單元,算法用時O(M+N),桶排序是穩定的排序。但是需要額外的空間。

 

 

穩定的排序有:冒泡,插入,歸併,基數,桶。


發佈了29 篇原創文章 · 獲贊 46 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章