算法-美团2015校招笔试:写一个复杂度为n的排序算法

一组随机排序的字母数字。请编写一个时间复杂度为O(n)的算法,使这些字母从小到大顺序排序。
说明:字母区分大小写,相同的字母,排序护小写排在前面。
例如:R,B,B,b,W,W,B,R,B,w
排序为:b,B,B,B,B,R,R,w,W,W

#include <stdio.h>
#include <string.h>
void getHelper();
int main() {
	char str[100];
	int helper[150]={0};
	int i,j;
	gets(str);
	getHelper(str, helper);//复杂度为O(n)
	for(i = 'a'; i <='z' ; i++) {//复杂度接近为O(1)
		if(helper[i] > 0) {
			for(j= 0;j < helper[i] ; j++){
				printf("%c",i);
			}
			if(helper[i-32] >0) {
				for(j= 0;j < helper[i-32] ; j++){
					printf("%c",i-32);
				}
			}
		}else if(helper[i-32] > 0){
			for(j= 0;j < helper[i-32] ; j++){
					printf("%c",i-32);
				}
		}
	}
	return 0;
}
void getHelper(char *p,int helper[]){
	int i, len;
	len = strlen(p);
	for (i = 0; i < len; i++) {
		helper[p[i]]++; /*将当前字符所对应的ASCII码作为helper数组的下标,同时加1*/
	}
	
	
}

这个是昨天写的,由于时间就比较晚了,没有做任何的解析,今天就做些说明,一方面自己当做笔记,日后可以翻阅,另一方面,帮助一些不太了解这个算法的同学们。

好了,废话不多说,现在开始:

先抛开这个这个题目的其他条件不看,仅仅看“写一个复杂度为O(n)的排序算法”,这个条件一定吓退了不少人,因为就目前来看,速度最快的算法也不过O(nlog(n)),怎么可能有O(n)的算法,所以从这里看,这个题目就不是考基础算法的,需要你动脑筋,根据条件,写一个算法的。

对于解决这个问题,我先告诉大家解决这类问题的方法,稍后再做解释:

如果是对于数字类型的排序,前提是这些数字是有范围(这个范围是很重要的前提,如果没有这个范围,该方法是失效的),可以新建一个数组(helper[]),可以通过变量所给数组(array[]),把所给数组的值作为helper[]的下标,该下标所对应的值为就是遍历到元素的个数;说的比较抽象,实在想不到再简单的语言了,我就用代码解释吧,看下面的代码:

void getHelper(char *array,int helper[]){
    int i,len;
    len = strlen(array);
    for(i = 0 ; i < len; i ++) {/*遍历的是所给的数组,而不是helper数组*/
        helper[array[i]]++; /*将对应的元素的下标+1*/
    }
    /*所以,我们可以把helper数组理解为一个计数器,就是记录所给数组元素的个数*/
}

这个helper数组非常重要,有了它稍后的操作就会变的很快,有点KMP的next数组的意思,但是他们的用法是不一样的。

有了这个helper数组,就可以指导我们去排序了,先贴代码吧:

void sort(int helper[]) {
    int i,j;
    for(i='a' ; i < 'z'; i++) {/*这一步遍历的范围就是我一开始提到的,这个题目的范围,其目的就是遍历helper的存储的数据*/    
        if(helper[i] > 0) {
            for(j = 0 ; j < helper[i] ; j++) {
                printf("%c",i);
            } 
        }
    }
}

通过上述的算法,你就已经可以排序“如果存在一个随机串,从a->z,写一个O(n)算法,对他们进行排序”,对于我们今天要解决的这道题,只是添加了小小的附加条件,就是"区分大小写,并且相同字母小写在前"。直接贴代码:

其实只需修改一下sort()方法就可以了:

void sort(int helper[]){
    for(i = 'a'; i <='z' ; i++) {
	if(helper[i] > 0) {
		for(j= 0;j < helper[i] ; j++){
			printf("%c",i);
		}
		helper[i] = 0;
		if(helper[i-32] >0) {
			for(j= 0;j < helper[i-32] ; j++){
				printf("%c",i-32);
			}
		}
		helper[i-32] = 0
		}else if(helper[i-32] > 0){
		   for(j= 0;j < helper[i-32] ; j++){
				printf("%c",i-32);
		   }
		}
	}
}

下面对上述代码,做些解释:

上述我们所讲的范围是从A->z,你为什么用a->z呢?

首先我们知道,大小写之间的ASCII码值相差32,所以我们知道了小写字母,同时也就知道了大写字母,其中helper[i-32]就是代表的对应的大写字母,所以我们就不需要在遍历A->Z的部分。


好了,其实理解的话这个算法也是很好理解的,非常感激某位博友,非常抱歉我忘记了你的博客站,是你的文章给了我启发。在此对你表示感谢,以后能找的话,我再贴到此处。

转载于:https://my.oschina.net/ccqy66/blog/484949

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值