码迷,mamicode.com
首页 > 编程语言 > 详细

排序算法

时间:2015-03-08 15:31:23      阅读:197      评论:0      收藏:0      [点我收藏+]

标签:

本文对部分算法有参考其他博客,在这里注明,此为初稿,以后会不断修改!

/*****************************************************************/
/* 冒泡排序时间复杂度最好的情况为O(n),最坏的情况是O(n^2)
* 基本思想是:两两比较相邻记录的关键字,如果反序则交换 */

void BubbleSort1(int arr[], int num)
{
    int i, j;
    for (i = 0; i < num; i++)
    {
        for (j = 1; j < num - i; j++)
        {
            if (arr[j - 1] > arr[j])
                swap1(&arr[j - 1], &arr[j]);
        }
    }
}

// 改进思路:设置标志位,明显如果有一趟没有发生交换(flag = flase),说明排序已经完成.
void BubbleSort2(int arr[], int num)
{
    int k = num;
    int j;
    bool flag = true;
    while (flag)
    {
        flag = false;
        for (j = 1; j < k; j++)
        {
            if (arr[j - 1] > arr[j])
            {
                swap1(&arr[j - 1], &arr[j]);
                flag = true;
            }
        }
        k--;
    }
}
//改进思路:记录一轮下来标记的最后位置,下次从头部遍历到这个位置就Ok
void BubbleSort3(int arr[], int num)
{
    int k, j;
    int flag = num;
    while (flag > 0)
    {
        k = flag;
        flag = 0;
        for (j = 1; j < k; j++)
        {
            if (arr[j - 1] > arr[j])
            {
                swap1(&arr[j - 1], &arr[j]);
                flag = j;
            }
        }
    }
}
/*************************************************************************/

/**************************************************************************/
/*插入排序: 将一个记录插入到已经排好序的有序表中, 从而得到一个新的,记录数增1的有序表
* 时间复杂度也为O(n^2), 比冒泡法和选择排序的性能要更好一些 */

void InsertionSort(int arr[], int num)
{
    int temp;
    int i, j;
    for (i = 1; i < num; i++)
    {
        temp = arr[i];
        for (j = i; j > 0 && arr[j - 1] > temp; j--)
            arr[j] = arr[j - 1];
        arr[j] = temp;
    }
}

 

 

/**************************************************************************/

/* 简单选择排序(simple selection sort) 就是通过n-i次关键字之间的比较,从n-i+1
* 个记录中选择关键字最小的记录,并和第i(1<=i<=n)个记录交换之
* 尽管与冒泡排序同为O(n^2),但简单选择排序的性能要略优于冒泡排序 */   不稳定

void SelectSort(int arr[], int num)
{
    int i, j, Mindex;
    for (i = 0; i < num; i++)
    {
        Mindex = i;
        for (j = i + 1; j < num; j++)
        {
            if (arr[j] < arr[Mindex])
                Mindex = j;
        }

        swap1(&arr[i], &arr[Mindex]);
    }
}

 

 

希尔排序:

void shellsort1(int a[], int n)

{

    int i, j, gap;

 

    for (gap = n / 2; gap > 0; gap /= 2) //步长

       for (i = 0; i < gap; i++)        //直接插入排序

       {

           for (j = i + gap; j < n; j += gap) //依次插入每个组内的数,它们的位置为i,i+gap,i+2gap```

              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 maxHeap(int *a,int n,int i)  //堆调整

    //left、right、largest分别指向 

    //左孩子、右孩子、{a[i],a[left]}中最大的一个 

    int left,right,largest; 

    largest=left=2*i; 

    if(left>n) 

        return; 

    right=2*i+1; 

    if(right<=n && a[right]>a[left]){ 

        largest=right; 

    } 

    if(a[i]<a[largest]){//根结点的值不是最大时,交换a[i],a[largest] 

        a[i]=a[i]+a[largest]; 

        a[largest]=a[i]-a[largest]; 

        a[i]=a[i]-a[largest]; 

        //自上而下调整堆 

        maxHeap(a,n,largest); 

    } 

}

 

void creatHeap(int *a,int n)  //建堆

    int i; 

    //自下而上调整堆 

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

        maxHeap(a,n,i); 

}

 

