也许你我素未谋面,但很可能相见恨晚,我是前端胖头鱼
前言
金三银四 大潮如期到来,在这个裁员
、降薪
、倒闭
无处不在的寒冷季节里,咱们都没有太多选择的余地。唯有努力提高自身能力,才能在寒风凛冽中站住脚跟,抵御随时到来的风险。
最近有个朋友在面字节时遇到了一道有意思的题,按照他的话说,差点就因为这个题寄了,不过好在他在最后几分钟,充分调动体内的洪荒之力,解出了答案,朝字节又迈进了一步。
要不要一起来做做这道题!!!
1.# 62进制的大数相加
js
// 实现两个62进制数的大数加法
// 输入:两个 62 进制数,String 类型,仅考虑整数
// 输出:两数之和,String 类型
// 62 进制数:按照 1-9,a-z,A-Z 递增
function sum (a, b) {}
不知道你们看到这道题时是什么感受!!!
我当时的想法是:"好像只听过2进制、8进制、16进制、32进制,62进制是什么鬼? "
终于在看到这段信息后恍然大悟:62 进制数:按照 1-9,a-z,A-Z 递增,
我掰着手加脚指头
数了数...
- 0 ~ 9 是10个数
- a ~ z 是26个字母
- A ~ Z 是26个字母
加起来就是62个数,刚好凑齐62进制。
1.1 10进制大数相加(整数)
突然看到求62进制的两数之和,我内心多少是有点懵逼的,懵逼的点在哪?
就是字母咋相加啊?比如 1a(10进制的72)
+ 2 = 1c(10进制的74)
不过作为接受过9年义务教育的社会主义接班人
,一年级咱们就学会了加法 (也就是所谓的10进制加法)
。
回顾一下小学知识: 19 + 23?
js
1. 个位数相加:9 + 3 = 12。写下 2,进位 1。
19
+ 23
-----
2
2. 十位数相加并加上进位:1 + 2 + 1(进位)= 4。
19
+ 23
-----
42
非常简单是不是?咱们尝试一下用程序来计算两个10进制数的加法。
别担心,朋友,这段代码虽然看起来长了点,但它完全模拟的小学加法,很容易看懂。
敲黑板:10进制的大数相加也是面试的热点
js
const addBigNumbers = (a, b) => {
// 从尾部往前计算
let i = a.length - 1
let j = b.length - 1
// 进位标志,1或者0
let carry = 0
let result = ''
while (i >= 0 || j >= 0 || carry) {
const n1 = +(a[ i ] || 0)
const n2 = +(b[ j ] || 0)
// 当前位相加(包含进位部分)
let sum = n1 + n2 + carry
// 当前位相加如果>=10,说明要往前进1,否则要重置进位标志
if (sum >= 10) {
sum -= 10
carry = 1
} else {
carry = 0
}
// 递减往前算
i--
j--
// 当前位 + 前面低位的结果
result = sum + '' + result
}
return result
}
console.log(addBigNumbers('19', '23')) // 42
console.log(addBigNumbers('100', '1024')) // 1124
1.2# 62进制与10进制之间如何转换?
其实咱们知道如何实现10进制大数相加
之后,62进制的大数相加
也就完成了一大半,只要解决这2个问题,面试官就该给你过了。
- 62进制转化为10进制
- 10进制转化为62进制
想想只要能将62进制转化为10进制进行相加,再把结果转化为62进制,问题不就迎刃而解了吗?
js
1a (10进制的72) + 2 = 1c (10进制的74)
1.3# 62进制转化为10进制
让我们一起来回顾一下计算机基础知识:62进制转化为10进制的规则
设 62 进制数为 d[n]d[n-1]...d[1]d[0],其中 d[i] 表示第 i 位的数字(从右向左,最低位为0)。
则该数的十进制值为:
js
d[0] * 62^0 + d[1] * 62^1 + ... + d[n] * 62^n
举个例子
js
1a = 1 * 62^1 + 10 * 62^0 // 62进制中的a代表10进制中的10,b代表的是11以此类推
= 1 * 62 + 10 * 1
= 62 + 10
= 72
有了这个规律用代码实现就很方便了.
js
const base62ToDecimal = (str) => {
const base = 62
const digits = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
let len = str.length
let sum = 0
let n =0 // 最后一位是0,倒数第二位是1...
while (len--) {
let digit = digits.indexOf(str[ len ]) // 获取当前位的10进制数,例如a表示10进制的10,b表示11,c表示12...
sum += digit * Math.pow(base, n)
n++
}
return sum
}
// 示例
console.log(base62ToDecimal('1a')) // 72
console.log(base62ToDecimal('1c')) // 74
1.4# 10进制转化为62进制
同样 10进制转化为62进制也有规律可言:
- 用输入的十进制数除以 62,得到商和余数。
- 将余数对应的 62 进制字符放在结果的最低位。
- 将商作为新的输入,重复步骤 1 和 2,直到商为 0。
- 将得到的所有余数按照逆序排列,就是最终的 62 进制表示。
举个例子: 将10进制的72转化为64进制是啥?
ini
第一步,将 72 不断除以 62,得到的余数就是 62 进制数的低位数字。依次计算如下:
1. 72 ÷ 62 = 1 ... 10 (余数为 10,10对应的62进制字符为 'a')
2. 1 ÷ 62 = 0 ... 1 (余数为 1,1对应的62进制字符为 '1')
第二步,将每次得到的余数按照逆序排列,就是最终的62进制表示:
72(10进制)= '1a'(62进制)
根据规律聪明的你也能很快用程序来实现10进制到62进制的转换
js
const decimalToBase62 = (decimal) => {
const base = 62
const digits = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
let result = []
while (decimal > 0) {
const remainder = decimal % base // 取余
result.push(digits[ remainder ]) // 将余数真正对应62进制字符存入数组
decimal = Math.floor(decimal / base) // 计算商,用于下次计算
}
return result.reverse().join('') || '0' // 反转结果
}
console.log(decimalToBase62('72')) // 1a
console.log(decimalToBase62('74')) // 1c
1.5 解题啦!!!
啰嗦了这么多,大家有没有发现解决62进制大数相加所需的知识点实在是太基础了
- 会小学加法
- 会进制转换
最后请丢出这份代码,亮瞎面试官的眼吧!!!
js
// 62进制转10进制
const base62ToDecimal = (str) => {
const base = 62
const digits = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
let len = str.length
let sum = 0
let n =0 // 最后一位是0,倒数第二位是1...
while (len--) {
let digit = digits.indexOf(str[ len ]) // 获取当前位的10进制数,例如a表示10进制的10,b表示11,c表示12...
sum += digit * Math.pow(base, n)
n++
}
return sum
}
// 10进制转62进制
const decimalToBase62 = (decimal) => {
const base = 62
const digits = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
let result = []
while (decimal > 0) {
const remainder = decimal % base // 取余
result.push(digits[ remainder ]) // 将余数真正对应62进制字符存入数组
decimal = Math.floor(decimal / base) // 计算商,用于下次计算
}
return result.reverse().join('') || '0' // 反转结果
}
const addBigBase62Numbers = (a, b) => {
// 转化为10进制的字符串
a = '' + base62ToDecimal(a)
b = '' + base62ToDecimal(b)
// 从尾部往前计算
let i = a.length - 1
let j = b.length - 1
// 进位标志,1或者0
let carry = 0
let result = ''
while (i >= 0 || j >= 0 || carry) {
const n1 = +(a[ i ] || 0)
const n2 = +(b[ j ] || 0)
// 当前位相加(包含进位部分)
let sum = n1 + n2 + carry
// 当前位相加如果>=10,说明要往前进1,否则要重置进位标志
if (sum >= 10) {
sum -= 10
carry = 1
} else {
carry = 0
}
// 递减往前算
i--
j--
// 当前位 + 前面的
result = sum + '' + result
}
return decimalToBase62(result) // 将10进制再转化为62进制
}
console.log(addBigBase62Numbers('1a', '2')) // 1c
console.log(addBigBase62Numbers('Z', '1')) // 10
2.# 另一种解法
咱们废了老大劲,写了一大坨的代码才实现这个功能,有没有其他更简便一点的解法呢?
其实62进制的数学性质和10进制类似,只是基数(62)不同而已,在10进制中是逢十进一
,62进制中就是逢62进一
了。所以在我们知道10进制的大数相加如何解之后,仅仅需要做少量的修改就可以变成62进制的大数相加。
搞起!!!
js
const addBigBase62Numbers = (a, b) => {
const digits = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
let i = a.length - 1
let j = b.length - 1
let carry = 0
let result = ''
while (i >= 0 || j >= 0 || carry) {
const n1 = digits.indexOf(a[i] || 0) // 获取当前位的10进制表示,如a表示10,b表示11
const n2 = digits.indexOf(b[j] || 0) // 获取当前位的10进制表示,如a表示10,b表示11
let sum = n1 + n2 + carry
// 62进1
if (sum >= 62) {
sum -= 62
carry = 1
} else {
carry = 0
}
// digits[sum] 将当前位转换回62进制
result = digits[sum] + result
i--
j--
}
return result
}
console.log(addBigBase62Numbers('1a', '2')) // 1c
console.log(addBigBase62Numbers('Z', '1')) // 10
好舒畅、代码一下子少了一大坨,又可以和面试官吹牛逼了...
3.# 举一反三
文中我们只处理了整数的62进制大数相加,聪明你可以尝试一下这几种场景
- 带小数的62进制大数相加?
- n进制的大数相加?
原理都是类似的,期待你在评论区写下你的答案,一起交流。