[LeetCode] 9. 回文数

引言

对于程序员来说, 算法二字应该都不陌生!!

首先什么是 算法? 它是解决问题的一个思路, 至于使用什么语言去完成不是它的重点, 你可以使用 Java 也可以使用 JS 甚至可以是任何你所熟悉的语言!!

那么我们为什么要学习 算法 呢? 即便我们没研究过它, 我们不是照样一样能完成手头的工作嘛? 其实在工作中, 业务开发过程中, 我们肯定会遇到一个个问题, 并通过编码解决它, 这中间其实或多或少都会用到 算法 只是我们可能对 算法 没怎么研究, 所以并没有感知到!!!

那么学习 算法 到底能给我带来什么呢?

  1. 对于求职者而, 算法 能力能成为你拿到优质 offer 的敲门砖, 不管是大厂还是国外面试都要求有一定 算法 能力
  2. 算法 是解决某类问题的的思路, 通过它可以很好培养我们的逻辑思维能力
  3. 也许在平常工作中你用不到你所学的 算法, 但是当遇到一些复杂问题, 它可以帮助你在复杂的情况中更好地分析问题、解决问题、从而寻找出最优的解决问题的思路
  4. 让我们自己研究 算法 很难, 但是目前很多 算法 都是几代人努力, 研究出来的, 我们可以直接研究学习, 站在巨人的肩膀上成就自我, 何乐而不为呢
  5. 算法 是一种解决问题的思路和方法, 也许有机会应用到生活和事业的其他方面呢
  6. 长期来看, 大脑思考能力是个人最重要的核心竞争力, 而算法是为数不多的能够有效训练大脑思考能力的途径之一

所以相信我, 学习 算法 应该是一件很酷的事情, 所以这里我专门整了个专栏, 希望我能坚持下去吧….

一、题目介绍

地址: leetcode.cn/problems/pa…

  1. 描述:
  • 首先何谓回文数, 回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数, 比如: 121122113231
  • 本题就是要实现一个方法, 对输入的整数进行判断, 如果该值是个回文整数则返回 true 否则返回 false
  1. 示例:
示例 1: 
输入: x = 121
输出: true

示例 2: 
输入: x = -121
输出: false
解释: 从左向右读, 为 121 从右向左读, 为 121 因此它不是一个回文数

示例 3: 
输入: x = 10
输出: false
解释: 从右向左读, 为 01 因此它不是一个回文数

二、暴力破解

思路:

  • 首先先将数字转为字符串, 如此是为了方便循环、获取数字(字符)
  • 循环一半的数据, 然后将每一对, 对称的字符进行对比
  • 如果每对, 对称的字符全部相等, 则说明该输入是一个回文数字
/**
 * @param {number} num
 * @return {boolean}
 */
var isPalindrome = function(num) {
  const strNum = num.toString()  // 转为字符串, 方便处理
  const len = Math.floor(strNum.length / 2) // 计算循环的长度, 最多只需要循环一半的数据即可, 长度为奇数的, 不需要处理中间数字
  
  for (let i = 0; i < len; i ++) {  // 开始循环
    const currentChat = strNum.charAt(i) // 取到当前位置的字符
    const symmetryChat = strNum.charAt(strNum.length - 1 - i) // 取到对称位置的字符
    
    if (currentChat !== symmetryChat) { // 判断, 如果对称的字符不一致, 则不是回文字, 直接返回 false 结束循环
      return false
    }
  }

  // 循环结束, 表示所有对称字符都相等, 则说明输入是回文字, 返回 true
  return true
};

isPalindrome(121) // true

LeetCode 执行结果如下:

[LeetCode] 9. 回文数

三、原生方法

使用 JS 原生方法:

  • 先将数字转为字符串
  • 再将字符串转为数组, 然后使用数组方法对数组进行反转, 然后再转为字符串, 这样就拿到一个反转后的字符串数字
  • 然后将反转的字符串数字和原本的字符串数字进行比较, 如果相等则表示输入的数字是一个回文数字
/**
 * @param {number} num
 * @return {boolean}
 */
var isPalindrome = function(num) {
  const strNum = num.toString()
  const reverseStrNum = [...strNum].reverse().join('')

  return reverseStrNum === strNum
};

LeetCode 执行结果如下:

[LeetCode] 9. 回文数

四、优雅解法

上面的解法其实都是将 数字转换为字符串, 然后检测字符串是否是回文字, 而这个转换的过程就需要额外的损耗, 不管是时间、还是空间都是有所损耗的!!

如果不转换数字, 我们可能想到的思路就是直接将数字进行反转, 然后和原数字进行对比即可!! 但是这样就会有个问题, 就是在反转数字时可能会出现 整数溢出 问题, 就是反转的数字可能大于 int.MAX

