zoukankan      html  css  js  c++  java
  • 算法导论 第二部分——排序和顺序统计量

    一、堆排序 : 原址排序 复杂度: nlg n

    最大堆: A[parent(i)] > = A[i]

    最小堆: A[parent(i)] < = A[i]

    除了最底层外,其它层都是满状态。

    判断节点是否为叶节点:  [n/2]+1,.....n 均为叶节点

    //Max-heapify(A,i)   : A为一个 假定 left(i) right(i)的二叉树都是最大堆 。 但是A[i]可能小于孩子  。 时间复杂度为: o(h)
    //build_max_heap(A,len) : 将一个数组转换为 最大堆(从底向上的建堆) , 时间复杂度为: o(n)
    //heap_sort(A,len)      : 将 A 进行排序  复杂度 为 nlg(n)
    void max_heapify(int *A , int i,int len)
    {
            int r = RIGHT(i);
            int l = LEFT(i);
            int large = i;
            if (i <= len&&*(A + i-1) < *(A+r-1))
                large = r;
            if (i <= len && *(A+large-1)<*(A+l-1))
                large = l;
            if (large == i)
                return;
            else
                swap(A, large-1, i-1,len);
            if (i <= len && 2 * i < len/2+1)    // decide if  the left son node is leaf node 
                max_heapify(A, large, len);     // not leaf node , carry on the recursion 
            else
                return;                            // end the recursion
    }
    
    void build_max_heap(int *A, int len)
    {
        for (int i = len / 2; i>0; i--)
            max_heapify(A,i,len);
    }
    
    void heap_sort(int *A, int len)
    {
        build_max_heap(A,len);
        for (int i = len; i > 0; i--)
        {
            swap(A, i - 1, 0 ,len);
            max_heapify(A,1,i);
        }
    }

    二、快速排序   原址

    最坏时间复杂度: n^2 ,但是是实际应用中最好的排序算法,期望时间复杂度:nlgn,而且隐藏的因子特别小。

     1 // partition();     将数组A[p,……,r] 分成 A[p,….,q-1]<=A[q]<=A[q+1,r],返回q的数组下标
     2 // quick_sort() :   递归调用,将分割好的数组继续分割
     3 int  PARTITION(int *A ,int p , int r ,int len)
     4 {
     5     if (p >= len || r >= len|| p<0||r<=0)
     6     {
     7         cout << "function PARTITION erro : the p or r is out range of the vector or array" << endl;
     8         return 0;
     9     }
    10     int i = p - 1;
    11     int x = 0;
    12     for (int j = p; j < r; j++)
    13     {
    14         if (*(A+j)<*(A+r))
    15         {
    16             i = i + 1;
    17             EXCHANGE(A,i,j,len);
    18         }
    19     }
    20     EXCHANGE(A,r,i+1,len);
    21     return i + 1;
    22 }
    23 void QUICK_SORT(int *A,int p,int r,int len)
    24 {
    25     if (p >= r)
    26     {
    27         cout << "function QUICK_SORT error : r must larger than p" << endl;
    28         return;
    29     }
    30     if (p >= len || r >= len||p<0||r<=0)
    31     {
    32         cout << "function QUICK_SORT error : the p or r is out range of the vector or array" << endl;
    33         return;
    34     }
    35     if (p == r)
    36     {
    37         cout << "end of calling of QUICK_SORT" << endl;
    38         return;
    39     }
    40     int mid=PARTITION(A,p,r,len);
    41     cout << "mid is :" << mid << "    p ="<<p<<"    r="<<r<<"    len="<<len<<endl;
    42     QUICK_SORT(A,p,mid-1,len);
    43     QUICK_SORT(A,mid+1,r,len);
    44     output(A,len);
    45 }

    performance:

      worst situation: 

        T(n) = T(n-1) + k;  它的复杂度为 n^2 ,当数组已经是排好序的,那么他需要进行  n^2 次运算

      best situation: 

        T(n) = 2T(n/2)+k;  当两个子问题的规模都不大于n/2,这时候快速排序的性能最好  nlg n次运算

    平衡的划分: 

      只要每次划分是一种常数比例的划分,都会产生深度为lgn的递归树

      例如: T(n) = T(n/10)+T(9n/10) + k;

    quicksort using random function

    RANDOMIZED-PARTITION(A, p, r):
        i = RANDOM(p, r )
        exchange A[r ] ↔ A[i ]
        return PARTITION(A, p, r )

    三、线性时间排序

    1、 决策树模型:

      每次比较判断可以影响下一次的比较,

    定理:对于一个比较排序算法在最坏情况下,都需要做Ω(nlgn)次比较。

    参考: http://www.cnblogs.com/Anker/archive/2013/01/25/2876397.html

    2、计数排序 : 时间复杂度为n ,其实是max-min+1,需要额外开辟内存空间

    前提条件: 所有的元素都必须在一个范围内,如:  min<a[i]<=max

    int * count_sort(int *a ,int n){
        //initialize 
         int max=0xffffffff, min=0x7fffffff;
         for (int i = 0; i < n; i++){
             if (*(a + i)>max)
                 max = *(a+i);
             if (*(a + i) < min)
                 min = *(a + i);
         }
         const int len = max - min+1;
         int *c = new int[len];
         int *re = new int[n];
         memset(c,0,len*4);
    
         //count the number of every element in a[] then store in c[]
         for (int i = 0; i < n; i++){
             *(c + *(a+i) - min) += 1;
         }
         //sort based c[] 
         for (int i = 0,j=0; i < len; i++){
             int k = 0;
             while (k < *(c + i)){
                 *(re + j) = min + i;
                 j++;
                 k++;
             }
         }
         delete []c;
         return re;
    }

    四:中位数和顺序统计量

    1、期望为线性时间的选择算法: 最坏时间为n^2

    RANDOMIZED_SELECT(A,p,r,i)
          if p==r
             then return A[p]
    //通过partition函数产生q值,与快速排序的partition原理相同
          q = RANDOMIZED_PARTITION(A,p,r)
          k = q-p+1;
          if i==k
             then return A[q]
          else  if i<k
              then return RANDOMIZED_SELECT(A,p,q-1,i)
          else
              return RANDOMIZED_SELECT(A,p,q-1,i-k)

    c++代码:

    #include <iostream>
    #include <ctime>
    #include <cstdlib>
    
    using namespace std;
    
    void swap(int* x, int* y)
    {
        int temp;
        temp = *x;
        *x = *y;
        *y = temp;
    }
    
    inline int random(int x, int y)
    {
        srand((unsigned)time(0));
        int ran_num = rand() % (y - x) + x;
        return ran_num;
    }
    
    int partition(int* arr, int p, int r)
    {
        int x = arr[r];
        int i = p - 1;
        for(int j = p; j < r; j++)
        {
            if (arr[j] <= x)
            {
                i++;
                swap(arr[i], arr[j]);
            }
        }
        swap(arr[i + 1], arr[r]);
        return ++i;
    }
    
    int randomizedpartition(int* arr, int p, int r)
    {
        int i = random(p, r);
        swap(arr[r], arr[i]);
        return partition(arr, p, r);
    }
    
    int randomizedSelect(int* arr, int p, int r, int i)
    {
        if(p == r)
        {
            return arr[p];
        }
        int q = randomizedpartition(arr, p, r);
        int k = q - p + 1;
        if(i == k)
        {
            return arr[q];
        }
        else if(i < k)
        {
            return randomizedSelect(arr, p, q - 1, i);
        }
        else
            return randomizedSelect(arr, q + 1, r, i - k);
    }
    
    int main()
    {
        int arr[] = {1, 3, 5, 23, 64, 7, 23, 6, 34, 98, 100, 9};
        int i = randomizedSelect(arr, 0, 11, 4);
        cout << i << endl;
        return 0;
    }
    View Code

    2、最坏情况为线性时间的选择算法

    SELECT算法

      (1)如果n=1,则select直接返回该值

      (2)将输入数组的n个元素划分为 n/5组,每组5个元素。且至多只有一个组由剩下的n mod 5个元素组成

      (3)寻找每个组的中位数,首先对每个组中的元素进行插入排序,然后从排序过的序列中选出中位数

      (4)对3步中找出的中位数,递归调用select以找出其中位数x(如果有偶数个中位数,根据约定,x是下中位数)

      (5)如果i=k,则返回x。否则,如果i<k, 则在低区递归调用select以找出第i小的元素。如果i>k,则在高区中找地第(i-k)个最小元素。

    SELECT算法通过中位数进行划分,可以保证每次划分是对称的,这样就能保证最坏情况下运行时间为θ(n)。算法的证明过程是采用数学归纳法,大致能看懂(p123)。

    举个例子说明此过程,求集合A={32,23,12,67,45,78,10,39,9,58,125,84}的第5小的元素,操作过程如下图所示:

                                   

    下面c代码和部分内容转自:http://www.cnblogs.com/Anker/archive/2013/01/25/2877311.html

    #include <stdio.h>
    #include <stdlib.h>
    
    int partition(int* datas,int beg,int last,int mid);
    int select(int* datas,int length,int i);
    void swap(int* a,int *b);
    
    int main()
    {
        int datas[12]={32,23,12,67,45,78,10,39,9,58,125,84};
        int i,ret;
        printf("The array is: 
    ");
        for(i=0;i<12;++i)
            printf("%d ",datas[i]);
        printf("
    ");
        for(i=1;i<=12;++i)
        {
           ret=select(datas,12,i);
           printf("The %dth least number is: %d 
    ",i,datas[i-1]);
        }
        exit(0);
    }
    
    int partition(int* datas,int beg,int last,int mid)
    {
        int i,j;
        swap(datas+mid,datas+last);
        i=beg;
        for(j=beg;j<last;j++)
        {
            if(datas[j] < datas[last])
            {
                swap(datas+i,datas+j);
                i++;
            }
        }
        swap(datas+i,datas+last);
        return i;
    }
    
    int select(int* datas,int length,int i)
    {
        int groups,pivot;
        int j,k,t,q,beg,glen;
        int mid;
        int temp,index;
        int *pmid;
        if(length == 1)
            return datas[length-1];
        if(length % 5 == 0)
            groups = length/5;
        else
            groups = length/5 +1;
        pmid = (int*)malloc(sizeof(int)*groups);
        index = 0;
        for(j=0;j<groups;j++)
        {
            beg = j*5;
            glen = beg+5;
            for(t=beg+1;t<glen && t<length;t++)
            {
                temp = datas[t];
                for(q=t-1;q>=beg && datas[q] > datas[q+1];q--)
                        swap(datas+q,datas+q+1);
                swap(datas+q+1,&temp);
            }
            glen = glen < length ? glen : length;
            pmid[index++] = beg+(glen-beg)/2;
        }
        for(t=1;t<groups;t++)
        {
            temp = pmid[t];
            for(q=t-1;q>=0 && datas[pmid[q]] > datas[pmid[q+1]];q--)
                swap(pmid+q,pmid+q+1);
            swap(pmid+q+1,&temp);
        }
       //printf("mid indx = %d,mid value=%d
    ",pmid[groups/2],datas[pmid[groups/2]]);
        mid = pmid[groups/2];
        pivot = partition(datas,0,length-1,mid);
        //printf("pivot=%d,value=%d
    ",pivot,datas[pivot]);
        k = pivot+1;
        if(k == i)
            return datas[pivot];
        else if(k < i)
            return select(datas+k,length-k,i-k);
        else
            return select(datas,pivot,i);
    
    }
    
    void swap(int* a,int *b)
    {
        int temp = *a;
        *a = *b;
        *b = temp;
    }
    View Code

     

  • 相关阅读:
    (OK) VirtualBox 5.0.10 中 Fedora 23 在安装了增强工具后无法自动调节虚拟机分辨率的问题
    (OK) install-fedora23—gnome classic—dnf—gdm—multi-user—graphical
    (OK) fedora23——add “opening terminal” in the menu of right click
    (OK) Install Docker on Fedora 23
    (OK) 运行cBPM in Fedora23
    错误:‘bOS::SQL::SQLCommand::SQLOperation’既不是类也不是命名空间
    My enum is not a class or namespace
    【t049】&&【u001】足球
    【u002】数列排序(seqsort)
    【u003】计算概率
  • 原文地址:https://www.cnblogs.com/NeilZhang/p/5650226.html
Copyright © 2011-2022 走看看