排序算法

将一个从大到小的数组,用以下排序方法排序成从小到大的,()最快。

正确答案: D   你的答案: C (错误)

插入排序
冒泡排序
快速排序
堆排序
答案:D
A和B的时间复杂度为N^2
C的平均时间复杂度为NlogN,这里是最坏的情况,也是N^2


Un-Infinite
排序方法        平均情况        最好情况        最坏情况        辅助空间        稳定性
冒泡排序         O(n^2)           O(n)              O(n^2)            O(1)                稳定
选择排序         O(n^2)          O(n^2)            O(n^2)            O(1)              不稳定
插入排序         O(n^2)           O(n)              O(n^2)            O(1)                稳定
希尔排序O(n*log(n))~O(n^2) O(n^1.3)       O(n^2)            O(1)              不稳定
堆排序          O(n*log(n))     O(n*log(n))    O(n*log(n))       O(1)              不稳定
归并排序       O(n*log(n))     O(n*log(n))    O(n*log(n))       O(n)                稳定
快速排序       O(n*log(n))     O(n*log(n))      O(n^2)            O(1)              不稳定

冒泡排序经过优化以后,最好时间复杂度可以达到O(n)。设置一个标志位,如果有一趟比较中没有发生任何交换,可提前结束,因此在正序情况下,时间复杂度为O(n)。选择排序在最坏和最好情况下,都必须在剩余的序列中选择最小(大)的数,与已排好序的序列后一个位置元素做交换,依次最好和最坏时间复杂度均为O(n^2)。插入排序是在把已排好序的序列的后一个元素插入到前面已排好序(需要选择合适的位置)的序列中,在正序情况下时间复杂度为O(n)。堆是完全二叉树,因此树的深度一定是log(n)+1,最好和最坏时间复杂度均为O(n*log(n))。归并排序是将大数组分为两个小数组,依次递归,相当于二叉树,深度为log(n)+1,因此最好和最坏时间复杂度都是O(n*log(n))。快速排序在正序或逆序情况下,每次划分只得到比上一次划分少一个记录的子序列,用递归树画出来,是一棵斜树,此时需要n-1次递归,且第i次划分要经过n-i次关键字比较才能找到第i个记录,因此时间复杂度是\sum_{i=1}^{n-1}(n-i)=n(n-1)/2,即O(n^2)。

排序是编程的基础,在程序中会经常使用,好的排序方法可以帮助你提高程序运行的效率,所以学好排序,打好基础,对于程序的优化会手到擒来。无论你的技术多么强,如果没有基础也强不到哪去。

       不多说了,我们直接进入今天的主题。

                                                      

    总有人说排序很难,但是我总觉得很简单,我们只需要了解基本思想就好了,通过简单的例子来加深理解。


1、直接插入排序


(1)简介:直接插入排序,从字面意思可以看出,直接插入数据完成排序。

(2)基本思想:在插入第i个数时,假设前i-1数已经排好序了,只需要将第i个数插入到i-1中,使得这i个数也是顺序的。

(3)例如:

                              


插入排序从小到大排序:首先位置1上的数和位置0上的数进行比较,如果位置1上的数大于位置0上的数,将位置0上的数向后移一位,将1插入到0位置,否则不处理。位置k上的数和之前的数依次进行比较,如果位置K上的数更大,将之前的数向后移位,最后将位置k上的数插入不满足条件点,反之不处理。 

核心代码(函数)

//array[]为待排序数组,n为数组长度
void insertSort(int array[], int n)
{
    int i,j,temp;
    for( i=1;i<n;i++)
    {
        if(array[i]<array[i-1])
        {
            temp=array[i];
            for( j=i;array[j-1]>temp;j--)
            {
                array[j]=array[j-1];
            }
            array[j]=temp;
        }
    }
}

 

2、希尔排序(Shell排序)


(1)简介: 希尔排序又称为缩小增量排序,是对直接插入排序方法的改进。

(2)基本思想:将整个序列分成多个子序列,然后分别进行直接插入排序,直到整个序列中的所有数基本有序时,再对整体进行一次直接插入排序。

(3)例如:

                             

希尔排序是插入排序改良的算法,希尔排序步长从大到小调整,第一次循环后面元素逐个和前面元素按间隔步长进行比较并交换,直至步长为1,步长选择是关键。 

