5
\$\begingroup\$

Problem description:

Given a 32-bit signed integer, reverse digits of an integer.

Note: Assume we are dealing with an environment that could only store integers within the 32-bit signed integer range: \$[−2^{31}, 2^{31} − 1]\$. For the purpose of this problem, assume that your function returns 0 when the reversed integer overflows.

My solution:

var reverse = function(x) {
    temp = (x < 0) ? -1 : 1;
    x = x * temp;
    let res = x % 10;
    while(x > 9) {
        x = (x - x % 10) / 10;
        res = 10 * res + x % 10;
    };
    res = ((res < 2147483648) && (res >= -2147483648)) ? res : 0;
    return res * temp;
};

console.log(reverse(-321));
console.log(reverse(1534236469));

Test:

Input #1: -321
Expected #1: -123

Input #2: 1534236469
Expected #2: 0

Test summmary:

Solution accepted
Runtime: 88ms

Question: Is there a way to make this function less complex, maybe also improving its runtime?

\$\endgroup\$
1
  • \$\begingroup\$ If you execute that code in an environment that could only store integers within the 32-bit signed integer range, then the code will fail detecting values outside the valid range. So I am not sure your code solves the problem. \$\endgroup\$
    – Etoplay
    Commented Nov 6, 2020 at 13:19

3 Answers 3

6
\$\begingroup\$

A simple and clear approach would be to take an array of characters of the number, and reverse the array:

const reverse = function(inputNum) {
    const result = Math.sign(inputNum) * String(Math.abs(inputNum))
      .split('')
      .reverse()
      .join('');
    return result < -2147483648 || result > 2147483647 ? 0 : result;
};
console.log(reverse(-321));
console.log(reverse(1534236469));

The runtime is a little bit higher (92ms, not sure how precisely reliable the measurements are on LeetCode, they seemed all over the place), but in the vast majority of real-world situations, to write strong and maintainable code, one should first optimize clarity and readability, and then if the whole app isn't running as fast as would be ideal, run a performance test to identify bottlenecks and work on those particular bottlenecks.

Note the use of Math.sign to determine whether the number is positive or negative - it'll return -1 if the number is negative, and 1 if the number is positive, so it's probably a bit more appropriate for the situation.

Another approach without creating an intermediate array would be

let res = '';
for (let i = inputStr.length - 1; i >= 0; i--) res += inputStr[i];

With regards to your code:

Always declare your variables You're implicitly creating a global variable temp. Implicit creation of global variables will cause odd bugs when a function gets called when it's already in the process of running. Consider using a linter or strict mode or both to avoid these sorts of mistakes. (When declaring variables, prefer const, or use let when it needs to be reassigned.) On a similar note, your reverse function should be declared with const (not var).

Give variables meaningful names An argument named x and a variable named temp aren't very informative at a glance. How about calling them inputNum and sign instead?

