一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第16天,点击查看活动详情。
虽然写的都是很基础的东西,但我在尽自己最大的努力去做,去记录。到现在是第16天了,好几次想过今天少写一次吧,少写一次吧,但转念一想,今天放弃了,少写一次,明天我是不是又会用相同的理由少写一次?我被困住了,真的被困住了吗?不,我相信这只是黎明前最后的黑暗。
用js实现英文字母大小写的切换,也就是将字符串中的大写字母变成小写,小写字母变成大小。
示例:'123aBc' => '123AbC'
分析题目
最开始看到题目的时候我竟然想过要for循环并且一次判断字符串中每个字符是a-z中哪一个,或者是A-Z中的哪一个,但是转年一想,如果这样写,那要写多少个if判断呀?
再仔细想想自己的思路,看到a-z和A-Z就想到了思路一,利用正则表达式。但是正则似乎时间复杂度不好判定,而且比较消耗性能,那是不是有其他的方案呢?
既然正则可以,那么利用数组是不是也可以呢?分别建立两个数组存储所有的小写和大写英文字母,判断数组中是否存在该字符。这应该也是一种思路,但相对而言数组的操作成本较高,是否存在其他方案呢?
这个时候突然想起enter回车键在ASCII中是有特定的编码的,那么字母肯定也会存在,利用charCodeAt() 是不是也可以呢?
通过上方的图片可以看到A-Z对应的ASCII编码中的十进制的数字为65-90,a-z对应的ASCII编码中的十进制的数字为97-122.
下面对三种思路进行验证。
思路一:利用正则
//利用正则
function switchLLetterCase1(s: string): string{
let res = '';
const reg1 = /[a-z]/;
const reg2 = /[A-Z]/;
for(let i = 0; i < s.length; i++){
const newS = s[i];
if(reg1.test(newS)){
res += newS.toUpperCase()
}else if(reg2.test(newS)){
res += newS.toLowerCase()
}else{
res += newS
}
}
return res;
}
//功能测试
const testStr = '123AbCdEf'
const newStr1 = switchLLetterCase1(testStr);
console.log(newStr1)//123aBcDeF
思路二: 利用数组
//利用数组
function switchLLetterCase2(s: string): string{
let res = '';
let lowerArr = ['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'];
let upperArr = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'];
for(let i = 0; i < s.length; i++){
const newS = s[i];
if(lowerArr.indexOf(newS) > -1 ){
res += newS.toUpperCase()
}else if(upperArr.indexOf(newS) > -1){
res += newS.toLowerCase()
}else{
res += newS
}
}
return res;
}
//功能测试
const newStr2 = switchLLetterCase2(testStr);
console.log(newStr2)//123aBcDeF
思路三: 利用ASCII
//利用ASCII
function switchLLetterCase3(s: string): string{
let res = '';
for(let i = 0; i < s.length; i++){
const newS = s[i];
if(newS.charCodeAt(0) > 96 && newS.charCodeAt(0) < 123){
res += newS.toUpperCase()
}else if(newS.charCodeAt(0) > 64 && newS.charCodeAt(0) < 91){
res += newS.toLowerCase()
}else{
res += newS
}
}
return res;
}
//功能测试
const newStr3 = switchLLetterCase3(testStr);
console.log(newStr3)//123aBcDeF
通过上面的代码,我们可以看出,三种方案都能够实现题目中的要求,那么这三种方案究竟谁的性能会更好呢?
性能分析
三种思路都用到了for循环,其时间复杂度都不会低于O(n),思路一是用了正则,而且是极简单的正则,查找每个字符是否符合正则;思路二利用了数组,使用了indexOf方法,也是查找该数组中是否存在该字符;思路一和思路二的时间复杂度应该是差不多的;思路三是用了charCodeAt方法,猜测该方法的时间复杂度是较低的。让我们实际测试一下;
//性能测试
const newTestStr = 'abcDeXyHzabcDeXyHzabcDeXyHzabcDeXyHzabcDeXyHzabcDeXyHzabcDeXyHzabcDeXyHzabcDeXyHzabcDeXyHzabcDeXyHz';
console.time('switchLLetterCase1')
for(let i = 0; i < 10 * 10000; i++){
switchLLetterCase1(newTestStr)
}
console.timeEnd('switchLLetterCase1')//switchLLetterCase1: 573.437255859375 ms
console.time('switchLLetterCase2')
for(let i = 0; i < 10 * 10000; i++){
switchLLetterCase2(newTestStr)
}
console.timeEnd('switchLLetterCase2')//switchLLetterCase2: 1360.840087890625 ms
console.time('switchLLetterCase3')
for(let i = 0; i < 10 * 10000; i++){
switchLLetterCase3(newTestStr)
}
console.timeEnd('switchLLetterCase3')//switchLLetterCase3: 290.35791015625 ms
通过上面的时间对比并结合本地多次测试得到相似的结果,我们可以看出思路三运行速度最快,利用数组的运行速度最慢。
思路一使用的是正则,但是这个正则非常的简单,其消耗性能也会相对较低。如果使用的是一个非常复杂的正则,那么它的运行时间还会继续增加。无法判定正则的渐渐复杂度,故综合结果和上方分析,其时间复杂度不会低于O(n)不会高于O(n^2);
思路二操作数组,使用了indexOf方法,其结果相对而言十分慢。其总的时间度达到了O(n^2);
思路三直接操作字符串,其时间复杂度较低,应该是O(n);
总的来说计算机处理数据的速度: 数字 > 字符串 > 数组