核心程序(函数)

//下面是插入排序
void InsertSort( int array[], int n)
{
    int i,j,temp;
    for( i=0;i<n;i++ )
    {
        if(array[i]<array[i-1])
        {
            temp=array[i];
            for( j=i-1;array[j]>temp;j--)
            {
                array[j+1]=array[j];
            }
            array[j+1]=temp;
        }
    }
}
//在插入排序基础上修改得到希尔排序
void SheelSort( int array[], int n)
{
    int i,j,temp;
    int gap=n; //~~~~~~~~~~~~~~~~~~~~~
    do{
        gap=gap/3+1;  //~~~~~~~~~~~~~~~~~~
        for( i=gap;i<n;i++ )
        {
            if(array[i]<array[i-gap])
            {
                temp=array[i];
                for( j=i-gap;array[j]>temp;j-=gap)
                {
                    array[j+gap]=array[j];
                }
                array[j+gap]=temp;
            }
        }
    }while(gap>1);  //~~~~~~~~~~~~~~~~~~~~~~

}


3、简单选择排序


(1)简介:简单选择排序也叫直接选择排序,其实说白了跟直接插入排序的道理特别简单,效率低。

(2)基本思想:首先在 n个数中选择一个最小的数,并将它从中删除,作为新的一组数的第一个;再在剩下的数中选择最小的数,将它从中删除,作为新的一组数的第二个。。。。如此反复,直到排序完成,最后得到一组从小到大排序的数。

(3)例如:

                          

选择排序从小到大排序:一开始从0~n-1区间上选择一个最小值,将其放在位置0上,然后在1~n-1范围上选取最小值放在位置1上。重复过程直到剩下最后一个元素,数组即为有序。

 核心代码(函数)

//array[]为待排序数组,n为数组长度
void selectSort(int array[], int n)
{
    int i, j ,min ,k;
    for( i=0; i<n-1; i++)
    {
        min=i; //每趟排序最小值先等于第一个数,遍历剩下的数
        for( j=i+1; j<n; j++) //从i下一个数开始检查
        {
            if(array[min]>array[j])
            {
                min=j;
            }
        }
        if(min!=i)
        {
            k=array[min];
            array[min]=array[i];
            array[i]=k;
        }
    }
}

4、堆排序


(1)简介:堆排序是一个相当有用的排序技术,特别适用于对大量的记录进行排序。同时,堆排序也是对简单选择排序的改进。

堆的定义:n个元素的序列{K1,K2,...,Kn}当满足下列关系时,称为堆:Ki≤K2i且Ki≤K2i+1或者Ki≥K2i且Ki≥K2i+1。注意:堆树必须是一颗完全二叉树。

(2)基本思想:利用堆积树这种数据结构所设计的一种排序,可以利用数组的特点快速的定位指定索引的元素。

最大堆:父结点大于或等于儿子结点

                         

最小堆:父结点小于或等于儿子结点

                         

(3)例如:

将序列{20,60,26,30,36,10}调整为递增序列。

1、首先将数据建立完全二叉树,填充规则是按层次遍历将数据一一填入,最后构建最小堆;

                         

2、提取堆顶并调整删除队顶后的元素为新堆;

3、重复第2步,直到堆空;

                        

4、每次提取的堆顶依次排序即为递增序列。

                     

核心代码(函数)


注意!!!数组从1开始,1~n

void heapSort(int array[], int n)
{
    int i;
    for (i=n/2;i>0;i--)
    {
        HeapAdjust(array,i,n);//从下向上,从右向左调整
    }
    for( i=n;i>1;i--)
    {
        swap(array, 1, i);
        HeapAdjust(array, 1, i-1);//从上到下,从左向右调整
    }
}
void HeapAdjust(int array[], int s, int n )
{
    int i,temp;
    temp = array[s];
    for(i=2*s;i<=n;i*=2)
    {
        if(i<n&&array[i]<array[i+1])
        {
            i++;
        }
        if(temp>=array[i])
        {
            break;
        }
        array[s]=array[i];
        s=i;
    }
    array[s]=temp;
}
void swap(int array[], int i, int j)
{
    int temp;

    temp=array[i];
    array[i]=array[j];
    array[j]=temp;
}

