LeetCode 6-10 question explanation Java version (ten thousand words picture and text explanation LeetCode algorithm question 1-5 = = = = > > > < recommended Collection >)

Keywords: Java Algorithm leetcode Interview

Question 6. ZigZag Conversion

1. Topic description (medium difficulty)

Is to give a string, and then arrange the characters in a vertical "z", which is what it looks like below.

Then output each character in line, line 0, line 1, line 2

2. Solution I

Follow the process of writing Z, traverse each character, and then save the character in the corresponding line. Use goingDown to save the current traversal direction. If you traverse to both ends, change the direction.

 public String convert(String s, int numRows) {

        if (numRows == 1) return s;

        List<StringBuilder> rows = new ArrayList<>();
        for (int i = 0; i < Math.min(numRows, s.length()); i++)
            rows.add(new StringBuilder());

        int curRow = 0;
        boolean goingDown = false;

        for (char c : s.toCharArray()) {
            rows.get(curRow).append(c);
            if (curRow == 0 || curRow == numRows - 1) goingDown = !goingDown; //Traverse to both ends and change direction
            curRow += goingDown ? 1 : -1;
        }

        StringBuilder ret = new StringBuilder();
        for (StringBuilder row : rows) ret.append(row);
        return ret.toString();
    }

Time complexity: O (n), n is the length of the string.

Space complexity: O (n), which saves the space required for each character.

3. Solution II

Find out the rules of characters arranged in Z shape, and then save them directly.

We can see that the graph actually has a period. There are 8 0, 1, 2... 7, and then it starts to repeat the same path. The calculation of the period is cyclen = 2 × numRows - 2 = 2 × 5 - 2 = 8.

We found that there is a character in a cycle in line 0 and the last line, so the first character subscript is 0, the second character subscript is 0 + cyclen = 8, and the third character subscript is 8 + cyclen = 16.

The other lines are two characters.

The rule of the first character and line 0 is the same.

The second character is actually the subscript of line 0 of the next cycle minus the current line. What do you mean?

Let's find the second character in the first cycle of line 1. The subscript of line 0 in the next cycle is 8. Subtract 1 from the current line to get 7.

Let's find the second character in the first line and the second character in the cycle. The subscript of line 0 in the next cycle is 16. Subtract 1 from the current line to get 15.

Let's find the second character in the first cycle of line 2. The subscript of line 0 in the next cycle is 8. Subtract 2 from the current line to get 6.

Of course, the subscript must be less than n to prevent cross-border.

You can write code.

public String convert(String s, int numRows) {

    if (numRows == 1)
        return s;

    StringBuilder ret = new StringBuilder();
    int n = s.length();
    int cycleLen = 2 * numRows - 2;

    for (int i = 0; i < numRows; i++) {
        for (int j = 0; j + i < n; j += cycleLen) { //Add one cycle at a time
            ret.append(s.charAt(j + i));
            if (i != 0 && i != numRows - 1 && j + cycleLen - i < n) //Remove line 0 and the last line
                ret.append(s.charAt(j + cycleLen - i));
        }
    }
    return ret.toString();
}

Time complexity: O (n). Although it is a two-layer loop, the second loop adds cyclen each time, which is nothing more than traversing each character once, so the number of executions in the two-layer loop must be the length of the string.

Space complexity: O (n), save string.

4. Summary

This is the easiest to sum up. This problem has some meaning of finding rules. Solution 1 traverses along the arrangement, and solution 2 finds the law of subscript directly from the entrance of the answer.

Question 7. Reverse Integer

1. Topic description (simple and difficult)

Very simple, is to input an integer and output its inversion.

The first reaction is to take the remainder to get the single digits, then divide it by 10 to remove the single digits, and then save the inverted number with a variable.

public int reverse(int x) {
    int rev = 0;
    while (x != 0) {
        int pop = x % 10;
        x /= 10;
        rev = rev * 10 + pop;
    }
    return rev;
}

Then it doesn't seem so ideal.

Why? Shouldn't it be 9646324351 upside down. In fact, as mentioned in the title, the scope of int is [− 231231 − 1] [- 2 ^ {31}, 2 ^ {31} - 1] [− 231231 − 1], that is [− 214748364824147483647] [- 214748364824147483647] [− 214748364824147483647]. 9646324351 is obviously out of range, causing overflow. Therefore, we need to judge whether it overflows before output.

The key to the problem is the following sentence.

rev = rev * 10 + pop;

In order to distinguish the two rev s, we introduce temp for better explanation.

temp = rev * 10 + pop;

rev = temp;

We compared temp = rev * 10 + pop; Discuss. intMAX = 2147483647 , intMin = - 2147483648 .

