8. String to Integer (atoi)

MediumString
Leetcode Link

Problem Description

The myAtoi function is designed to mimic the behavior of the atoi function in C/C++, converting a string to a 32-bit signed integer following a set of specific rules. The steps of the algorithm are:

  1. Start by skipping any leading whitespace in the input string, as these are irrelevant for number conversion.
  2. Identify if the first non-whitespace character is a sign indicator ('-' for negative, '+' for positive). If found, note the sign for the final number. If no sign is specified, the number is considered positive by default.
  3. Read the subsequent characters until a non-digit is encountered or the end of the string is reached. These characters represent the numeric part of the string.
  4. Convert the sequence of digits into an integer. If no digits are found, the result should be 0. Apply the previously noted sign to this integer.
  5. Make sure the resulting integer fits within the range of a 32-bit signed integer. If it does not, "clamp" the value to the nearest boundary of the range, which is either -2**31 or 2**31 - 1.
  6. Return the processed integer as the final result.

A couple of important notes to consider:

  • The only whitespace character to be considered is the space character ' '.
  • All characters after the numeric part of the string should not be ignored, as they can influence the final output if they are non-digits.

Intuition

To convert the string to an integer while following the rules, we need to go step by step:

  1. Skip Whitespaces: Check each character until we encounter a non-whitespace character or the end of the string. This is achieved by using a while loop that increments an index whenever a space is found.

  2. Check for a Sign: Once we hit a non-whitespace character, we decide on the sign of our result. If it's a '-' we set the sign to -1, otherwise, we assume it's positive (1).

  3. Convert String to Integer: We parse the string character by character as long as they are numeric (digits). We need to be mindful of possible overflow past the 32-bit integer range, which happens when our number exceeds the maximum allowed value when multiplied by 10 or when adding the next digit.

  4. Handle Overflows: Since we're working within the bounds of a signed 32-bit integer, we ensure that any number that goes beyond the limits is clamped to the closest limit, either the lower bound -2**31 or the upper bound 2**31 - 1.

By carefully following each of these steps, we simulate the atoi function's behavior, producing a 32-bit signed integer from an input string.

Not Sure What to Study? Take the 2-min Quiz to Find Your Missing Piece:

How many times is a tree node visited in a depth first search?

Solution Approach

The solution implements the conversion process through a series of steps that correspond to the rules provided in the problem description. Here's a walk-through of the key components:

  1. Initialization: The function first checks if the string s is not empty. It then initializes important variables such as n for the length of the string, i for the current index, and res for the resulting integer value.

  2. Whitespace Skipping: Using a while loop, the implementation skips over the leading whitespace characters ' ' until a non-whitespace character is encountered by incrementing the i index. If i becomes equal to n (the length of the string), that means the string contained only whitespace, and we return 0.

  3. Sign Detection: The next character is checked to determine the sign of the resultant integer. If it's a '-', the variable sign is set to -1, otherwise 1. If a sign character is found, i is incremented to move past it.

  4. Integer Conversion: A while loop is used to iterate over the remaining characters of the string, as long as they are digits. Each digit is converted to an integer using int(s[i]) and added to the result res after multiplying the current res by 10 (shifting the number one decimal place to the left). The loop stops if a non-digit character appears.

  5. Overflow Handling: Before adding the new digit to res, we check for potential overflow. For positive numbers, it checks if res is greater than (2**31 - 1) // 10, or if res is equal to (2**31 - 1) // 10 and the next digit (c) is greater than 7 (since 2**31 - 1 ends in 7). For negative numbers, the same logic is applied but with the limits of -2**31. If an overflow is detected, the maximum or minimum 32-bit signed integer value is returned based on the sign.

  6. Result Return: Once the loop finishes, the integer is adjusted with the sign and returned as the result.

The code demonstrates good practices such as:

  • Early exits to avoid unnecessary processing (empty string or string with only spaces).
  • Overflow protection by checking the conditions before the actual overflow could occur.
  • Efficient string traversal by using index arithmetic rather than creating new strings or arrays.

The algorithm does not use any complex data structures, as it only requires integer variables to keep track of the result, the index, and the sign. It is a straightforward and efficient implementation governed by control statements to ensure that the resulting integer adheres to the constraints defined in the problem.

