线性时间排序算法
继续上一次的排序算法的总结,今天带来的是线性时间排序算法
可以看到,我们之前的交换算法时间复杂度最少也只是O(nlogn),那么有没有O(n)的时间复杂度的算法呢,也是有的,就是线性时间排序算法。常见的线性时间算法有:计数排序,基数排序和桶排序,而基数排序和桶排序十分类似,这里只介绍更为普遍的基数排序。
①计数排序
(1)算法思想:计数排序的算法思想比较简单,即我们把序列中一个数之前有多少个数字统计出来,然后直接把这个数放到相应的位置的算法。这种算法的空间要求比较高,因为需要一个数组来储存从0到序列中最大的数的之前的数的个数,所以空间消耗很大。
(2)时间复杂度:O(n^2)
(3)算法描述(吸取教训,从这篇博文开始代码加备注~):
void countinsort(int a[], int b[], int n, int max){ int i, pos, c[max + 1]; //pos为位置,定义一个长度为序列中最大数加一的数组 memset(c, 0, sizeof(c)); //将数组初始化置为0 for(i = 0; i < n; i ++) c[a[i]] ++; //将一个数出现的次数在其相应的位置表示 for(i = 1; i <= n; i ++) c[i] += c[i - 1]; //算出一个数之前的数的个数 for(i = n - 1; i >= 0; i --){ int temp = a[i]; pos = c[temp]; //得到当前数的前面的数的个数,也就是位置 b[pos - 1] = temp; //因为存入数组,所以需要加1 c[temp] --; //储存完毕后需要减去已储存的那个数 } }
②基数排序
(1)算法思想:基数排序的算法思想是对于序列中的每个数从低位开始互相比较,然后产生新的序列,然后对新的序列的高位互相比较,继续产生新的数列,最后对每一位比较完毕后产生最终的数列。具体实例如图所示
(2)时间复杂度:O(n^2)
(3)算法描述:
int getpos(int n, int pos){ //取出对应位置的数字 int temp = 1; for(int i = 0; i < pos ;i ++) temp *= 10; return (n/temp) % 10; } int radixsort(int a[], int n){ int *b[10]; int i, j; for(i = 0; i < 10; i++){ b[i] = (int *)malloc(sizeof(int) * (n + 1));//申请一个二维数组,它的横坐标为0-9,纵坐标为序列元素个数 b[i][0] = 0; //对每行第一个计数元素初始化0 } for(int pos = 1; pos <= 10; pos ++){ for(i =0; i < n; i ++){ int number = getpos(a[i], pos); //取出对应位置的数字 int index = ++b[number][0]; //对相对应位置数字所在的行的计数元素加1 b[number][index] = a[i]; //对二维数组对应位置数字所在的行顺序加入原序列中的数字 } for(i = 0, j =0; i < 10; i++){ for(int k = 1; k <=b[i][0]; k ++){ //将排好一轮序的数组重新生成。 a[j ++] = b[i][k]; } b[i][0] = 0; //重新初始化计数元素 } } return 0; }
③桶排序
桶排序和基数排序类似,是将数字分成n个桶,然后将每个数字除以n然后得到的数放在相应的桶里,对每个非空的桶进行快排,之后顺序存入新的数组。产生最后排好序的数列。
排序算法就说这么多,其实还有一种堆排序也十分常用,但是苦于数据结构的树形结构还未学习,所以暂时不予介绍,待再啃啃数据结构回头再来总结。