void heapSort(int *a,int n) 

    int i; 

    creatHeap(a,n);//建堆 

    for(i=n;i>=2;i--){ 

        //堆顶记录和最后一个记录交换 

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

        a[i]=a[1]-a[i]; 

        a[1]=a[1]-a[i]; 

        //堆中记录个数减少一个,筛选法调整堆 

        maxHeap(a,i-1,1); 

    } 

}

 

 

归并排序:

public void Merger(int[] v, int first, int mid, int last)

       {

           Queue<int> tempV = new Queue<int>();

           int indexA, indexB;

           //设置indexA,并扫描subArray1 [first,mid]

           //设置indexB,并扫描subArray2 [mid,last]

           indexA = first;

           indexB = mid;

           //在没有比较完两个子标的情况下,比较 v[indexA]和v[indexB]

           //将其中小的放到临时变量tempV中

           while (indexA < mid && indexB < last)

           {

               if (v[indexA] < v[indexB])

               {

                   tempV.Enqueue(v[indexA]);

                   indexA++;

               }

               else

               {

                   tempV.Enqueue(v[indexB]);

                   indexB++;

               }

           }

           //复制没有比较完子表中的元素

           while (indexA < mid)

           {

               tempV.Enqueue(v[indexA]);

               indexA++;

           }

           while (indexB < last)

           {

               tempV.Enqueue(v[indexB]);

               indexB++;

           }

           int index = 0;

           while (tempV.Count > 0)

           {

               v[first+index] = tempV.Dequeue();

               index++;

           }

       }

 

 

public void MergerSort(int[] v, int first, int last)

       {

           if (first + 1 < last)

           {

               int mid = (first + last) / 2;

               MergerSort(v, first, mid);

               MergerSort(v, mid, last);

               Merger(v, first, mid, last);

           }

       }

 

快速:

单次划分时设头元素为p,将p放到最终的正确位置,设置划分边界并扫描所有元素将划分边界放到正确的位置

递归排序左右两边

def partition(start,end,s):

    if end-start<1:

        return

    edge=start+1

    for p in range(start+1, end+1):

        if s[p]<=s[start]:

            tmp=s[p]

            s[p]=s[edge]

            s[edge]=tmp

            edge+=1

    tmp=s[edge-1]

    s[edge-1]=s[start]

    s[start]=tmp

return edge-1

 

def quickSort2(start,end ,s):

    if end-start<1:

        return

    k=partition(start,end ,s)

    quickSort2(start,k-1,s)

    quickSort2(k+1,end,s)

 

 

def quickSort(s):

    length=len(s)

    if length<=1:

        return s

    quickSort2(0,length-1,s)

    return s 

   

 

 

 

 

计数:时间复杂度:O(n+k),空间复杂度:n+O(k)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

//针对c数组的大小,优化过的计数排序

publicclassCountSort{

    publicstaticvoidmain(String[]args){

      //排序的数组

        int a[]={100,93,97,92,96,99,92,89,93,97,90,94,92,95};

        int b[]=countSort(a);

        for(inti:b){

           System.out.print(i+"");

        }

        System.out.println();

    }

    public static int[] countSort(int[]a){

        int b[] = new int[a.length];

        int max = a[0],min = a[0];

        for(int i:a){

            if(i>max){

                max=i;

            }

            if(i<min){

                min=i;

            }

        }//这里k为待排序数组中数值的范围

        int k=max-min+1;

        int c[]=new int[k];

        for(int i=0;i<a.length;++i){

            c[a[i]-min]+=1;//c为待排序数组相对值的统计

        }

        for(int i=1;i<c.length;++i){

            c[i]=c[i]+c[i-1];//将c[i]由相对值个数转变为比该相对值小于等于的个数

        }

        for(int i=a.length-1;i>=0;--i){//保证稳定性

            b[--c[a[i]-min]]=a[i];//根据小于等于a[i]的数值个数安排a[i]的位置

        }

    return b;

    }

}

 

 