Discover Your Strengths and Weaknesses: Take Our 2-Minute Quiz to Tailor Your Study Plan:

What's the output of running the following function using input [30, 20, 10, 100, 33, 12]?

1def fun(arr: List[int]) -> List[int]:
2    import heapq
3    heapq.heapify(arr)
4    res = []
5    for i in range(3):
6        res.append(heapq.heappop(arr))
7    return res
8
1public static int[] fun(int[] arr) {
2    int[] res = new int[3];
3    PriorityQueue<Integer> heap = new PriorityQueue<>();
4    for (int i = 0; i < arr.length; i++) {
5        heap.add(arr[i]);
6    }
7    for (int i = 0; i < 3; i++) {
8        res[i] = heap.poll();
9    }
10    return res;
11}
12
1class HeapItem {
2    constructor(item, priority = item) {
3        this.item = item;
4        this.priority = priority;
5    }
6}
7
8class MinHeap {
9    constructor() {
10        this.heap = [];
11    }
12
13    push(node) {
14        // insert the new node at the end of the heap array
15        this.heap.push(node);
16        // find the correct position for the new node
17        this.bubble_up();
18    }
19
20    bubble_up() {
21        let index = this.heap.length - 1;
22
23        while (index > 0) {
24            const element = this.heap[index];
25            const parentIndex = Math.floor((index - 1) / 2);
26            const parent = this.heap[parentIndex];
27
28            if (parent.priority <= element.priority) break;
29            // if the parent is bigger than the child then swap the parent and child
30            this.heap[index] = parent;
31            this.heap[parentIndex] = element;
32            index = parentIndex;
33        }
34    }
35
36    pop() {
37        const min = this.heap[0];
38        this.heap[0] = this.heap[this.size() - 1];
39        this.heap.pop();
40        this.bubble_down();
41        return min;
42    }
43
44    bubble_down() {
45        let index = 0;
46        let min = index;
47        const n = this.heap.length;
48
49        while (index < n) {
50            const left = 2 * index + 1;
51            const right = left + 1;
52
53            if (left < n && this.heap[left].priority < this.heap[min].priority) {
54                min = left;
55            }
56            if (right < n && this.heap[right].priority < this.heap[min].priority) {
57                min = right;
58            }
59            if (min === index) break;
60            [this.heap[min], this.heap[index]] = [this.heap[index], this.heap[min]];
61            index = min;
62        }
63    }
64
65    peek() {
66        return this.heap[0];
67    }
68
69    size() {
70        return this.heap.length;
71    }
72}
73
74function fun(arr) {
75    const heap = new MinHeap();
76    for (const x of arr) {
77        heap.push(new HeapItem(x));
78    }
79    const res = [];
80    for (let i = 0; i < 3; i++) {
81        res.push(heap.pop().item);
82    }
83    return res;
84}
85

Example Walkthrough

