😠 就因為這道題,面位元組差點兒就寄了...

發表於2024-02-27

也許你我素未謀面,但很可能相見恨晚,我是前端胖頭魚

前言

金三銀四大潮如期到來,在這個裁員降薪倒閉無處不在的寒冷季節裡,我們們都沒有太多選擇的餘地。唯有努力提高自身能力,才能在寒風凜冽中站住腳跟,抵禦隨時到來的風險。

最近有個朋友在面位元組時遇到了一道有意思的題,按照他的話說,差點就因為這個題寄了,不過好在他在最後幾分鐘,充分調動體內的洪荒之力,解出了答案,朝位元組又邁進了一步。

要不要一起來做做這道題!!!

1.# 62進位制的大數相加


// 實現兩個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 遞增

我掰著手加腳指頭數了數...

  1. 0 ~ 9 是10個數
  2. a ~ z 是26個字母
  3. A ~ Z 是26個字母

加起來就是62個數,剛好湊齊62進位制。

1.1 10進位制大數相加(整數)

突然看到求62進位制的兩數之和,我內心多少是有點懵逼的,懵逼的點在哪?

就是字母咋相加啊?比如 1a(10進位制的72) + 2 = 1c(10進位制的74)

不過作為接受過9年義務教育的社會主義接班人,一年級我們們就學會了加法(也就是所謂的10進位制加法)

回顧一下小學知識: 19 + 23?

1. 個位數相加:9 + 3 = 12。寫下 2,進位 1。
  19
+ 23
-----
   2
   
2. 十位數相加並加上進位:1 + 2 + 1(進位)= 4。
  19
+ 23
-----
  42

非常簡單是不是?我們們嘗試一下用程式來計算兩個10進位制數的加法。

別擔心,朋友,這段程式碼雖然看起來長了點,但它完全模擬的小學加法,很容易看懂。

敲黑板:10進位制的大數相加也是面試的熱點

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個問題,面試官就該給你過了。

  1. 62進位制轉化為10進位制
  2. 10進位制轉化為62進位制

想想只要能將62進位制轉化為10進位制進行相加,再把結果轉化為62進位制,問題不就迎刃而解了嗎?

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)。

則該數的十進位制值為:

d[0] * 62^0 + d[1] * 62^1 + ... + d[n] * 62^n

舉個例子

1a = 1 * 62^1 + 10 * 62^0 // 62進位制中的a代表10進位制中的10,b代表的是11以此類推
   = 1 * 62 + 10 * 1
   = 62 + 10
   = 72

有了這個規律用程式碼實現就很方便了.

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進位制也有規律可言:

  1. 用輸入的十進位制數除以 62,得到商和餘數。
  2. 將餘數對應的 62 進位制字元放在結果的最低位。
  3. 將商作為新的輸入,重複步驟 1 和 2,直到商為 0。
  4. 將得到的所有餘數按照逆序排列,就是最終的 62 進製表示。

舉個例子: 將10進位制的72轉化為64進位制是啥?


第一步,將 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進位制的轉換

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進位制大數相加所需的知識點實在是太基礎了

  1. 會小學加法
  2. 會進位制轉換

最後請丟出這份程式碼,亮瞎面試官的眼吧!!!

// 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進位制的大數相加。

搞起!!!

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進位制大數相加,聰明你可以嘗試一下這幾種場景

  1. 帶小數的62進位制大數相加?
  2. n進位制的大數相加?

原理都是類似的,期待你在評論區寫下你的答案,一起交流。

相關文章