5、冒泡排序


(1)简介:冒泡排序,就跟水里的物体一样,小的往上浮,大的往下沉。

(2)基本思想:将数组垂直排列,取出最后一个元素逐个向上交换,得到大数(小数),继续步骤一的操作,直到排序完成。

(3)例如:

                       

冒泡排序从小到大排序:一开始交换的区间为0~N-1,将第1个数和第2个数进行比较,前面大于后面,交换两个数,否则不交换。再比较第2个数和第三个数,前面大于后面,交换两个数否则不交换。依次进行,最大的数会放在数组最后的位置。然后将范围变为0~N-2,数组第二大的数会放在数组倒数第二的位置。依次进行整个交换过程,最后范围只剩一个数时数组即为有序。

核心代码(函数)

//array[]为待排序数组,n为数组长度
void BubbleSort(int array[], int n)
{
    int i, j, k;
    for(i=0; i<n-1; i++)
        for(j=0; j<n-1-i; j++)
        {
            if(array[j]>array[j+1])
            {
                k=array[j];
                array[j]=array[j+1];
                array[j+1]=k;
            }
        }
}

6、快速排序


(1)简介:快速排序是目前内部排序中速度最快的一种排序算法。

(2)基本思想:选取一个数据(通常是数组的第一个数)作为关键数据,然后将所有比它小的数都放在它前面,所有比它大的数都放在它后面,这个过程称为一趟快速排序,再从分开的部分选取基准数,进行分组划分,重复执行,直到完成。

(3)例如:

                        

快速排序从小到大排序:在数组中随机选一个数(默认数组首个元素),数组中小于等于此数的放在左边,大于此数的放在右边,再对数组两边递归调用快速排序,重复这个过程。

 核心代码(函数)

推荐程序(好理解)

//接口调整
void adjust_quicksort(int k[],int n)  
{  
   quicksort(k,0,n-1);  
}  
void quicksort(int a[], int left, int right)  
{  
    int i,j,t,temp;  
    if(left>right)   //(递归过程先写结束条件)
       return;  

    temp=a[left]; //temp中存的就是基准数  
    i=left;  
    j=right;  
    while(i!=j)  
    {  
                   //顺序很重要,要先从右边开始找(最后交换基准时换过去的数要保证比基准小,因为基准                               
                   //选取数组第一个数,在小数堆中) 
                   while(a[j]>=temp && i<j)  
                            j--;  
                   //再找右边的  
                   while(a[i]<=temp && i<j)  
                            i++;  
                   //交换两个数在数组中的位置  
                   if(i<j)  
                   {  
                            t=a[i];  
                            a[i]=a[j];  
                            a[j]=t;  
                   }  
    }  
    //最终将基准数归位 (之前已经temp=a[left]过了,交换只需要再进行两步)
    a[left]=a[i];  
    a[i]=temp;  

    quicksort(left,i-1);//继续处理左边的,这里是一个递归的过程  
    quicksort(i+1,right);//继续处理右边的 ,这里是一个递归的过程  
}  

7、归并排序 


(1)简介:归并排序又称为二路合并操作,使用合并操作完成排序的算法。

(2)基本思想:将两个或两个以上的有序表合并成一个新的有序表,最后将所有的有序表合成一个整体有序表。

(3)例如:

                       


归并排序从小到大排序:首先让数组中的每一个数单独成为长度为1的区间,然后两两一组有序合并,得到长度为2的有序区间,依次进行,直到合成整个区间。 

  • 递归实现