For discussions larger than intMax, x must be positive and pop must be positive.

  • If Rev > intmax / 10, there must be overflow at this time.
  • If rev == intMax / 10 = 2147483647 / 10 = 214748364, rev * 10 is 2147483640. If pop is greater than 7, it must overflow. But! If pop is equal to 8, it means that the original number x is 8463847412, the input is int, and it is in the overflow state, so it is impossible to input, so it means that pop cannot be greater than 7, which means that there will be no overflow when rev == intMax / 10.
  • If Rev < intmax / 10, it means that the maximum Rev is 214748363, and rev * 10 is 2147483630. At this time, pop will not overflow.

The same is true for discussions less than intMin.

public int reverse(int x) {
    int rev = 0;
    while (x != 0) {
        int pop = x % 10;
        x /= 10;
        if (rev > Integer.MAX_VALUE/10 ) return 0;
        if (rev < Integer.MIN_VALUE/10 ) return 0;
        rev = rev * 10 + pop;
    }
    return rev;
}

Time complexity: how many cycles? The number of digits will be cycled as many times as possible, that is, log10(x)+1log_{10}(x) + 1log10(x)+1 time, so the time complexity is O (log (x)).

Space complexity: O (1).

Of course, we don't have to think so much. In a lazy way, we directly define rev as long, and then judge whether rev is within the range before output. If it is not, we directly output 0.

public int reverse(int x) {
    long rev = 0;
    while (x != 0) {
        int pop = x % 10;
        x /= 10;
        rev = rev * 10 + pop;
    }
    if (rev > Integer.MAX_VALUE || rev < Integer.MIN_VALUE ) return 0;
    return (int)rev;
}

2. Summary

A relatively simple question, mainly in the test to judge whether it is overflow, is another easy day!

Question 8: String to Integer

Title Description (medium difficulty)

Converts a string to an integer.

This problem is actually not difficult, and Last question There are many overlaps. The whole idea is to traverse the string, and then take out one character in turn. It is nothing more than considering some special situations and understanding the meaning of the topic.

After many trial and error, the meaning of the title is like this.

When traversing the string from the left, you can encounter spaces until you encounter '+' or numbers or '-', which indicates the beginning of the number to be converted. If you encounter other characters other than numbers (including spaces), you will end the traversal and output the results, regardless of whether there are numbers behind. For example, "- 32332ada2323" will output "- 32332".

If you encounter spaces or '+' or numbers or other characters before '-', you can directly output 0, such as "we1332".

If the converted number exceeds int, intMax or intMin is returned.

public int myAtoi(String str) {
        int sign = 1;
        int ans = 0, pop = 0;
        boolean hasSign = false; //Represents whether to start converting numbers
        for (int i = 0; i < str.length(); i++) {
            if (str.charAt(i) == '-' && !hasSign) {
                sign = -1;
                hasSign = true;
                continue;
            }
            if (str.charAt(i) == '+' && !hasSign) {
                sign = 1;
                hasSign = true;
                continue;
            }
            if (str.charAt(i) == ' ' && !hasSign) {
                continue;
            }

            if (str.charAt(i) >= '0' && str.charAt(i) <= '9') {
                hasSign = true;
                pop = str.charAt(i) - '0';
                 //It means to judge out of bounds with the last question, but remember to multiply by sign.
                if (ans * sign > Integer.MAX_VALUE / 10 || (ans * sign == Integer.MAX_VALUE / 10 && pop * sign > 7))
                    return 2147483647;
                if (ans * sign < Integer.MIN_VALUE / 10 || (ans * sign == Integer.MIN_VALUE / 10 && pop * sign < -8))
                    return -2147483648;
                ans = ans * 10 + pop;
            } else {
                return ans * sign;
            }
        }
        return ans * sign;
    }

Time complexity: O (n), n is the length of the string.

Space complexity: O (1).

summary

This question makes me feel a little confused. It seems that I didn't get the point of the person who wrote the question???

Question 9: Palindrome Number

Title Description (simple difficulty)

Judge whether it is a palindrome number, and a negative number is not a palindrome number.

Solution I

Turn int into a string, and then judge whether it is a palindrome string. The disadvantage is that you need additional space to store the string. Of course, the topic also tells you that this can't be done, so pass.

Solution II

stay Question 7 We wrote the algorithm of inverting int, which can of course be used here. We only need to judge that the phases before and after inversion are not equal.

Remember, when the inverted number exceeds the range of int, we return 0, so it must not be equal to the original number. At this time, it must return false, which is incorrect?

We just need to prove that if the inversion exceeds the range of int, it must not be a palindrome number.

To prove the contrary, we assume that there is a number that is out of the range of int after inversion, and it is a palindrome number.

