排序算法的时间复杂度以及空间复杂度 计数排序

来源:互联网 发布:淘宝怎样设置运费模板 编辑:程序博客网 时间:2024/05/14 07:21

时间复杂度为o(n)的排序算法:不是基于比较的排序算法;思想来自于桶排序!比如:计数排序和基数排序。其中基数排序中是分别基于个位、十位以及等等更高的位将数据放入桶中,然后再将数据倒出来!


八个经典排序算法的时间复杂度:

O(n2)插入排序 选择排序 冒泡排序

O(n*logn)堆排序 希尔排序  快速排序 归并排序

O(n)不基于比较的排序算法:计数排序  基数排序

八个经典排序算法的空间复杂度:

O(1)插入排序 选择排序 冒泡排序  堆排序 希尔排序

O(logN)~O(n) 快速排序(取决于划分情况)

O(N) 归并排序(通过手摇算法优化之后空间复杂度可以达到O(1),但同时时间复杂度会上升)

O(M)计数排序  基数排序 (M是桶的数量)


排序算法的稳定性:

稳定性:假定待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,称这种排序算法是稳定的,否则称为不稳定的。

稳定的排序算法:

冒泡排序 插入排序 归并排序 计数排序 基数排序 桶排序

不稳定的排序算法:

选择排序 快速排序 希尔排序 堆排序



工程上的排序:

工程上的排序是综合排序;

数组极小时,插入排序(常量系数比较小

数组较大时,快速排序或其他o(n*logn)的排序


计数排序是一个非基于比较的排序算法,该算法于1954年由 Harold H. Seward 提出。它的优势在于在对一定范围内的整数排序时,它的复杂度为Ο(n+k)(其中k是整数的范围),快于任何比较排序算法。[1-2]  当然这是一种牺牲空间换取时间的做法,而且当O(k)>O(n*log(n))的时候其效率反而不如基于比较的排序(基于比较的排序的时间复杂度在理论上的下限是O(n*log(n)), 如归并排序,堆排序)。计数排序算法之所以能取得线性计算时间的上界是因为对元素的取值范围作了一定的限制。必须已知要排序的数据的取值范围。

计数排序的算法如下:

class CountingSort {
public:
    int* countingSort(int* A, int n) {
        // write code here
         int max=A[0];
        for(int i=0;i<n;i++){
           if(A[i]>max)
               max=A[i];
        }
        int* B=new int[max+1];
        //int B[max+1];
        for(int k=0;k<max+1;k++)
        {
            B[k]=-1;
        }//代表max个桶
        int index;//代表第index个桶,第index个桶里面存放的是index+1
        for(int j=0;j<n;j++){
            index=A[j];
            B[index]=B[index]+1;
        }
        int count=n-1;//倒着输出才能保证算法是稳定的,也就是说相同值的数据能够按照之间存储的顺序进行输出
        for(int t=max;t>=0;t--){//但是这段程序中有两个循环,所以会导致算法的时间复杂度不是O(n)。
            while(B[t]!=-1){
            A[count]=t;
            count--;
            B[t]=B[t]-1;
            }
        }
        delete B;
        return A;
    }
};

对于上面的算法来说,尽管结果运行是正确的,但是时间复杂度并不是o(n),因为最后的一个循环里面嵌套了一个循环,所以导致算法的时间复杂度为o(n2)。之所以会这样是因为B中存放的并不是t所处数组中的位置,而是数组中t值的数量!!!!也就是说上面程序的逻辑有问题!!!正确的程序应该如下:

class CountingSort {
public:
    int* countingSort(int* A, int n) {
        // write code here
         int max=A[0];
        for(int i=0;i<n;i++){
           if(A[i]>max)
               max=A[i];
        }
        int* B=new int[max+1];//实际上有max+1个数据,因为还有一个0,因为数组大小中有变量max,所以需要用动态分配,不能用int B[max+1]来声明
        int* C=new int[n];//C中存放的是排好序的数据
        //int B[max+1];
        for(int k=0;k<max+1;k++)
        {
            B[k]=0;
        }//B代表max+1个桶
        int index;//代表第index个桶,第index个桶里面存放的是值为index的数据的个数
        for(int j=0;j<n;j++){
            index=A[j];
            B[index]=B[index]+1;
        }
        for(int m=1;m<max+1;m++){
            B[m]=B[m]+B[m-1];//这样B中的值代表m所处排好序的序列中的位置
            
        }
        //借助于另一个数组可以降低时间复杂度
        int pos,value;
        for(int p=n-1;p>=0;p--){
            value=A[p];
            pos=B[value]-1;
            C[pos]=value;
            B[value]--;
        }
        for(int r=0;r<n;r++){
            A[r]=C[r];
        }//这里可以用 memcpy(A,C,n*sizeof(int));,但是绝对不可以用 memcpy(A,C,sizeof(A));或者 memcpy(A,C,sizeof(*A));
        delete C;
        delete B;
        return A;
    }
};


上面的程序才是正确的计数排序的程序,符合逻辑!其中数组B中存放的是相应下标的数据所处的排好序的数组中的位置;最后直接扫描数组A,将相应的数据按照数组B中的位置进行排列,放到数组C中就可以了!最后将数组C中的数据赋值给数组A!对于上面的程序,在倒数第二个循环中也可以从头开始向后将数据进行排序放到数组C中,但是这样就破坏了排序算法的稳定性,这样会导致值相同的数据中最后放到桶B中的数据先放到前面的位置!!所以必须采用倒序的方式输出!


对于上面的程序来说,在最后一个循环中,可以用memcpy(A,C,n*sizeof(int));但是绝对不可以用另外两个!!!!!!

原因如下:

sizeof是一个关键字,一个操作符,不是一个函数,它的计算结果在编译时就已经确定了的,不是在运行时,如果你要在运行时分配一个空间,显然大小在运行时应该也是可知的。
你不能要求sizeof在编译时知道运行时的东西,这是不可能的。而A只是一个指针变量,它的大小用sizeof计算即sizeof(A)最后结果只是4.只是一个地址所占的空间的大小。A所指向的空间的大小n只有在运行时通过动态分配才能知道;
sizeof(*p);如果指针指向的是单个变量还可以这样用但是如果指向的是一个数组就不行了,只能得到数组第一个元素的长度。

但是如果A是一个正常的数组也就是A[100]之类的,这样是可以通过sizeof(A)来计算整个数组的长度的!!!!



c++中变量做数组长度:

在c++中时不支持变量作为数组长度参数的,如 int n=10;byte bs[n];   这样写会提示编译错误”表达式必须含有常量值“。

虽然用变量声明数组大小会报编译错误,但是可以通过指针来动态申请空间实现动数组长度的变量赋值,写法如下:

1 int length = 10;2 int * varArray;3 varArray = new int[length];

这样varArray就可以当做数组来用了,这个数组的长度可以在程序运行时由计算得来。如果是普通的数组如int is[10] 编译时必须能确定数组长度,不然会报编译错误,这样灵活性受限比较大。我想这个就是new的存在原因之一吧,在栈中分配的内存,大小都是编译时就确定好的,如果想在运行时来动态计算使用内存的大小的话,就要用new这样的动态分配函数,来达到更高的灵活性。

可以自己声明一个结构体,来代表这个指针实现的数组,这样可读性会高点,用起来也方便点。

注意:c++ 用new分配空间以后,不用的时候要记得delete释放内存,不然会有内存泄露问题。


阅读全文
0 0
原创粉丝点击