计数排序和基数排序是属于线性级时间复杂度的排序方式,虽然没有冒泡,选择,快排算法那些让人广为所知,但是这两种排序方式在某些场合非常适用。计数排序是基数排序的基础,最为关键的是:基数排序算法是后缀数组的关键(当然后缀数组也可以用快排),后缀数组是用于处理字符串的一种非常优秀的数据结构,可以高效的处理很多字符串问题,比如重复子串,回文子串等等,这里不讨论后缀数组的问题。
1、计数排序算法
计数排序算法的前提是:假设有n个输入元素,且这些元素都是在0~k区间内的一个整数。其算法的思想就是 对于每一个输入的元素x,确定小于x的元素个数。利用这个信息,就可以直接把x输出到输出数组中的制定位置。例如,如果有10个元素小于x,那么x就应该在第11的位置输出。当然如果有多个元素相同时,输出方案要做修改,因为不可能将这些元素输出到相同的位置。 利用这一思想,写出计数排序算法的c++的实现:
/*计数排序算法*/ #include <iostream> using namespace std; const int MAXN = 1000; const int k = 1000; int a[MAXN], c[MAXN], b[MAXN]; /*a数组是待排序的数组,b数组是最终排好序的数组 c数组是用于统计每个元素大小关系的数组,k表示待排序 数组的上界,就是a数组中不能有大于k的元素*/ int main() { int n = 10; int a[10] = {201,562,39,96,269,336,987,369,825,456}; //将c数组清零 for(int i = 0; i < k;i++) c[i] = 0; /*统计a中所有元素出现的次数.如果输入的元素是i,则c[i]的值就会 加1.值得注意的是在这里c数组的下标代表了输入的元素,而c数组的 值代表了元素的出现次数*/ for (int i = 0; i < n;i++) c[a[i]]++; /*这个循环的作用是通过加总计算确定对于每一个i=0,1.....k有多少个元素是小于或者等于i的*/ for (int i = 1; i < k; ++i) c[i] += c[i-1]; /*如果所有元素都不相同,那么对于每一个a[i]来说,c[a[i]]肯定就是a[i]输出的正确位置了,如果a中 有相同的元素,那么我们将a[i]放入数组b后,c[a[i]]必须减一才能保证相同元素不会重叠*/ for (int i = n-1; i >= 0; --i) b[--c[a[i]]] = a[i]; /*输出拍好序的数组*/ for (int i = 0; i < n; ++i) cout << b[i] << " "; return 0;
2、基数排序算法
当输入的序列元素取值特别大的时候,计数排序算法在效率和可行性上面将会崩溃,而基数排序则可以很好的解决这内大数值数据的问题。基数排序是基于对数的每一位进行比较。那么是该从数据的高位开始比较还是低位开始比较?答案是从低位开始比较,这是因为越是排在后面位置的数位,对整个数的影响就越大,而最高有效位显然比低位的数据对整个数的影响大。例如对123,312,245,531,进行基数排序,首先对各位数排序,结果是:531,312,123,245,第二次对十位数排序,结果是:312,123,531,245,最后一次对百位数排序,结果是:123,245,312,531.可以看到结果是对的。按照这样的思想,写出c++代码的实现:
/*基数排序*/ #include <iostream> using namespace std; const int MAXN = 1000; const int k = 1000; int a[MAXN], c[MAXN], b[MAXN]; int main(){ int n = 10,t=1; int a[10] = {201,562,39,96,269,336,987,369,825,556}; /*a中数据最多有3位,所以我们分为三段来处理,用变量t来 依次取出数据的各个位*/ for (int i = 0,t = 1;i < 3;i++,t*=10){ /*同样首先清零*/ for (int i = 0;i < 10;i++) c[i] = 0; /*依次取出每一位上的数*/ for (int i = 0;i < n;i++) c[(a[i]/t)%10]++; /*c数组标记大小个数关系,参见计数排序*/ for (int i = 1;i < 10;i++) c[i] = c[i-1] + c[i]; /*输出到指定位置*/ for (int i = n-1;i >= 0;i--) b[--c[(a[i]/t)%10]] = a[i]; for (int j = 0; j < n;j++) a[j] = b[j]; } for (int i = 0; i < n;i++) cout << a[i] << " "; }
3、字符串的排序
关于比较字符串的大小,是按照字典顺序进行比较的,对于字符串a、b首先从字符串的起始位置开始比较,如果a[0] > b[0],则字符串a>b,如果相等,则比较下一位,直到比较出结果。字符串的比较在很多公司的笔试中都会看到,其特点就是按字典序给字符串排序。基于字符串这种比较的特点,用基数排序是比较合适的。
在这里,用c++来实现的话就很方便,因为c++ 有string类,而且重载了>,<,==等运算符。但是其内部实现的话我觉得用基数排序是很好的(虽然我不知道库里面是不是这样写的)。在这里就不用c来写了,因为实现机制和上面的一样,但是值得注意的是,字符串的比较中我们同样的从最低的位开始比较,这里最低的位其实是从字符串的最后一个字符开始比较的。
用c++冒泡排序实现的字符串比较
#include <iostream> #include <string> using namespace std; int main(){ string str[6] = {"aabcd","abcdd","bcdec","acbbd","acbbd","ddd"}; int n = 6; for (int i = 0; i < n-1;i++) for (int j = 0; j < n - i - 1;j++) if (str[j] > str[j+1]){ string temp = str[j]; str[j] = str[j+1]; str[j+1] = temp; } for (int i = 0; i < n;i++) cout <<str[i] << endl; }