int Max 2147483647,

Let's discuss what this number might be.

Is it possible that the overflow is caused by the highest bit greater than 2? For example, the highest bit is 3. Because it is a palindrome string, the lowest bit is 3, which will cause the highest bit before transpose to be 3, so it can't be this case.

Is it possible that the overflow caused by the second high bit is greater than 1, and the highest bit remains unchanged at this time? If the second high bit is 2, because it is a palindrome string, the one bit is 2 and the ten bit is 2, which will also lead to exceeding the maximum value of int before inversion, so it is impossible.

Similarly, the third high position, the fourth, the fifth and the left of the straight line are all the above situations, so it is impossible that the number of digits in front is too large.

In order to ensure that this number overflows, the first 5 bits must be fixed, because it is a palindrome string, so the gray number behind the straight line must be 4. At this time, no matter how many numbers are taken, it cannot overflow.

To sum up, there is no such number, so you can write code at ease.

public int reverse(int x) {
    int rev = 0;
    while (x != 0) {
        int pop = x % 10;
        x /= 10;
        if (rev > Integer.MAX_VALUE / 10)
            return 0;
        if (rev < Integer.MIN_VALUE / 10)
            return 0;
        rev = rev * 10 + pop;
    }
    return rev;
}

public boolean isPalindrome(int x) {
    if (x < 0) {
        return false;
    }
    int rev = reverse(x);
    return x == rev;
}

Time complexity: like transpose, X loops as many times as there are bits, so it is O (log (x)).

Space complexity: O (1).

Solution III

In fact, we just need to invert the right half and compare it with the left half. For example, 1221, just compare 21 transpose with 12.

public boolean isPalindrome(int x) {
    if (x < 0) {
        return false;
    }
    int digit = (int) (Math.log(x) / Math.log(10) + 1); //Total digits
    int revert = 0;
    int pop = 0;
    //Inverted right half 
    for (int i = 0; i < digit / 2; i++) { 
        pop = x % 10;
        revert = revert * 10 + pop;
        x /= 10;
    }
    if (digit % 2 == 0 && x == revert) {
        return true;
    }
    //In the case of odd numbers, x divided by 10 removes 1 bit
    if (digit % 2 != 0 && x / 10 == revert) { 
        return true;
    }
    return false;
}

Time complexity: half of the total number of bits of cycle x, so the time complexity is still O (log (x)).

Spatial complexity: O (1), constant, variable.

summary

These days are relatively simple, come on!.

Question 10: Regular Expression Matching

Title Description (difficulty)

A simple rule matching, "dot." represents any character, "asterisk *" represents the repetition of the previous character 0 or any time.

Solution one recursion

If there is no wildcard *, the difficulty of this problem will be much less. We only need one character and one character matching. If you don't know much about recursion, it is strongly recommended to take a look This article , you can sort out the idea of recursion.

  • Let's assume that there is such a function isMatch, which will tell us whether text and pattern match

    boolean isMatch ( String text, String pattern ) ;

  • Recursive scale reduction

    Matching text and pattern is equivalent to matching the first character of text and pattern and the remaining characters. To judge whether the remaining characters match, we can call the isMatch function. that is

    (pattern.charAt(0) == text.charAt(0) || pattern.charAt(0) == '.')&&isMatch(text.substring(1), pattern.substring(1));
    

Recursive exit

As the scale decreases, when pattern is empty, if text is also empty, it returns True, otherwise it returns False.

if (pattern.isEmpty()) return text.isEmpty();

To sum up, our code is

public boolean isMatch(String text, String pattern) {
        if (pattern.isEmpty()) return text.isEmpty();

        //Judge whether text is empty to prevent cross-border. If text is empty, the expression is directly judged as false, and text.charat (0) will not be executed
        boolean first_match = (!text.isEmpty() &&
                               (pattern.charAt(0) == text.charAt(0) || pattern.charAt(0) == '.'));
        return first_match && isMatch(text.substring(1), pattern.substring(1));
    }

When we consider * the reduction of recursive scale will increase the judgment of * and look at the code directly.

public boolean isMatch(String text, String pattern) {
        if (pattern.isEmpty()) return text.isEmpty();

        boolean first_match = (!text.isEmpty() &&
                               (pattern.charAt(0) == text.charAt(0) || pattern.charAt(0) == '.'));
        //Only when the length is greater than 2*
        if (pattern.length() >= 2 && pattern.charAt(1) == '*'){
            //Two cases
            //pattern skips two characters directly. Indicates that the character before * appears 0 times
            //Pattern remains unchanged, for example, text = aa, pattern = a *, the first a matches, and then the second a of text matches the first a of pattern. Indicates that * is replaced by the previous character.
            return (isMatch(text, pattern.substring(2)) ||
                    (first_match && isMatch(text.substring(1), pattern)));
        } else {
            return first_match && isMatch(text.substring(1), pattern.substring(1));
        }
    }

