计数排序和基数排序

计数排序基数排序是属于线性级时间复杂度的排序方式,虽然没有冒泡,选择,快排算法那些让人广为所知,但是这两种排序方式在某些场合非常适用。计数排序是基数排序的基础,最为关键的是:基数排序算法是后缀数组的关键(当然后缀数组也可以用快排),后缀数组是用于处理字符串的一种非常优秀的数据结构,可以高效的处理很多字符串问题,比如重复子串,回文子串等等,这里不讨论后缀数组的问题。

1、计数排序算法

计数排序算法的前提是:假设有n个输入元素,且这些元素都是在0~k区间内的一个整数。其算法的思想就是 对于每一个输入的元素x,确定小于x的元素个数。利用这个信息,就可以直接把x输出到输出数组中的制定位置。例如,如果有10个元素小于x,那么x就应该在第11的位置输出。当然如果有多个元素相同时,输出方案要做修改,因为不可能将这些元素输出到相同的位置。 利用这一思想,写出计数排序算法的c++的实现:  

/*计数排序算法*/
#include 
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 
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 
#include 
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 <




你可能感兴趣的:(算法设计和数据结构)