🤖

LeetCode #9 Palindrome Numberをやってみた

に公開

問題 (Easy)

Given an integer x, return true if x is a palindrome, and false otherwise.
詳細

問題を理解する (5分)

与えられた整数xが、回文(上から読んでも下から読んでも同じ)である時、true、そうでない時(マイナスの時も)は、falseを返す。

解き方を日本語で組み立てる (10分)

  1. 整数を文字列の型にし、それぞれの桁の数字を分解して、配列に挿入する (split関数)
  2. 配列の先頭 (head)と後尾の数(end)を比較する
  3. indexが同じまたは、隣同士(1違い)になるまで繰り返す
  4. head と endの数が同じであれば、trueを返す
  5. headとendの数が違えば、falseを返す。

考えた解き方をJavaScriptコードで表現する (15分)

私が考えたコード↓

わかりにくすぎるが、これが今の私の実力

/**
 * @param {number} x
 * @return {boolean}
 */
var isPalindrome = function(x) {
    // 1. 整数のそれぞれの桁の数字を分解して、配列に挿入する
    const numArray = String(x).split('');
    
    // 2. 配列の先頭 (head)と後尾の数(end)を比較する
    for (i = 1; i < numArray.length; i++){
        let head_index = i -1;
        let end_index = numArray.length - i;
        let head = numArray[head_index];
        let end = numArray[end_index];

        //3. indexが同じまたは、隣同士(1違い)の時は
        if (head_index == end_index || head_index == end_index - 1){
            if (head !== end) {
                return false;
            } else {
                //4. headとendの数が同じであれば、trueを返す
                return true;
            }
        } else {
            continue;
        }

        // 5. headとendの数が違えば、falseを返す。
        if (head !== end) {
            return false
        } 
    }
};

Geminiにリファクタリングしてもらった

head_indexの数、end_indexが同じまたは、head_indexがend_indexより1小さい時を条件に、while構文にリファクタリングできそうかな、、、と思ったけど、どうするかが思いつかずGeminiに聞いたら以下のコードが返ってきた。

/**
 * @param {number} x
 * @return {boolean}
 */
var isPalindrome = function(x) {
    const s = String(x);
    let head = 0;
    let end = s.length - 1;

    while (head < end) {
        if (s[head] !== s[end]) {
            return false;
        }
        head++;
        end--;
    }

    return true;
};

こんなシンプルになるの!?

文字列を配列にしなくてよかった。

JavaScript の文字列は、一見するとただの文字の連なりに見えますが、実は配列のように各文字にインデックスを使ってアクセスできる。

head++, end--で表現できた。

そうだ1ずつ増やす/減らす方法はこれでできるんだ。この方法を思いつけなかったから、for文から while文にできなかった。

Geminiにもコードを書かせてみる

/**
 * @param {number} x
 * @return {boolean}
 */
var isPalindrome = function(x) {
    // 1. 負の数の場合は回文ではない
    if (x < 0) {
        return false;
    }

    // 2. 整数を文字列に変換
    const originalString = String(x);

    // 3. 文字列を反転
    const reversedString = originalString.split('').reverse().join('');

    // 4. 元の文字列と反転させた文字列を比較
    return originalString === reversedString;
};

気づき

  • 確かに負の数だったら、すぐfalseにできる
  • 整数を文字列にするのは正解
  • splitして、reverse関数で反対にできるんだ!そしたらめっちゃ簡単やん。
  • join関数は、配列の要素を結合する。

どっちがいいんだ?

私が書いたロジックの方がいいと思う。
Geminimのコードは、文字列を全て反転して比較しているが、私のコードは後ろから1つずつ比べる方法で、一つでも文字列が違った場合、即終了できる。

Follow-up Question

Could you solve it without converting the integer to a string?

えー。むずすぎる…。もしかしたら計算で回文かどうか分かるとかあるんかな。考えよう。

Geminiが出してくれた。

/**
 * @param {number} x
 * @return {boolean}
 */
var isPalindrome = function(x) {
    // 負の数は回文ではない
    if (x < 0) {
        return false;
    }

    // 0は回文
    if (x === 0) {
        return true;
    }

    let originalNumber = x;
    let reversedNumber = 0;

    while (originalNumber > 0) {
        const lastDigit = originalNumber % 10;
        reversedNumber = (reversedNumber * 10) + lastDigit;
        originalNumber = Math.floor(originalNumber / 10);
    }

    return x === reversedNumber;
};

なるほどね、、、10で割った余りを使ったり、10で割って切り上げたら桁を減らしていけるのか。
頭柔らかくすれば出てくる問題だったな

まとめ

文字全部を反転させたGeminiくんが出してくれたコードよりも、最初と最後の桁を逐次比較していった私のロジックの方がいいと思えた理由として、以下の理由があると考えた。

  • 大きな数を扱う時に、逐一比較する方が効率がいい
  • 将来の数の拡張性に対応できる
  • 早期に非回文を検出したい場合

Discussion