一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第14天,点击查看活动详情。
困难再大,也不应该更不能束缚住那颗勇往直前的心。
分析问题
我们要先了解什么是对称数(回文):那就是前后完全对称的数,比如1,9,99,88,101,202,111等。
在题目中给定了某区间,那么就需要在输入max,这里之所以从1开始,是不考虑0和负数的情况。
如何判断一个数是回文呢?根据回文的特点,我们可以有以下几种思路:
-
将数字转换成字符串A => 转换成数组 => 数组reverse => 转换成字符串B;将A和B进行对比,相等就是回文不想等就不是;
-
将数字转换成字符串A => 获取字符串A的最开始的下标i和最后一位下标j对应的字符,如果相等继续i++,j--,直到i不小于j为止,判定为回文,否则不是回文;
-
根据数字num1生成其翻转数或者其倒叙数mun2,如果num1和num2相等,则是回文,否则不是回文
解题
思路一:
思路一应该是最容易想到的方案,首先看代码:
function palindrome1(max: number): number[]{
const arr: number[] = [];
if(max <= 0) return arr
for(let i = 1; i < max+1; i++){
const iString = i.toString();
//反转字符串
const iStringReverse = iString.split('').reverse().join('')
if(iString == iStringReverse){
arr.push(i)
}
}
return arr
}
功能测试
console.info(palindrome1(200))//[1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 22, 33, 44, 55, 66, 77, 88, 99, 101, 111, 121, 131, 141, 151, 161, 171, 181, 191]
符合预期。
思路二
function palindrome2(max: number): number[]{
const arr: number[] = [];
if(max <= 0) return arr;
for(let i = 1; i < max + 1; i++){
const iStr = i.toString()
const length = iStr.length;
let startIndex = 0;
let endIndex = length - 1;
let flag = true;
while( startIndex < endIndex){
if(iStr[startIndex] != iStr[endIndex]){
flag = false
break;
}else{
startIndex++;
endIndex--
}
}
if(flag){ arr.push(i)}
}
return arr
}
功能测试
console.info(palindrome2(200))//[1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 22, 33, 44, 55, 66, 77, 88, 99, 101, 111, 121, 131, 141, 151, 161, 171, 181, 191]
符合预期。
思路三
function palindrome3(max: number): number[]{
const arr: number[] = [];
if(max <= 0) return arr;
for(let i = 1; i < max + 1; i++){
let n = i;
let rev = 0;//记录翻转数
//获得翻转数
while( n > 0 ){
rev = rev * 10 + n % 10;
n = Math.floor( n / 10 )
}
if(i == rev){
arr.push(i)
}
}
return arr
}
功能测试
console.info(palindrome3(200))//[1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 22, 33, 44, 55, 66, 77, 88, 99, 101, 111, 121, 131, 141, 151, 161, 171, 181, 191]
符合预期。
注意事项
- 三种思路中for循环都是从i=1开始,需要i < max + 1或者i<=max这样在最后的计算结果中才会包含max的计算
- 思路一中反转字符串那里需要注意api的使用
- 思路二中当不想等的时候,注意break,这样能减少循环次数,并且注意对比的是单个数字,所以像flag等字段需要定义在for循环内
- 思路三种,最重要的是获得翻转数那里,可以寄一位固定公式。如果无法理解,可以将你想到的数字带入其中,进行验证
性能分析
代码
console.time('palindrome1')
palindrome1(100 * 10000)
console.timeEnd('palindrome1')//palindrome1: 293.262939453125 ms
console.time('palindrome2')
palindrome2(100 * 10000)
console.timeEnd('palindrome2')//palindrome2: 47.834228515625 ms
console.time('palindrome3')
palindrome3(100 * 10000)
console.timeEnd('palindrome3')//palindrome3: 41.034912109375 ms
通过上方的性能分析,我们可以看出方案一的性能最差,方案三的性能最好,而且在本地测试相同代码多次后,结果和展示时间长短的排序是一样的
结论
- 思路一看似时间复杂度是O(n),但是注意这段代码
const iStringReverse = iString.split('').reverse().join(''),它需要消耗大量的时间,操作数组是十分慢的,对于单一算法来说,如果可以不使用有序结果比如数组,是可以减少时间复杂度的;并且要注意内置api的使用 - 思路二和思路三的对比说明,相对而言对计算机对数字的处理要优于对字符串的处理。
- 虽然思路三最快,性能最好,但一定要理解“获得翻转数”那里的代码