////实现归并,并把数据都放在list1里面 
void merging(int *list1, int list1_size, int *list2,  int list2_size)
{
    int i=0, j=0, k=0, m=0;
    int temp[MAXSIZE];

    while(i < list1_size && j < list2_size)
    {
        if(list1[i]<list2[j])
        {
            temp[k++] = list1[i++];
        }
        else
        {
            temp[k++] = list2[j++];
        }
    }
    while(i<list1_size)
    {
        temp[k++] = list1[i++];
    }
    while(j<list2_size)
    {
        temp[k++] = list2[j++];
    }

    for(m=0; m < (list1_size+list2_size); m++)
    {
        list1[m]=temp[m];
    }
}
//如果有剩下的,那么说明就是它是比前面的数组都大的,直接加入就可以了 
void mergeSort(int array[], int n)
{
    if(n>1)
    {
        int *list1 = array;
        int list1_size = n/2;
        int *list2 = array + n/2;
        int list2_size = n-list1_size;

        mergeSort(list1, list1_size);
        mergeSort(list2, list2_size);

        merging(list1, list1_size, list2, list2_size);
    }
}
//归并排序复杂度分析:一趟归并需要将待排序列中的所有记录  
//扫描一遍,因此耗费时间为O(n),而由完全二叉树的深度可知,  
//整个归并排序需要进行[log2n],因此,总的时间复杂度为  
//O(nlogn),而且这是归并排序算法中平均的时间性能  
//空间复杂度:由于归并过程中需要与原始记录序列同样数量级的  
//存储空间去存放归并结果及递归深度为log2N的栈空间,因此空间  
//复杂度为O(n+logN)  
//也就是说,归并排序是一种比较占内存,但却效率高且稳定的算法 

  • 迭代实现
void MergeSort(int k[],int n)  
{  
    int i,next,left_min,left_max,right_min,right_max;  
    //动态申请一个与原来数组一样大小的空间用来存储
    int *temp = (int *)malloc(n * sizeof(int));  
    //逐级上升,第一次比较2个,第二次比较4个,第三次比较8个。。。  
    for(i=1; i<n; i*=2)  
    {  
        //每次都从0开始,数组的头元素开始  
        for(left_min=0; left_min<n-i; left_min = right_max)  
        {  
            right_min = left_max = left_min + i;  
            right_max = left_max + i;  
            //右边的下标最大值只能为n  
            if(right_max>n)  
            {  
                right_max = n;  
            }  
            //next是用来标志temp数组下标的,由于每次数据都有返回到K,  
            //故每次开始得重新置零  
            next = 0;  
            //如果左边的数据还没达到分割线且右边的数组没到达分割线,开始循环  
            while(left_min<left_max&&right_min<right_max)  
            {  
                if(k[left_min] < k[right_min])  
                {  
                    temp[next++] = k[left_min++];  
                }  
                else  
                {  
                    temp[next++] = k[right_min++];  
                }  
            }  
            //上面循环结束的条件有两个,如果是左边的游标尚未到达,那么需要把  
            //数组接回去,可能会有疑问,那如果右边的没到达呢,其实模拟一下就可以  
            //知道,如果右边没到达,那么说明右边的数据比较大,这时也就不用移动位置了  

            while(left_min < left_max)  
            {  
                //如果left_min小于left_max,说明现在左边的数据比较大  
                //直接把它们接到数组的min之前就行  
                k[--right_min] = k[--left_max];   
            }  
            while(next>0)  
            {  
                //把排好序的那部分数组返回该k  
                k[--right_min] = temp[--next];        
            }  
        }  
    }  
}  
//非递归的方法,避免了递归时深度为log2N的栈空间,
//空间只是用到归并临时申请的跟原来数组一样大小的空间,并且在时间性能上也有一定的提升,
//因此,使用归并排序是,尽量考虑用非递归的方法。


8、基数排序


(1)简介:前面介绍的排序方法都是对元素进行的,基数排序是对元组进行的。

(2)基本思想:从低位到高位依次对待排序的数进行分配和收集,经过d趟分配和收集,就可以得到一个有序序列。

(3)例如:

                     

基数排序是基于数据位数的一种排序算法。 
它有两种算法 
①LSD–Least Significant Digit first 从低位(个位)向高位排。 
②MSD– Most Significant Digit first 从高位向低位(个位)排。 
时间复杂度O(N*最大位数)。 
空间复杂度O(N)。

图解

这里写图片描述 
对a[n]按照个位0~9进行桶排序: 
这里写图片描述 
对b[n]进行累加得到c[n],用于b[n]中重复元素计数 
!!!b[n]中的元素为temp中的位置!!!跳跃的用++补上: 
这里写图片描述 
temp数组为排序后的数组,写回a[n]。temp为按顺序倒出桶中的数据(联合b[n],c[n],a[n]得到),重复元素按顺序输出: 
这里写图片描述

核心程序