Time complexity: it's a little complicated. It's more complicated.

Space complexity: it's a little complicated. It'll be more.

Solution II dynamic programming

The recursion above, in order to facilitate understanding, simplifies the next idea.

In order to judge the situation of text [0, len], you need to know text [1, len]

In order to judge the situation of text [1, len], you need to know text [2, len]

In order to judge the situation of text [2, len], you need to know text [3, len]

...

In order to judge the situation of text [len - 1, len], you need to know text [len, len]

Text [len, len] it must be easy

Find out the situation of text [len, len], and you will know text [len - 1, len]

Find out the situation of text [len - 1, len], and you will know text [len - 2, len]

...

Find out the situation of text [2, len], and you will know text [1, len]

Find out the situation of text [L1, len], and you will know text [0, len]

Thus we know the situation of text [0, len] and obtain the solution of the problem.

The above is to press the stack first and then out of the stack. In fact, we can directly reverse it and omit the process of pressing the stack.

Let's start with text [len, len]

Use the case of text [len, len] to find the case of text [len - 1, len]

...

Use the case of text [2, len] to find the case of text [1, len]

Use the case of text [1, len] to find the case of text [0, len]

So as to find the solution of the problem

We use dp[i][j]dp[i][j]dp[i][j] DP [i] [j] to indicate whether text and pattern match at this time.

dp[2][2]dp[2][2]dp[2][2] is the orange part in the figure

public boolean isMatch(String text, String pattern) {
    // One more dimensional space, because when seeking dp[len - 1][j], you need to know the situation of dp[len][j],
    // If you have more than one dimension, you can write the pair dp[len - 1][j] into the loop
    boolean[][] dp = new boolean[text.length() + 1][pattern.length() + 1];
    // dp[len][len] represents whether the two empty strings match. "" and "" are true of course.
    dp[text.length()][pattern.length()] = true;

    // Decrease from len
    for (int i = text.length(); i >= 0; i--) {
        for (int j = pattern.length(); j >= 0; j--) {
            // dp[text.length()][pattern.length()] has been initialized
            if(i==text.length()&&j==pattern.length()) continue;

            boolean first_match = (i < text.length() && j < pattern.length()
                                   && (pattern.charAt(j) == text.charAt(i) || pattern.charAt(j) == '.'));
            if (j + 1 < pattern.length() && pattern.charAt(j + 1) == '*') {
                dp[i][j] = dp[i][j + 2] || first_match && dp[i + 1][j];
            } else {
                dp[i][j] = first_match && dp[i + 1][j + 1];
            }
        }
    }
    return dp[0][0];
}

Time complexity: suppose the length of text is T, the length of pattern is P, and the spatial complexity is O (TP).

Space complexity: dp space is applied, so it is O (TP). Because we only need to know the situation at i and i + 1 for each cycle, we can Question 5 Optimize as before.

    public boolean isMatch(String text, String pattern) {
        // One more dimensional space, because when seeking dp[len - 1][j], you need to know the situation of dp[len][j],
        // If you have more than one dimension, you can write the pair dp[len - 1][j] into the loop
        boolean[][] dp = new boolean[2][pattern.length() + 1]; 
        dp[text.length()%2][pattern.length()] = true;

        // Decrease from len
        for (int i = text.length(); i >= 0; i--) {
            for (int j = pattern.length(); j >= 0; j--) {
                if(i==text.length()&&j==pattern.length()) continue;
                boolean first_match = (i < text.length() && j < pattern.length()
                        && (pattern.charAt(j) == text.charAt(i) || pattern.charAt(j) == '.'));
                if (j + 1 < pattern.length() && pattern.charAt(j + 1) == '*') {
                    dp[i%2][j] = dp[i%2][j + 2] || first_match && dp[(i + 1)%2][j];
                } else {
                    dp[i%2][j] = first_match && dp[(i + 1)%2][j + 1];
                }
            }
        }
        return dp[0][0];
    }

Time complexity: constant, O (TP).

Space complexity: it mainly uses two arrays for rotation, O (P).

total

For the recursive solution of this problem, I feel that the difficulty lies in how to find the time complexity. I don't have any ideas yet. I'll add it later. On the whole, the two algorithms are easy to understand as long as we clarify our ideas.

Today, we learned the algorithm analysis of LeetCode 6-10 questions. Thank you for reading. I think it's good. Remember to collect it!

If you like, please click + pay attention

Posted by shan169 on Wed, 01 Sep 2021 15:06:27 -0700