实际上我们并不需要反转全部数字, 我们只需要反转数字的 后半部分, 然后和数字的 前半部分 进行比较即可! 比如:

  • 输入数字是 1221, 我们需要将数字的最后两位 21 进行反转得到数字 12, 然后和数字前半部分 12 进行比较, 发现它们相等, 则说明数字 1221 是个回文数
  • 输入数字是 1231, 我们尝试将数字的最后两位 31 进行反转得到数字 13, 然后和数字前半部分 12 进行比较, 发现它们并不相等则说明数字 1231 不是一个回文数

这里我们需要解决几个问题:

  • 如何循环数字, 并判断何时结束循环?
  • 如何获取数字的最后一位, 并对数字进行反转?
  • 需要注意数字基数位和偶数位的不同情况
  1. 循环我们可以使用 while 来处理, 至于何时结束循环, 只需要对比 反转的数字剩余的数字, 当 反转的数字 大于或等于 剩余的数字 时则结束循环, 因为此时 反转的数字 必然已经过半, 具体代码如下所示, reverseNum 为反转后的数字 num 则为 剩余的数字
while (num > reverseNum) {
  
}
  1. 这里我们可以使用 num % 10 来获取 num 的最后一个数字, 例如 123 % 10 则会返回数字 123 最后一位数字 3; 至于反转逻辑可以直接看下面代码:
  • 循环外声明变量 reverseNum 用于记录每次反转后的值
  • 循环内每次都会获取当前 num 的最后一位数字 last, 然后作为 reverseNum 的最后一位数值, 做法就是原本的 reverseNum 值乘以 10, 然后加上获取到的 last
  • 最后还需要移除 num 的最后一位数字, 直接除以 10, 然后抹除小数位即可
let reverseNum = 0

while (num > reverseNum) {
  const last = num % 10 // 取当前 num 最后一位
  reverseNum = reverseNum * 10 + last // 将最后一位加到 reverseNum
  num = Math.floor(num / 10) // 当前 num, 移除最后一位
}
  1. 在循环结束后, 通过判断 numreverseNum 来确定输入是否是回文数字, 这里有两种情况
  • 当输入是偶数位, 则只有当 numreverseNum 完全相等情况下, 才是回文数字
  • 当输入是基数位, 则允许 reverseNumnum 相差一位, 即 num === Math.floor(reverseNum / 10) 情况下输入才是回文数字
// - num 和 reverseNum 相等, 说明输入是一个偶数位并且是个回文数, 例: 1001、1221
// - Math.floor(reverseNum / 10) 移除 reverseNum 最后一位, 如果值和 num 相等, 说明输入是一个基数位并且是个回文数, 例: 121、434
if (num === reverseNum || num === Math.floor(reverseNum / 10)) {
  return true
}

最后完整编码如下: 在函数开始时还针对特殊情况做了处理, 具体看注释说明

/**
 * @param {number} num
 * @return {boolean}
 */
var isPalindrome = function(num) {
  // 边界处理: 负数, 肯定不是
  // 非 0 且个位数位 0 肯定不是, 例: 10 110  210
  if (num < 0 || (num !== 0 && num % 10 === 0)) {
    return false
  }

  let reverseNum = 0

  // 循环结束: 循环过半即可
  // - 当 num 等于 reverseNum, 则说明 num 和 reverseNum 长度(位数)相等
  // - 当 num 小于 reverseNum, 则说明 reverseNum 长度(位数)大于或等于 num
  while (num > reverseNum) {
    const last = num % 10 // 取当前 num 最后一位
    reverseNum = reverseNum * 10 + last // 将最后一位加到 reverseNum

    num = Math.floor(num / 10) // 当前 num, 移除最后一位
  }

  // - num 和 reverseNum 相等, 说明输入是一个偶数位并且是个回文数, 例: 1001、1221
  // - Math.floor(reverseNum / 10) 移除 reverseNum 最后一位, 如果值和 num 相等, 说明输入是一个基数位并且是个回文数, 例: 121、434
  if (num === reverseNum || num === Math.floor(reverseNum / 10)) {
    return true
  }

  return false
};


isPalindrome(10)

LeetCode 执行结果如下:

[LeetCode] 9. 回文数

复杂度分析:

  • 时间复杂度: O(log^n)O 对于每次循环, 我们都会将输入除以 10, 因此时间复杂度为 O(log^n)
  • 空间复杂度: O(1) 我们只需要常数空间存放若干变量

[LeetCode] 9. 回文数

原文链接:https://juejin.cn/post/7341797865108750362 作者:墨渊君

(0)
上一篇 2024年3月4日 上午10:05
下一篇 2024年3月4日 上午10:16

相关推荐

发表回复

登录后才能评论