//基数排序  
//LSD  先以低位排,再以高位排  
//MSD  先以高位排,再以低位排  
void LSDSort(int *a, int n)  
{  
    assert(a);  //判断a是否为空,也可以a为空||n<2返回
    int digit = 0;   //最大位数初始化
    for (int i = 0; i < n; ++i)  
    {   //求最大位数
        while (a[i] > (pow(10,digit)))  //pow函数要包含头文件math.h,pow(10,digit)=10^digit
        {  
            digit++;  
        }  
    }  
    int flag = 1;   //位数
    for (int j = 1; j <= digit; ++j)  
    {  
        //建立数组统计每个位出现数据次数(Digit[n]为桶排序b[n])  
        int Digit[10] = { 0 };  
        for (int i = 0; i < n; ++i)  
        {  
            Digit[(a[i] / flag)%10]++;  //flag=1时为按个位桶排序
        }  
         //建立数组统计起始下标(BeginIndex[n]为个数累加c[n],用于记录重复元素位置
         //flag=1时,下标代表个位数值,数值代表位置,跳跃代表重复)
        int BeginIndex[10] = { 0 };  
        for (int i = 1; i < 10; ++i)  
        {  
            //累加个数
            BeginIndex[i] = BeginIndex[i - 1] + Digit[i - 1];  
        }  
        //建立辅助空间进行排序 
        //下面两条可以用calloc函数实现
        int *tmp = new int[n];  
        memset(tmp, 0, sizeof(int)*n);//初始化  
        //联合各数组求排序后的位置存在temp中
        for (int i = 0; i < n; ++i)  
        {  
            int index = (a[i] / flag)%10;  //桶排序和位置数组中的下标
            //计算temp相应位置对应a[i]中的元素,++为BeginIndex数组数值加1
            //跳跃间隔用++来补,先用再++
            tmp[BeginIndex[index]++] = a[i];  
        }  
        //将数据重新写回原空间  
        for (int i = 0; i < n; ++i)  
        {  
            a[i] = tmp[i];  
        }  
        flag = flag * 10;  
        delete[] tmp;  
    }  
}  



9 桶排序(基数排序和基数排序的思想)

过程

桶排序是计数排序的变种,把计数排序中相邻的m个”小桶”放到一个”大桶”中,在分完桶后,对每个桶进行排序(一般用快排),然后合并成最后的结果。

图解

核心程序

#include <stdio.h>
int main()
{
    int a[11],i,j,t;
    for(i=0;i<=10;i++)
        a[i]=0;  //初始化为0

    for(i=1;i<=5;i++)  //循环读入5个数
    {
        scanf("%d",&t);  //把每一个数读到变量t中
        a[t]++;  //进行计数(核心行)
    }

    for(i=0;i<=10;i++)  //依次判断a[0]~a[10]
        for(j=1;j<=a[i];j++)  //出现了几次就打印几次
            printf("%d ",i);

    getchar();getchar(); 
    //这里的getchar();用来暂停程序,以便查看程序输出的内容
    //也可以用system("pause");等来代替
    return 0;
}

10 计数排序

过程

算法的步骤如下: 
- 找出待排序的数组中最大和最小的元素 
- 统计数组中每个值为i的元素出现的次数,存入数组C的第i项 
- 对所有的计数累加(从C中的第一个元素开始,每一项和前一项相加) 
- 反向填充目标数组:将每个元素i放在新数组的第C(i)项,每放一个元素就将C(i)减去1

 图解

这里写图片描述

核心程序(函数)

程序1#define NUM_RANGE (100)    //预定义数据范围上限,即K的值

void counting_sort(int *ini_arr, int *sorted_arr, int n)  //所需空间为 2*n+k
{  
       int *count_arr = (int *)malloc(sizeof(int) * NUM_RANGE);  
       int i, j, k;  

       //初始化统计数组元素为值为零 
       for(k=0; k<NUM_RANGE; k++){  
               count_arr[k] = 0;  
       }  
       //统计数组中,每个元素出现的次数    
       for(i=0; i<n; i++){  
               count_arr[ini_arr[i]]++;  
       }  

       //统计数组计数,每项存前N项和,这实质为排序过程
       for(k=1; k<NUM_RANGE; k++){  
               count_arr[k] += count_arr[k-1];  
       }  

       //将计数排序结果转化为数组元素的真实排序结果
       for(j=n-1 ; j>=0; j--){  
           int elem = ini_arr[j];          //取待排序元素
           int index = count_arr[elem]-1;  //待排序元素在有序数组中的序号
           sorted_arr[index] = elem;       //将待排序元素存入结果数组中
           count_arr[elem]--;              //修正排序结果,其实是针对算得元素的修正
       }  
       free(count_arr);  
}  