\$\endgroup\$
4
  • \$\begingroup\$ By reversing the sense of the validity tests (from the way they were given in the question, which is correct) the reverse function in this answer gets them wrong. It should be return (result < -2147483648 || result > 2147483647) ? 0 : result; \$\endgroup\$ Commented Nov 6, 2020 at 11:12
  • \$\begingroup\$ Quick question (I'm not a JS dev): What is Math.sign(inputNum) * String(Math.abs(inputNum)) for? Wouldn't the number itself already contain all the information? \$\endgroup\$
    – Aloha
    Commented Nov 8, 2020 at 22:50
  • \$\begingroup\$ @PNDA The Math.sign(inputNum) is being multiplied by String(Math.abs(inputNum)) .split('') .reverse() .join('') - it's a much longer expression on the right, and for that logic to be implemented, the sign needs to be stripped out, which can be done with Math.abs. \$\endgroup\$ Commented Nov 8, 2020 at 22:58
  • \$\begingroup\$ @CertainPerformance Oh, I see what I missed. Thanks! \$\endgroup\$
    – Aloha
    Commented Nov 9, 2020 at 8:48
5
\$\begingroup\$

Disclaimer: Not a Code Reviewer

Just a few brief comments:

  • Looks pretty good!

  • You have the right approach.

  • We can just make it (very slightly) more readable if you will, or maybe add a new function to get the sign for instance.

  • We are allowed to alter LeetCode's variable names.

  • LeetCode's runtime/memory measurements are inaccurate (fluctuating; no isolation – differs from cloud region to region); we can safely ignore those data.

  • We are not supposed to use the built-in .reverse() of JavaScript for the reverse integer problem, not to mention that would be inefficient in JavaScript.

  • Here is a rough imprecise non-isolated benchmark, still efficiency is pretty visible:

const reverseUsingMath = function(num) {
    sign = getSign(num);
    num *= sign;
    let res = num % 10;
    while (num > 9) {
        num -= num % 10;
        num /= 10;
        res *= 10;
        res += num % 10;
    };

    res = (res < 2147483648) && (res >= -2147483648) ? res : 0;
    return res * sign;
};

const getSign = function(num) {
    return num < 0 ? -1 : 1;
}

let counter = 10000000;
let start = Date.now();

for (let i = counter; i >= 0; i--) {
    reverseUsingMath(-2147483641);
}

let end = Date.now() - start;

console.log(end / 1000 + " is the runtime of reverseUsingMath " + counter + " times benchmark test. 😳 ");



const reverseWithBuiltInReverse = function(inputNum) {
    const result = Math.sign(inputNum) * String(Math.abs(inputNum))
        .split('')
        .reverse()
        .join('');
    return result <= -2147483648 || result > 2147483648 ? 0 : result;
};


start = Date.now();

for (let i = counter; i >= 0; i--) {
    reverseWithBuiltInReverse(-2147483641);
}

end = Date.now() - start;

console.log(end / 1000 + " is the runtime of reverseWithBuiltInReverse " + counter + " times benchmark test. 😳 ");

// Not an efficient solution because of the parseInt() function;
// Yet easy to understand;
const reverseWithParseInt = function(num) {
    let reversed = 0;

    while (num) {
        reversed = parseInt(reversed * 10);
        reversed = parseInt(reversed + num % 10);
        num = parseInt(num / 10);
    };


    return (reversed > 2147483647) || (reversed < -2147483648) ? 0 : reversed;
};


start = Date.now();

for (let i = counter; i >= 0; i--) {
    reverseWithParseInt(-2147483641);
}

end = Date.now() - start;

console.log(end / 1000 + " is the runtime of reverseWithParseInt " + counter + " times benchmark test. 😳 ");



function reverseUsingMathAndUseStrict(num) {
    "use strict";
    const max = 0x80000000;
    const sign = (num & max) ? -1 : 1;
    num *= sign;
    var res = num % 10;
    if (num < 1e9 || res < 3) {
        while (num > 9) {
            num = num / 10 | 0;
            res = res * 10 + (num % 10);
        }
        return res < max ? res * sign : 0;
    }
    return 0;
}


start = Date.now();

for (let i = counter; i >= 0; i--) {
    reverseUsingMathAndUseStrict(-2147483641);
}

end = Date.now() - start;

console.log(end / 1000 + " is the runtime of reverseUsingMathAndUseStrict " + counter + " times benchmark test. 😳 ");

  • Here is a C solution:
// Since the relevant headers are already included on the LeetCode platform,
// the headers can be removed;
#include <stdio.h>
#include <limits.h>

int reverse(int num) {

    long long reversed = 0;

    while (num) {
        reversed *= 10;
        reversed += num % 10;
        num /= 10;
    }

    return reversed < INT_MIN || reversed > INT_MAX ? 0 : reversed;
}


int main() {
    puts(reverse(123456789) == 987654321 ? "true" : "false");
    puts(reverse(-123456789) == -987654321 ? "true" : "false");
}

PS: Now a code reviewer has to review my code ( ˆ_ˆ )

Happy Coding! ( ˆ_ˆ )


Reference

\$\endgroup\$
1
  • 2
    \$\begingroup\$ Use jsbench. It's usually more accurate at timing code execution. \$\endgroup\$ Commented Nov 5, 2020 at 23:07
2
\$\begingroup\$

Performance

Numbers

You can get a little more out of JS numbers if you force them to be 32 Bit Signed Integers. (Note this will depend on the JS engine. Works for V8 (chrome))

This is done by performing a bit wise operation on the number Eg the double 2147483648 is converted to the uint32 -2147483648 by applying bit wise or zero (2147483648 | 0) === -2147483648 is true.

Integers are generally about 4-5% quicker than using Doubles.

As applying a bit-wise operator to a positive double < 2 ** 31 is the equivalent to Math.floor you can also gain some improvement by simplifying x = (x - x % 10) / 10; to x = x / 10 | 0. Saving a subtraction and a remainder per iteration.

Early exit

Approx 42% of reversed numbers will be out of range.

Thus there is also an opportunity for performance gain with an exit early.

We can check if the lowest digit is greater than 2 and number is greater than 1e9 and just return 0 if so thus not having to process a significant number of the 42% out of range values.

Use declared variables

You use the value temp that you have not declared. That means it will be in global scope. Every step out of the current scope a variable is the slower the access to the variable.

If you declare temp in the functions scope and use strict mode you will gain a significant performance boost.

Needless test

You can remove the test for < -2147483648 as the result will be positive until you revert the sign. Thus you can change the sign at the very last moment saving the need to check past the min int.

Rewrite is 3.5 times faster

The rewrite is 3.5 times quicker (for a random set of 32bit signed integers)

Though the source is a little longer it is well worth the performance gain

function reverse(num) {
    "use strict";
    const max = 0x80000000;
    const sign = (num & max) ? -1 : 1;    
    num *= sign;
    var res = num % 10;
    if (num < 1e9 || res < 3) {
        while (num > 9) {
            num = num / 10 | 0;
            res = res * 10 + (num % 10);
        }
        return res < max ? res * sign : 0;
    }
    return 0;
}
\$\endgroup\$
1
  • 1
    \$\begingroup\$ @Emma Must be first line of function or first line of file / script tag As the OPs code is likely running in an embedded file or script tag its safest to put inside function.. Most add it as first line of file / script tag to save the need to add to each function. Not needed for modules as they are always in strict mode. \$\endgroup\$
    – Blindman67
    Commented Nov 7, 2020 at 16:47

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.