基数排序:

/******************** 基数排序LSD*********************/

void base_sort_ISD(int *arr, int num)
{
    Node *buck[10]; // 创建一个链表数组
    Node *tail[10]; //保存每条链表尾节点指针集合,
    //这样插入buck数组时就不用每次遍历到末尾
    int  i, MaxValue, kth, high, low;
    Node *ptr;
    for(MaxValue = arr[0], i = 1; i < num; i++)
        MaxValue = max(MaxValue, arr[i]);

    memset(buck, 0, sizeof(buck));
    memset(tail, 0, sizeof(buck));

    for(low = 1; high = low * 10, low < MaxValue; low *= 10)
    {
        //只要没排好序就一直排序
        for(i = 0; i < num; i++)
        {
            //往桶里放
            kth = (arr[i] % high) / low;//取出数据的某一位,作为桶的索引
            ptr = new Node(arr[i]); //创建新节点

            //接到末尾
            if (buck[kth] != NULL)
            {
                tail[kth]->next_ = ptr;
                tail[kth] = ptr;
            }
            else
            {
                buck[kth] = ptr;
                tail[kth] = ptr;
            }

        }
        //把桶中的数据放回数组中(同条链表是从头到尾)
        for (kth = 0, i = 0; kth < num; i++)
        {
            while (buck[i] != NULL)
            {
                arr[kth++] = buck[i]->key_;
                ptr = buck[i];
                buck[i] = buck[i]->next_;
                delete ptr;
            }
        }

        memset(tail, 0, sizeof(buck));

    }

}


桶排序的基本思想

       假设有一组长度为N的待排关键字序列K[1....n]。首先将这个序列划分成M个的子区间(桶) 。然后基于某种映射函数 ,将待排序列的关键字k映射到第i个桶中(即桶数组B的下标 i) ,那么该关键字k就作为B[i]中的元素(每个桶B[i]都是一组大小为N/M的序列)。接着对每个桶B[i]中的所有元素进行比较排序(可以使用快排)。然后依次枚举输出B[0]....B[M]中的全部内容即是一个有序序列。

 

[关键字]映射函数

      bindex=f(key)   其中,bindex 为桶数组B的下标(即第bindex个桶), k为待排序列的关键字。桶排序之所以能够高效,其关键在于这个映射函数,它必须做到:如果关键字k1<k2,那么f(k1)<=f(k2)。也就是说B(i)中的最小数据都要大于B(i-1)中最大数据。很显然,映射函数的确定与数据本身的特点有很大的关系,我们下面举个例子:

 

假如待排序列K= {49、 38 、 35、 97 、 76、 73 、 27、 49 }。这些数据全部在1—100之间。因此我们定制10个桶,然后确定映射函数f(k)=k/10。则第一个关键字49将定位到第4个桶中(49/10=4)。依次将所有关键字全部堆入桶中,并在每个非空的桶中进行快速排序后得到如下图所示:

                                                       

对上图只要顺序输出每个B[i]中的数据就可以得到有序序列了。

 

桶排序代价分析

桶排序利用函数的映射关系,减少了几乎所有的比较工作。实际上,桶排序的f(k)值的计算,其作用就相当于快排中划分,已经把大量数据分割成了基本有序的数据块(桶)。然后只需要对桶中的少量数据做先进的比较排序即可。

 

对N个关键字进行桶排序的时间复杂度分为两个部分:

(1) 循环计算每个关键字的桶映射函数,这个时间复杂度是O(N)。

(2) 利用先进的比较排序算法对每个桶内的所有数据进行排序,其时间复杂度为  ∑ O(Ni*logNi) 。其中Ni 为第i个桶的数据量。

 

很显然,第(2)部分是桶排序性能好坏的决定因素。尽量减少桶内数据的数量是提高效率的唯一办法(因为基于比较排序的最好平均时间复杂度只能达到O(N*logN)了)。因此,我们需要尽量做到下面两点:

(1) 映射函数f(k)能够将N个数据平均的分配到M个桶中,这样每个桶就有[N/M]个数据量。

