如何获取1到某个数之间所有的对称数(回文)

305 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第14天,点击查看活动详情

困难再大,也不应该更不能束缚住那颗勇往直前的心。

分析问题

我们要先了解什么是对称数(回文):那就是前后完全对称的数,比如1,9,99,88,101,202,111等。
在题目中给定了某区间,那么就需要在输入max,这里之所以从1开始,是不考虑0和负数的情况。

如何判断一个数是回文呢?根据回文的特点,我们可以有以下几种思路:

  1. 将数字转换成字符串A => 转换成数组 => 数组reverse => 转换成字符串B;将A和B进行对比,相等就是回文不想等就不是;

  2. 将数字转换成字符串A => 获取字符串A的最开始的下标i和最后一位下标j对应的字符,如果相等继续i++,j--,直到i不小于j为止,判定为回文,否则不是回文;

  3. 根据数字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]

符合预期。

注意事项

  1. 三种思路中for循环都是从i=1开始,需要i < max + 1或者i<=max这样在最后的计算结果中才会包含max的计算
  2. 思路一中反转字符串那里需要注意api的使用
  3. 思路二中当不想等的时候,注意break,这样能减少循环次数,并且注意对比的是单个数字,所以像flag等字段需要定义在for循环内
  4. 思路三种,最重要的是获得翻转数那里,可以寄一位固定公式。如果无法理解,可以将你想到的数字带入其中,进行验证

性能分析

代码

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

通过上方的性能分析,我们可以看出方案一的性能最差,方案三的性能最好,而且在本地测试相同代码多次后,结果和展示时间长短的排序是一样的

结论

  1. 思路一看似时间复杂度是O(n),但是注意这段代码const iStringReverse = iString.split('').reverse().join(''),它需要消耗大量的时间,操作数组是十分慢的,对于单一算法来说,如果可以不使用有序结果比如数组,是可以减少时间复杂度的;并且要注意内置api的使用
  2. 思路二和思路三的对比说明,相对而言对计算机对数字的处理要优于对字符串的处理。
  3. 虽然思路三最快,性能最好,但一定要理解“获得翻转数”那里的代码