算法導論:比較排序算法筆記

 

好幾天沒看《算法導論》,今天看了一天的排序算法,印象第一的是基數算法,由於竟然違反個人一個常識,它採用的是最低有效位進行排序的。

 


插入排序、歸併排序、堆排序、快速排序,這些都是比較排序算法:它們都是經過對元素進行比較操做來肯定輸入數組的有序次序,這些算法能夠用決策樹模型分析,能夠證實任意比較排序算法排序n個元素的最壞狀況運行時間的下界爲Omega(nlgn),其中堆排序和歸併排序是漸進最優的比較排序算法。ios

 

算法c++

最壞狀況運行時間算法

平均狀況/指望運行時間api

插入排序(原址)數組

Thetan^2數據結構

Thetan^2性能

歸併排序ui

Thetanlgnspa

Thetanlgncode

堆排序(原址)

Thetanlgn

--------

快速排序(原址)

Thetan^2

Thetanlgn)指望)

 

 

堆排序

堆排序的時間複雜度和歸併排序同樣,都是O(nlgn),另外它具備插入排序的空間原址性優勢,即任什麼時候候都只須要常數個額外的空間元素存儲臨時數據。
堆排序中主要是引入了一種稱爲「堆」的數據結構來進行信息管理。這裏的(二叉)堆能夠看做是一個近似的徹底二叉樹。樹上的每個結點對應數組中的一個元素,如圖1所示。

二叉堆能夠分爲兩種形式:最大堆和最小堆。由於在堆排序中應用的是最大堆,因此這裏只說最大堆,最大堆中是指除根之外的全部結點i都要知足: A[PARENT[i]]>=A[i],即某個結點的值至多與其父節點同樣大。

兩個性質:
  1. 一個包含n個元素的堆能夠看着一顆徹底二叉樹,那麼該堆的高度是O(lgn),並且堆結構上的一些基本操做的運行時間至多與樹的高度成正比,即時間複雜度O(lgn);
  2. 當用數組表示存儲n個元素的堆時,葉結點下標分別是[n/2]+1,[n/2]+2,…,n。
僞代碼:
HEAPSORT(A)
  BULID-HEAP(A)
  For  i = A.length  downto  2
  exchange A[1] with A[i]
  length-size=A.length-size-1
    HEAPIFY(A,1)

下面分別解析上面代碼調用的子過程
這個子過程是維護最大堆,被不斷的調用。

這個子過程是採用自底向上建堆的過程,裏面用到了上文的性質2。

具體代碼實現過程:
#include <cstdio>
#include <cstdlib>
#include <cmath>
using namespace std;
 
int parent(int);
int left(int);
int right(int);
void Max_Heapify(int [], int, int);
void Build_Max_Heap(int [], int);
void print(int [], int);
void HeapSort(int [], int);
 
/*父結點*/
int parent(int i)
{
    return (int)floor((i - 1) / 2);
}
 
/*左子結點*/
int left(int i)
{
    return (2 * i + 1);
}
 
/*右子結點*/
int right(int i)
{
    return (2 * i + 2);
}
 
/*單一子結點最大堆積樹調整*/
void Max_Heapify(int A[], int i, int heap_size)
{
    int l = left(i);
    int r = right(i);
    int largest;
    int temp;
    if(l < heap_size && A[l] > A[i])
    {
        largest = l;
    }
    else
    {
        largest = i;
    }
    if(r < heap_size && A[r] > A[largest])
    {
        largest = r;
    }
    if(largest != i)
    {
        temp = A[i];
        A[i] = A[largest];
        A[largest] = temp;
        Max_Heapify(A, largest, heap_size);
    }
}
 
/*創建最大堆積樹*/
void Build_Max_Heap(int A[],int heap_size)
{
    for(int i = (heap_size-2)/2; i >= 0; i--)
    {
        Max_Heapify(A, i, heap_size);
    }
}
 
/*印出樹狀結構*/
void print(int A[], int heap_size)
{
    for(int i = 0; i < heap_size;i++)
    {
        printf("%d ", A[i]);
    }
    printf("\n");
}
 