Let's illustrate the solution approach with a small example. Consider the input string s = " -42" which we want to convert to a 32-bit signed integer following the defined rules.

  1. Initialization:
    • The string s is not empty, so we initialize n=5 (n being the string's length), i=0 for the current index, and res=0 for the resulting integer value.
  2. Whitespace Skipping:
    • We use a while loop to skip the leading whitespaces. After skipping, i=3 as the first three characters are spaces.
  3. Sign Detection:
    • The next character is '-', which indicates a negative number. We set sign=-1 and increment i to move past the sign, and now i=4.
  4. Integer Conversion:
    • We reach the digits now. With a while loop, we start adding the digits to res. The first character '4' is converted to an integer 4, and res becomes 4. Then i is incremented.
    • Next, i=5 and the character is '2'. We multiply res by 10 (shifting to the left) and add 2, making res=42.
    • i is incremented again, and with i=n, we have reached the end of the string, and the loop ends.
  5. Overflow Handling:
    • During integer conversion, we did not encounter any potential overflow cases, as the number -42 is within the range of a 32-bit signed integer.
  6. Result Return:
    • The final step is to apply the sign to res. With sign=-1, the result becomes -42, which is then returned.

This example adhered to the steps of the algorithm, resulting in the correct 32-bit signed integer conversion of the input string s. The key to solving this problem was carefully handling each character by categorizing it as whitespace, sign, or digit, and applying appropriate operations to avoid overflows.

Not Sure What to Study? Take the 2-min Quiz:

The three-steps of Depth First Search are:

  1. Identify states;
  2. Draw the state-space tree;
  3. DFS on the state-space tree.

Python Solution

1class Solution:
2    def myAtoi(self, str: str) -> int:
3        # Return 0 if string is empty
4        if not str:
5            return 0
6
7        length_of_string = len(str)
8        index = 0
9
10        # Skip leading whitespaces
11        while index < length_of_string and str[index] == ' ':
12            index += 1
13
14        # After skipping whitespaces, if we're at the end it means it's an empty string
15        if index == length_of_string:
16            return 0
17
18        # Check if we have a sign, and set the sign accordingly
19        sign = -1 if str[index] == '-' else 1
20        if str[index] in ['-', '+']:  # skip the sign for next calculations
21            index += 1
22
23        result = 0
24        max_safe_int = (2 ** 31 - 1) // 10  # Precomputed to avoid overflow
25
26        while index < length_of_string:
27            # If the current character is not a digit, break from loop
28            if not str[index].isdigit():
29                break
30
31            digit = int(str[index])
32
33            # Check for overflow cases
34            if result > max_safe_int or (result == max_safe_int and digit > 7):
35                return 2 ** 31 - 1 if sign == 1 else -2 ** 31  # Clamp to INT_MIN or INT_MAX
36
37            # Append current digit to result
38            result = result * 10 + digit
39            index += 1
40
41        # Apply sign and return final result
42        return sign * result
43

Java Solution

1class Solution {
2    public int myAtoi(String str) {
3        // Ensure the input string is not null
4        if (str == null) {
5            return 0;
6        }
7
8        int length = str.length();
9
10        // If the string is empty, return 0
11        if (length == 0) {
12            return 0;
13        }
14
15        int index = 0;
16
17        // Skip whitespace characters
18        while (index < length && str.charAt(index) == ' ') {
19            index++;
20        }
21
22        // If we reached the end of string after skipping spaces, return 0
23        if (index == length) {
24            return 0;
25        }
26
27        // Determine the sign based on the current character
28        int sign = 1;
29        if (str.charAt(index) == '-') {
30            sign = -1;
31            index++;
32        } else if (str.charAt(index) == '+') {
33            index++;
34        }
35
36        int result = 0;
37        // Pre-calculate the threshold to check for overflow
38        int threshold = Integer.MAX_VALUE / 10;
39      
40        // Convert the number
41        while (index < length) {
42            char currentChar = str.charAt(index);
43          
44            // Break if the current character is not a digit
45            if (currentChar < '0' || currentChar > '9') {
46                break;
47            }
48
49            // Check for overflow when adding a new digit
50            if (result > threshold || (result == threshold && currentChar > '7')) {
51                return sign == 1 ? Integer.MAX_VALUE : Integer.MIN_VALUE;
52            }
53
54            // Update result with the new digit
55            result = result * 10 + (currentChar - '0');
56            index++;
57        }
58
59        // Apply the determined sign to the result and return
60        return sign * result;
61    }
62}
63

C++ Solution

1#include <climits>
2#include <string>
3
4class Solution {
5public:
6    /**
7     * Converts the string argument to an integer (32-bit signed integer).
8     * Trims whitespace, manages sign, and processes characters until a non-digit is found,
9     * or the number goes out of the 32-bit signed integer range.
10     * 
11     * @param s - the string to convert to an integer.
12     * @return The parsed integer, or 0 if no conversion is possible.
13     */
14    int myAtoi(std::string s) {
15        // Trim leading whitespace
16        size_t start = s.find_first_not_of(" \t\n\r");
17        if (start == std::string::npos) return 0;  // Return 0 if the string is just whitespace
18      
19        s = s.substr(start);  // Trim leading whitespace
20      
21        bool isPositive = true;  // Flag indicating if the number is positive
22        int i = 0;              // Index for iteration
23        int answer = 0;         // Variable to accumulate the parsed number
24      
25        // Check if there is a sign in the beginning
26        if (s[i] == '+') {
27            isPositive = true;  // Positive number
28            ++i;
29        } else if (s[i] == '-') {
30            isPositive = false; // Negative number
31            ++i;
32        }
33      
34        // Process each character in the string
35        for (; i < s.length(); ++i) {
36            // Calculate the numeric value of the current character
37            int currentValue = s[i] - '0';
38          
39            // Break the loop if the character is not a digit
40            if (currentValue < 0 || currentValue > 9) break;
41          
42            // Check for overflow: if the current answer is already at the risk of overflow
43            // with an additional digit, return the respective limit
44            if (answer > INT_MAX / 10 ||
45                (answer == INT_MAX / 10 && currentValue > INT_MAX % 10)) {
46                return isPositive ? INT_MAX : INT_MIN;
47            }
48          
49            // Update the answer by adding the current digit
50            answer = answer * 10 + currentValue;
51        }
52      
53        // Return the final answer, considering the sign
54        return isPositive ? answer : -answer;
55    }
56};
57

Typescript Solution

1/**
2 * Converts the string argument to an integer (32-bit signed integer).
3 * Trims whitespace, manages sign, and processes characters until a non-digit is found,
4 * or the number goes out of the 32-bit signed integer range.
5 * @param str - the string to convert to an integer.
6 * @returns The parsed integer, or 0 if no conversion is possible.
7 */
8function myAtoi(str: string): number {
9    // Trim leading or trailing whitespace
10    str = str.trim();
11  
12    // Return 0 if the string is empty after trimming
13    if (!str) return 0;
14  
15    let isPositive: boolean = true; // Flag indicating if the number is positive
16    let i: number = 0;              // Index for iteration
17    let answer: number = 0;         // Variable to accumulate the parsed number
18  
19    // Check if there is a sign in the beginning
20    if (str[i] === '+') {
21        isPositive = true;  // Positive number
22        i++;
23    } else if (str[i] === '-') {
24        isPositive = false; // Negative number
25        i++;
26    }
27  
28    // Process each character in the string
29    for (; i < str.length; i++) {
30        // Calculate the numeric value of the current character
31        let currentValue: number = str.charCodeAt(i) - '0'.charCodeAt(0);
32      
33        // Break the loop if the character is not a digit
34        if (currentValue > 9 || currentValue < 0) break;
35      
36        // Check for overflow: if the current answer is already at the risk of overflow
37        // with an additional digit, return the respective limit
38        if (answer > Math.floor(2147483647 / 10) || 
39            answer > Math.floor((2147483647 - currentValue) / 10)) {
40            return isPositive ? 2147483647 : -2147483648;
41        }
42      
43        // Update the answer by adding the current digit
44        answer = answer * 10 + currentValue;
45    }
46  
47    // Return the final answer, considering the sign
48    return isPositive ? answer : -answer;
49}
50
Fast Track Your Learning with Our Quick Skills Quiz:

The three-steps of Depth First Search are:

  1. Identify states;
  2. Draw the state-space tree;
  3. DFS on the state-space tree.

Time and Space Complexity

Time Complexity

The time complexity of the given function is O(n), where n is the length of the string. Here's the breakdown:

  • We iterate once over the string to trim whitespaces, which takes O(n) in the worst case (if all characters are spaces).
  • Checking if the character is a sign ('+' or '-') is O(1).
  • The following while loop iterates over the rest of the string, but it runs at most n times, which is also O(n) in the worst case.
  • Each operation inside the while loop, including checking if a character is a digit, converting it to an integer, checking for overflow, and updating the result, is done in constant time O(1).

Therefore, the dominating factor in the time complexity is the length of the string n, resulting in O(n) overall.

Space Complexity

The space complexity of the function is O(1) because we use a fixed number of integer variables (i, sign, res, flag, and n) and they do not depend on the size of the input string. No additional structures are allocated that would grow with the input size. The space used by s is not counted since it is an input to the function and not additional space allocated by the function itself.

Learn more about how to find time and space complexity quickly.


Recommended Readings


Got a question? Ask the Teaching Assistant anything you don't understand.

Still not clear? Ask in the Forum,  Discord or Submit the part you don't understand to our editors.


TA 👨‍🏫