引言
对于程序员来说, 算法二字应该都不陌生!!
首先什么是 算法
? 它是解决问题的一个思路, 至于使用什么语言去完成不是它的重点, 你可以使用 Java
也可以使用 JS
甚至可以是任何你所熟悉的语言!!
那么我们为什么要学习 算法
呢? 即便我们没研究过它, 我们不是照样一样能完成手头的工作嘛? 其实在工作中, 业务开发过程中, 我们肯定会遇到一个个问题, 并通过编码解决它, 这中间其实或多或少都会用到 算法
只是我们可能对 算法
没怎么研究, 所以并没有感知到!!!
那么学习 算法
到底能给我带来什么呢?
- 对于求职者而,
算法
能力能成为你拿到优质offer
的敲门砖, 不管是大厂还是国外面试都要求有一定算法
能力 算法
是解决某类问题的的思路, 通过它可以很好培养我们的逻辑思维能力- 也许在平常工作中你用不到你所学的
算法
, 但是当遇到一些复杂问题, 它可以帮助你在复杂的情况中更好地分析问题、解决问题、从而寻找出最优的解决问题的思路 - 让我们自己研究
算法
很难, 但是目前很多算法
都是几代人努力, 研究出来的, 我们可以直接研究学习, 站在巨人的肩膀上成就自我, 何乐而不为呢 算法
是一种解决问题的思路和方法, 也许有机会应用到生活和事业的其他方面呢- 长期来看, 大脑思考能力是个人最重要的核心竞争力, 而算法是为数不多的能够有效训练大脑思考能力的途径之一
所以相信我, 学习
算法
应该是一件很酷的事情, 所以这里我专门整了个专栏, 希望我能坚持下去吧....
一、题目介绍
- 描述:
- 首先何谓回文数, 回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数, 比如:
121
、1221
、13231
... - 本题就是要实现一个方法, 对输入的整数进行判断, 如果该值是个回文整数则返回
true
否则返回false
- 示例:
js
示例 1:
输入: x = 121
输出: true
示例 2:
输入: x = -121
输出: false
解释: 从左向右读, 为 121 从右向左读, 为 121 因此它不是一个回文数
示例 3:
输入: x = 10
输出: false
解释: 从右向左读, 为 01 因此它不是一个回文数
二、暴力破解
思路:
- 首先先将数字转为字符串, 如此是为了方便循环、获取数字(字符)
- 循环一半的数据, 然后将每一对, 对称的字符进行对比
- 如果每对, 对称的字符全部相等, 则说明该输入是一个回文数字
js
/**
* @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
执行结果如下:
三、原生方法
使用 JS
原生方法:
- 先将数字转为字符串
- 再将字符串转为数组, 然后使用数组方法对数组进行反转, 然后再转为字符串, 这样就拿到一个反转后的字符串数字
- 然后将反转的字符串数字和原本的字符串数字进行比较, 如果相等则表示输入的数字是一个回文数字
js
/**
* @param {number} num
* @return {boolean}
*/
var isPalindrome = function(num) {
const strNum = num.toString()
const reverseStrNum = [...strNum].reverse().join('')
return reverseStrNum === strNum
};
在 LeetCode
执行结果如下:
四、优雅解法
上面的解法其实都是将 数字转换为字符串
, 然后检测字符串是否是回文字, 而这个转换的过程就需要额外的损耗, 不管是时间、还是空间都是有所损耗的!!
如果不转换数字, 我们可能想到的思路就是直接将数字进行反转, 然后和原数字进行对比即可!! 但是这样就会有个问题, 就是在反转数字时可能会出现 整数溢出
问题, 就是反转的数字可能大于 int.MAX
实际上我们并不需要反转全部数字, 我们只需要反转数字的 后半部分
, 然后和数字的 前半部分
进行比较即可! 比如:
- 输入数字是
1221
, 我们需要将数字的最后两位21
进行反转得到数字12
, 然后和数字前半部分12
进行比较, 发现它们相等, 则说明数字1221
是个回文数 - 输入数字是
1231
, 我们尝试将数字的最后两位31
进行反转得到数字13
, 然后和数字前半部分12
进行比较, 发现它们并不相等则说明数字1231
不是一个回文数
这里我们需要解决几个问题:
- 如何循环数字, 并判断何时结束循环?
- 如何获取数字的最后一位, 并对数字进行反转?
- 需要注意数字基数位和偶数位的不同情况
- 循环我们可以使用
while
来处理, 至于何时结束循环, 只需要对比反转的数字
和剩余的数字
, 当反转的数字
大于或等于剩余的数字
时则结束循环, 因为此时反转的数字
必然已经过半, 具体代码如下所示,reverseNum
为反转后的数字num
则为剩余的数字
js
while (num > reverseNum) {
}
- 这里我们可以使用
num % 10
来获取num
的最后一个数字, 例如123 % 10
则会返回数字123
最后一位数字3
; 至于反转逻辑可以直接看下面代码:
- 循环外声明变量
reverseNum
用于记录每次反转后的值 - 循环内每次都会获取当前
num
的最后一位数字last
, 然后作为reverseNum
的最后一位数值, 做法就是原本的reverseNum
值乘以10
, 然后加上获取到的last
- 最后还需要移除
num
的最后一位数字, 直接除以10
, 然后抹除小数位即可
js
let reverseNum = 0
while (num > reverseNum) {
const last = num % 10 // 取当前 num 最后一位
reverseNum = reverseNum * 10 + last // 将最后一位加到 reverseNum
num = Math.floor(num / 10) // 当前 num, 移除最后一位
}
- 在循环结束后, 通过判断
num
和reverseNum
来确定输入是否是回文数字, 这里有两种情况
- 当输入是偶数位, 则只有当
num
和reverseNum
完全相等情况下, 才是回文数字 - 当输入是基数位, 则允许
reverseNum
和num
相差一位, 即num === Math.floor(reverseNum / 10)
情况下输入才是回文数字
js
// - num 和 reverseNum 相等, 说明输入是一个偶数位并且是个回文数, 例: 1001、1221
// - Math.floor(reverseNum / 10) 移除 reverseNum 最后一位, 如果值和 num 相等, 说明输入是一个基数位并且是个回文数, 例: 121、434
if (num === reverseNum || num === Math.floor(reverseNum / 10)) {
return true
}
最后完整编码如下: 在函数开始时还针对特殊情况做了处理, 具体看注释说明
js
/**
* @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
执行结果如下:
复杂度分析:
- 时间复杂度:
O(log^n)O
对于每次循环, 我们都会将输入除以10
, 因此时间复杂度为 O(log^n) - 空间复杂度:
O(1)
我们只需要常数空间存放若干变量