/*堆積排序程序碼*/
void HeapSort(int A[], int heap_size)
{
    Build_Max_Heap(A, heap_size);
    int temp;
    for(int i = heap_size - 1; i >= 0; i--)
    {
        temp = A[0];
        A[0] = A[i];
        A[i] = temp;
        Max_Heapify(A, 0, i);
    }
    print(A, heap_size);
}
 
/*輸入資料並作堆積排序*/
int main(int argc, char* argv[])
{
    const int heap_size = 13;
    int A[] = {19, 1, 10, 14, 16, 4, 7, 9, 3, 2, 8, 5, 11};
    HeapSort(A, heap_size);
    system("pause");
    return 0;
}

另外一個代碼:
#include <iostream>
using namespace std;
/*
        #堆排序#%
          #數組實現#%
*/
//#篩選算法#%
void sift(int d[], int ind, int len)
{
        //#置i爲要篩選的節點#%
        int i = ind;
 
        //#c中保存i節點的左孩子#%
        int c = i * 2 + 1; //#+1的目的就是爲了解決節點從0開始而他的左孩子一直爲0的問題#%
 
        while(c < len)//#未篩選到葉子節點#%
        {
                //#若是要篩選的節點既有左孩子又有右孩子而且左孩子值小於右孩子#%
                //#從兩者中選出較大的並記錄#%
                if(c + 1 < len && d[c] < d[c + 1])
                        c++;
                //#若是要篩選的節點中的值大於左右孩子的較大者則退出#%
                if(d[i] > d[c]) break;
                else
                {
                        //#交換#%
                        int t = d[c];
                        d[c] = d[i];
                        d[i] = t;
                        //
                        //#重置要篩選的節點和要篩選的左孩子#%
                        i = c;
                        c = 2 * i + 1;
                }
        }
 
        return;
}
 
void heap_sort(int d[], int n)
{
        //#初始化建堆, i從最後一個非葉子節點開始#%
        for(int i = (n - 2) / 2; i >= 0; i--)
                sift(d, i, n);
 
        for(int j = 0; j < n; j++)
        {
                //#交換#%
                int t = d[0];
                d[0] = d[n - j - 1];
                d[n - j - 1] = t;
 
                //#篩選編號爲0 #%
                sift(d, 0, n - j - 1);
 
        }
}
 
int main()
{
        int a[] = {3, 5, 3, 6, 4, 7, 5, 7, 4}; //#QQ#%
 
        heap_sort(a, sizeof(a) / sizeof(*a));
 
        for(int i = 0; i < sizeof(a) / sizeof(*a); i++)
        {
                cout << a[i] << ' ';
        }
        cout << endl;
    return 0;
}

快速排序

上面的堆排序是一個優秀的算法,可是在實際應用中,應用更多的倒是快速 排序。快速排序算法的最壞狀況時間複雜度爲O(n^2),雖然最壞狀況的時間複雜度不好,可是它的平均性能卻很是好,它的指望時間複雜度是O(nlgn),並且O(nlgn)中隱含的常數因子很是小。另外它還能進行原址排序,在虛擬環境下也能很好地工做。快速排序同歸並排序同樣,都是基於分治思想的,對一個典型的子數組A[p..r]進行快速排序的三步分治過程以下:

僞代碼:
QUICKSORT(A,p,r)
  if  p < r
  q=PARTITION(A,p,r)
  QUICKSORT(A,p,q-1)
  QUICKSORT(q+1,r)
上面算法的關鍵部分是PARTITION過程,它實現了對子數組的原址重排。
  PARTITION(A,p,r)
  x=A[r]
  i=p-1
  for j=p to r-1
  if A[j]<=x
  I=i+1
  Exchange A[i] with A[j]
  Exchange A[i+1] with A[r]
  Return i+1



 
具體代碼實現:
#include <utility>
using std::swap;
 
int partition(int* array, int left, int right)
{
        int index = left;
        int pivot = array[index];       
        swap(array[index], array[right]);
        for (int i=left; i<right; i++)
        {
                if (array[i] > pivot)    // 降序
                        swap(array[index++], array[i]);
        }
        swap(array[right], array[index]);
        return index;
}
 
void qsort(int* array, int left, int right)
{
        if (left >= right) 
                return;
        int index = partition(array, left, right);
        qsort(array, left, index - 1);
        qsort(array, index + 1, right);
}
相關文章
相關標籤/搜索