程序2:C++(最大最小压缩桶数)
public static void countSort(int[] arr) {
        if (arr == null || arr.length < 2) {
            return;
        }
        int min = arr[0];
        int max = arr[0];
        for (int i = 1; i < arr.length; i++) {
            min = Math.min(arr[i], min);
            max = Math.max(arr[i], max);
        }
        int[] countArr = new int[max - min + 1];
        for (int i = 0; i < arr.length; i++) {
            countArr[arr[i] - min]++;
        }
        int index = 0;
        for (int i = 0; i < countArr.length; i++) {
            while (countArr[i]-- > 0) {
                arr[index++] = i + min;
        }
}


排序算法对比

                


 


排序算法比较表格


注:

1 归并排序可以通过手摇算法将空间复杂度降到O(1),但是时间复杂度会提高。

2 基数排序时间复杂度为O(N*M),其中N为数据个数,M为数据位数。

辅助记忆

  • 时间复杂度记忆- 
    • 冒泡、选择、直接 排序需要两个for循环,每次只关注一个元素,平均时间复杂度为On2O(n2)(一遍找元素O(n)O(n),一遍找位置O(n)O(n)
    • 快速、归并、希尔、堆基于二分思想,log以2为底,平均时间复杂度为O(nlogn)O(nlogn)(一遍找元素O(n)O(n),一遍找位置O(logn)O(logn)
  • 稳定性记忆-“快希选堆”(快牺牲稳定性) 
    • 排序算法的稳定性:排序前后相同元素的相对位置不变,则称排序算法是稳定的;否则排序算法是不稳定的。



  •  

  •  


附:

1 完整程序框架(冒泡排序举例)

1.1 VS2010程序
#include "stdafx.h"
#include "stdio.h"
#include <stdlib.h>

void BubbleSort(int array[], int n){
    int i,j,k,count1=0, count2=0;
    for(i=0; i<n-1; i++)
        for(j=n-1; j>i; j--)
        {
            count1++;
            if(array[j-1]>array[j])
            {
                count2++;
                k=array[j-1];
                array[j-1]=array[j];
                array[j]=k;
            }
        }
    printf("总共的循环次序为:%d,  总共的交换次序为:%d\n\n", count1, count2);
}


int main(int argc, _TCHAR* argv[])
{
    int as[]={0,1,2,3,4,6,8,5,9,7};
    BubbleSort(as, 10);
    for(int i=0; i<10; i++)
    {
        printf("%d", as[i]);
    }
    printf("\n\n");
    system("pause");
    return 0;
}

1.2 执行程序(OJ)
#include <stdio.h>

void BubbleSort(int array[], int n){
    int i,j,k,count1=0, count2=0;
    for(i=0; i<n-1; i++)
        for(j=n-1; j>i; j--)
        {
            count1++;
            if(array[j-1]>array[j])
            {
                count2++;
                k=array[j-1];
                array[j-1]=array[j];
                array[j]=k;
            }
        }
    printf("总共的循环次序为:%d,  总共的交换次序为:%d\n\n", count1, count2);
}

int main()
{
    int as[]={0,1,2,3,4,6,8,5,9,7};
    BubbleSort(as, 10);
    int i=0;
    for(i=0; i<10; i++)
    {
        printf("%d", as[i]);
    }
    return 0;
}

2 关于交换的优化

不用中间变量进行交换

if(A[j] <= A[i]){
    A[j] = A[j] + A[i];
    A[i] = A[j] - A[i];
    A[j] = A[j] - A[i];
}

3 C语言实现数组动态输入

#include <stdio.h>  
#include <assert.h>  //断言头文件
#include <stdlib.h>  

int main(int argc, char const *argv[])  
{  
    int size = 0;  
    scanf("%d", &size);   //首先输入数组个数
    assert(size > 0);     //判断数组个数是否非法

    int *array = (int *)calloc(size, sizeof(int));  //动态分配数组
    if(!R1)  
    {  
        return;           //申请空间失败  
    }  

    int i = 0;  
    for (i = 0; i < size; ++i) {  
        scanf("%d", &array[i]);  
    }  

    mergeSort(array, size);  
    printArray(array, size);  

    free(array);  
    return 0;  
} 

注: 
1.colloc与malloc类似,但是主要的区别是存储在已分配的内存空间中的值默认为0,使用malloc时,已分配的内存中可以是任意的值. 
2.colloc需要两个参数,第一个是需要分配内存的变量的个数,第二个是每个变量的大小

8 桶排序(基数排序和基数排序的思想)

8.1 过程

桶排序是计数排序的变种,把计数排序中相邻的m个”小桶”放到一个”大桶”中,在分完桶后,对每个桶进行排序(一般用快排),然后合并成最后的结果。

8.2 图解

8.3 核心程序

#include <stdio.h>
int main()
{
    int a[11],i,j,t;
    for(i=0;i<=10;i++)
        a[i]=0;  //初始化为0

    for(i=1;i<=5;i++)  //循环读入5个数
    {
        scanf("%d",&t);  //把每一个数读到变量t中
        a[t]++;  //进行计数(核心行)
    }

    for(i=0;i<=10;i++)  //依次判断a[0]~a[10]
        for(j=1;j<=a[i];j++)  //出现了几次就打印几次
            printf("%d ",i);

    getchar();getchar(); 
    //这里的getchar();用来暂停程序,以便查看程序输出的内容
    //也可以用system("pause");等来代替
    return 0;
}

9 计数排序

9.1 过程

算法的步骤如下: 
- 找出待排序的数组中最大和最小的元素 
- 统计数组中每个值为i的元素出现的次数,存入数组C的第i项 
- 对所有的计数累加(从C中的第一个元素开始,每一项和前一项相加) 
- 反向填充目标数组:将每个元素i放在新数组的第C(i)项,每放一个元素就将C(i)减去1

9.2 图解

这里写图片描述

9.3 核心程序(函数)

程序1#define NUM_RANGE (100)    //预定义数据范围上限,即K的值

void counting_sort(int *ini_arr, int *sorted_arr, int n)  //所需空间为 2*n+k
{  
       int *count_arr = (int *)malloc(sizeof(int) * NUM_RANGE);  
       int i, j, k;  

       //初始化统计数组元素为值为零 
       for(k=0; k<NUM_RANGE; k++){  
               count_arr[k] = 0;  
       }  
       //统计数组中,每个元素出现的次数    
       for(i=0; i<n; i++){  
               count_arr[ini_arr[i]]++;  
       }  

       //统计数组计数,每项存前N项和,这实质为排序过程
       for(k=1; k<NUM_RANGE; k++){  
               count_arr[k] += count_arr[k-1];  
       }  

       //将计数排序结果转化为数组元素的真实排序结果
       for(j=n-1 ; j>=0; j--){  
           int elem = ini_arr[j];          //取待排序元素
           int index = count_arr[elem]-1;  //待排序元素在有序数组中的序号
           sorted_arr[index] = elem;       //将待排序元素存入结果数组中
           count_arr[elem]--;              //修正排序结果,其实是针对算得元素的修正
       }  
       free(count_arr);  
}  

程序2:C++(最大最小压缩桶数)
public static void countSort(int[] arr) {
        if (arr == null || arr.length < 2) {
            return;
        }
        int min = arr[0];
        int max = arr[0];
        for (int i = 1; i < arr.length; i++) {
            min = Math.min(arr[i], min);
            max = Math.max(arr[i], max);
        }
        int[] countArr = new int[max - min + 1];
        for (int i = 0; i < arr.length; i++) {
            countArr[arr[i] - min]++;
        }
        int index = 0;
        for (int i = 0; i < countArr.length; i++) {
            while (countArr[i]-- > 0) {
                arr[index++] = i + min;
        }
}
posted @ 2018-06-30 14:08  lightmare  阅读(215)  评论(0编辑  收藏  举报