(2) 尽量的增大桶的数量。极限情况下每个桶只能得到一个数据,这样就完全避开了桶内数据的“比较”排序操作。当然,做到这一点很不容易,数据量巨大的情况下,f(k)函数会使得桶集合的数量巨大,空间浪费严重。这就是一个时间代价和空间代价的权衡问题了。

 

对于N个待排数据,M个桶,平均每个桶[N/M]个数据的桶排序平均时间复杂度为:

             O(N)+O(M*(N/M)*log(N/M))=O(N+N*(logN-logM))=O(N+N*logN-N*logM)

当N=M时,即极限情况下每个桶只有一个数据时。桶排序的最好效率能够达到O(N)。

 

总结: 桶排序的平均时间复杂度为线性的O(N+C),其中C=N*(logN-logM)。如果相对于同样的N,桶数量M越大,其效率越高,最好的时间复杂度达到O(N)。 当然桶排序的空间复杂度 为O(N+M),如果输入数据非常庞大,而桶的数量也非常多,则空间代价无疑是昂贵的。此外,桶排序是稳定的。

 

其实我个人还有一个感受:在查找算法中,基于比较的查找算法最好的时间复杂度也是O(logN)。比如折半查找、平衡二叉树、红黑树等。但是Hash表却有O(C)线性级别的查找效率(不冲突情况下查找效率达到O(1))。大家好好体会一下:Hash表的思想和桶排序是不是有一曲同工之妙呢?

 

 

源代码

Cpp代码  

  1. #include<iostream.h>  
  2. #include<malloc.h>  
  3.   
  4. typedef struct node{  
  5.     int key;  
  6.     struct node * next;  
  7. }KeyNode;  
  8.   
  9. void inc_sort(int keys[],int size,int bucket_size){  
  10. 10.     KeyNode **bucket_table=(KeyNode **)malloc(bucket_size*sizeof(KeyNode *));  
  11. 11.     for(int i=0;i<bucket_size;i++){  
  12. 12.         bucket_table[i]=(KeyNode *)malloc(sizeof(KeyNode));  
  13. 13.         bucket_table[i]->key=0; //记录当前桶中的数据量  
  14. 14.         bucket_table[i]->next=NULL;  
  15. 15.     }  
  16. 16.     for(int j=0;j<size;j++){  
  17. 17.         KeyNode *node=(KeyNode *)malloc(sizeof(KeyNode));  
  18. 18.         node->key=keys[j];  
  19. 19.         node->next=NULL;  
  20. 20.         //映射函数计算桶号  
  21. 21.         int index=keys[j]/10;  
  22. 22.         //初始化P成为桶中数据链表的头指针  
  23. 23.         KeyNode *p=bucket_table[index];  
  24. 24.         //该桶中还没有数据  
  25. 25.         if(p->key==0){  
  26. 26.             bucket_table[index]->next=node;  
  27. 27.             (bucket_table[index]->key)++;  
  28. 28.         }else{  
  29. 29.             //链表结构的插入排序  
  30. 30.             while(p->next!=NULL&&p->next->key<=node->key)  
  31. 31.                 p=p->next;     
  32. 32.             node->next=p->next;  
  33. 33.             p->next=node;  
  34. 34.             (bucket_table[index]->key)++;  
  35. 35.         }  
  36. 36.     }  
  37. 37.     //打印结果  
  38. 38.     for(int b=0;b<bucket_size;b++)  
  39. 39.         for(KeyNode *k=bucket_table[b]->next; k!=NULL; k=k->next)  
  40. 40.             cout<<k->key<<" ";  
  41. 41.     cout<<endl;  

42. }  

  1. 43.   

44. void main(){  

  1. 45.     int raw[]={49,38,65,97,76,13,27,49};     
  2. 46.     int size=sizeof(raw)/sizeof(int);     
  3. 47.     inc_sort(raw,size,10);  

48. }  

 

 上面源代码的桶内数据排序,我们使用了基于单链表的直接插入排序算法。可以使用基于双向链表的快排算法提高效率。

 

排序算法

标签:

原文地址:http://www.cnblogs.com/littlebugfish/p/4321697.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!