Go-Python-Java-C-LeetCode High Decomposition Method-Fifth Week Collection

Preface

The Go language part of this problem solution is based on LeetCode-Go.
The other parts are based on my practical learning.
Personal problem solution GitHub link: LeetCode-Go-Python-Java-C
Go-Python-Java-C-LeetCode High Resolution Method-First Week Collection
Go-Python- Java-C-LeetCode high decomposition method - second week collection
Go-Python-Java-C-LeetCode high decomposition method - third week collection
Go-Python-Java-C-LeetCode high decomposition method - fourth week collection
Part of this article From online collection and personal practice. If any information is incorrect, readers are welcome to criticize and correct it. This article is only for learning and communication, not for any commercial purposes.

29. Divide Two Integers

topic

Given two integersdividendanddivisor, divide two integers without using multiplication, division and mod operator.

Return the quotient after dividingdividendbydivisor.

The integer division should truncate toward zero.

Example 1:

Input: dividend = 10, divisor = 3
Output: 3

Example 2:

Input: dividend = 7, divisor = -3
Output: -2

Note:

  • Both dividend and divisor will be 32-bit signed integers.
  • The divisor will never be 0.
  • Assume we are dealing with an environment which 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 2^31 − 1 when the
    division result overflows.

The general idea of ​​the topic

Given two integers, dividend and divisor. To divide two numbers, do not use multiplication, division and mod operators. Returns the quotient of dividend divided by
divisor.

illustrate:

  • Both the dividend and the divisor are 32-bit signed integers.
  • The divisor is not 0.
  • Assume that our environment can only store 32-bit signed integers, whose value range is [−2^31, 2^31 − 1]. In this question, if the division result overflows, 2^31 − 1 is returned.

Problem-solving ideas

  • Given the divisor and dividend, it is required to calculate the quotient after the division operation. Note that the value range is [−2^31, 2^31 − 1]. Anything exceeding the range is calculated as the boundary.
  • This problem can be solved using binary search. Request the quotient after the division operation, and use the quotient as the target to search for. The value range of the quotient is [0, dividend], so
    search from 0 to the dividend. Using bisection, find (quotient + 1) * divisor > dividend and quotient * divisor ≤ dividend or (quotient + 1) * divisor ≥ dividend and quotient * divisor
    < dividend, then the quotient is found, and in other cases, continue to divide in half. Finally, pay attention to the symbol and the Int32 value range specified in the question.
  • There are three common mistakes in writing dichotomy:
    1. low ≤ high (note that the condition for exiting the binary loop is less than or equal to)
    2. mid = low + (high-low)>>1 (to prevent overflow)
    3. low = mid + 1; high = mid - 1 (Pay attention to updating the values ​​​​of low and high. If the updates are incorrect, it will cause an endless loop).
      The following is a detailed introduction to the problem-solving ideas of each version:

Go version

The solution to the Go version is as follows:

  1. First, handle special cases, including dividend equal to 0 and divisor equal to 1, and avoid overflow situations (for example, the dividend is math.MinInt32 and the divisor is -1).

  2. Determine the sign of the final result (positive or negative) and convert both the dividend and divisor to positive numbers for bitwise operations.

  3. Use binary search to find the quotient. The goal of binary search is to find an integer x such that (x + 1) * divisor > dividendand x * divisor ≤ dividend, or (x + 1) * divisor ≥ dividendand x * divisor < dividend. This x is the quotient we are looking for.

  4. During the binary search process, be sure to update the search range and values ​​while handling possible overflow conditions.

  5. Finally, the final quotient is returned based on the sign and boundary cases.

Python version

The Python version of the problem-solving idea is as follows:

  1. Handle special cases, including dividend equal to 0 and divisor equal to 1, and avoid overflow situations (e.g., dividend is -2^31, divisor is -1).

  2. Determine the sign of the final result (positive or negative) and convert both the dividend and divisor to positive numbers for bitwise operations.

  3. Use a recursive binary search method to find the quotient. The goal of the recursive function is to find an integer x such that (x + 1) * divisor > dividendand x * divisor ≤ dividend, or (x + 1) * divisor ≥ dividendand x * divisor < dividend. This x is the quotient we are looking for.

  4. During recursion, make sure to update the search range and values ​​while handling possible overflow conditions.

  5. Finally, the final quotient is returned based on the sign and boundary cases.

Java version

The solution to the Java version is as follows:

  1. Handle special cases, including dividend equal to 0 and divisor equal to 1, and avoid overflow situations (e.g., dividend is Integer.MIN_VALUE and divisor is -1).

  2. Determine the sign of the final result (positive or negative) and convert both the dividend and divisor to positive numbers for bitwise operations.

  3. Use a recursive binary search method to find the quotient. The goal of the recursive function is to find an integer x such that (x + 1) * divisor > dividendand x * divisor ≤ dividend, or (x + 1) * divisor ≥ dividendand x * divisor < dividend. This x is the quotient we are looking for.

  4. During recursion, make sure to update the search range and values ​​while handling possible overflow conditions.

  5. Finally, the final quotient is returned based on the sign and boundary cases.

C++ version

The solution to the C++ version is as follows:

  1. Handle special cases, including dividend equals 0, divisor equals 1, and avoid overflow situations (e.g., dividend is INT_MIN, divisor is -1).

  2. Determine the sign of the final result (positive or negative) and convert both the dividend and divisor to positive numbers for bitwise operations.

  3. Use a recursive binary search method to find the quotient. The goal of the recursive function is to find an integer x such that (x + 1) * divisor > dividendand x * divisor ≤ dividend, or (x + 1) * divisor ≥ dividendand x * divisor < dividend. This x is the quotient we are looking for.

  4. During recursion, make sure to update the search range and values ​​while handling possible overflow conditions.

  5. Finally, the final quotient is returned based on the sign and boundary cases.

In short, the core idea of ​​these solutions is to use binary search to find the quotient, while handling special cases and overflow situations to ensure that the final calculation result is correct. Recursive functions are widely used in the implementation for the search process.

code

Go

func abs(x int) int {
    if x < 0 {
        return -x
    }
    return x
}

// 解法一 递归版的二分搜索
func divide(dividend int, divisor int) int {
    sign, res := -1, 0
    // low, high := 0, abs(dividend)
    if dividend == 0 {
        return 0
    }
    if divisor == 1 {
        return dividend
    }
    if dividend == math.MinInt32 && divisor == -1 {
        return math.MaxInt32
    }
    if dividend > 0 && divisor > 0 || dividend < 0 && divisor < 0 {
        sign = 1
    }
    if dividend > math.MaxInt32 {
        dividend = math.MaxInt32
    }

    // 调用二分搜索函数计算商
    res = binarySearchQuotient(0, abs(dividend), abs(divisor), abs(dividend))

    // 处理溢出情况
    if res > math.MaxInt32 {
        return sign * math.MaxInt32
    }
    if res < math.MinInt32 {
        return sign * math.MinInt32
    }
    return sign * res
}

// 二分搜索函数,用于计算商
func binarySearchQuotient(low, high, val, dividend int) int {
    quotient := low + (high-low)>>1
    if ((quotient+1)*val > dividend && quotient*val <= dividend) || ((quotient+1)*val >= dividend && quotient*val < dividend) {
        if (quotient+1)*val == dividend {
            return quotient + 1
        }
        return quotient
    }
    if (quotient+1)*val > dividend && quotient*val > dividend {
        return binarySearchQuotient(low, quotient-1, val, dividend)
    }
    if (quotient+1)*val < dividend && quotient*val < dividend {
        return binarySearchQuotient(quotient+1, high, val, dividend)
    }
    return 0
}

func abs(x int) int {
    if x < 0 {
        return -x
    }
    return x
}



// 解法二 非递归版的二分搜索
func divide(dividend int, divisor int) int {
    if dividend == math.MinInt32 && divisor == -1 {
        return math.MaxInt32
    }
    result := 0
    sign := -1
    if dividend > 0 && divisor > 0 || dividend < 0 && divisor < 0 {
        sign = 1
    }
    dividendAbs, divisorAbs := abs(dividend), abs(divisor)
    for dividendAbs >= divisorAbs {
        temp := divisorAbs
        multiplier := 1
        for temp<<1 <= dividendAbs {
            temp <<= 1
            multiplier <<= 1
        }
        dividendAbs -= temp
        result += multiplier
    }
    return sign * result
}

Python

class Solution:
    def divide(self, dividend: int, divisor: int) -> int:
        # 递归版
        def recursive_divide(dividend, divisor):
            if dividend < divisor:
                return 0
            quotient = 1
            div = divisor
            while (div + div) <= dividend:
                div += div
                quotient += quotient
            return quotient + recursive_divide(dividend - div, divisor)

        if dividend == 0:
            return 0
        if dividend == -2 ** 31 and divisor == -1:
            return 2 ** 31 - 1

        sign = 1 if (dividend > 0) == (divisor > 0) else -1
        dividend, divisor = abs(dividend), abs(divisor)

        result = recursive_divide(dividend, divisor)
        return sign * result


class Solution:

    # 非递归版
    def divide(self, dividend: int, divisor: int) -> int:
        if dividend == 0:
            return 0
        if dividend == -2 ** 31 and divisor == -1:
            return 2 ** 31 - 1

        sign = 1 if (dividend > 0) == (divisor > 0) else -1
        dividend, divisor = abs(dividend), abs(divisor)
        result = 0

        while dividend >= divisor:
            temp, m = divisor, 1
            while dividend >= (temp << 1):
                temp <<= 1
                m <<= 1
            dividend -= temp
            result += m

        return sign * result

Java

class Solution {
    public int divide(int dividend, int divisor) {
        // 递归版
        if (dividend == 0) {
            return 0;
        }
        if (dividend == Integer.MIN_VALUE && divisor == -1) {
            return Integer.MAX_VALUE;
        }

        int sign = (dividend > 0) == (divisor > 0) ? 1 : -1;
        long dvd = Math.abs((long) dividend);
        long dvs = Math.abs((long) divisor);

        int result = recursiveDivide(dvd, dvs);
        return sign * result;
    }

    private int recursiveDivide(long dividend, long divisor) {
        if (dividend < divisor) {
            return 0;
        }
        int quotient = 1;
        long div = divisor;
        while ((div + div) <= dividend) {
            div += div;
            quotient += quotient;
        }
        return quotient + recursiveDivide(dividend - div, divisor);
    }

}
class Solution {

    // 非递归版
    public int divide(int dividend, int divisor) {
        if (dividend == 0) {
            return 0;
        }
        if (dividend == Integer.MIN_VALUE && divisor == -1) {
            return Integer.MAX_VALUE;
        }

        int sign = (dividend > 0) == (divisor > 0) ? 1 : -1;
        long dvd = Math.abs((long) dividend);
        long dvs = Math.abs((long) divisor);

        int result = 0;

        while (dvd >= dvs) {
            long temp = dvs;
            int m = 1;
            while (dvd >= (temp << 1)) {
                temp <<= 1;
                m <<= 1;
            }
            dvd -= temp;
            result += m;
        }

        return sign * result;
    }
}

Cpp

class Solution {
public:
    int divide(int dividend, int divisor) {
        // 处理特殊情况
        if (dividend == 0) {
            return 0;
        }
        if (divisor == 1) {
            return dividend;
        }
        if (dividend == INT_MIN && divisor == -1) {
            return INT_MAX;
        }
        
        int sign = (dividend > 0 && divisor > 0) || (dividend < 0 && divisor < 0) ? 1 : -1;
        
        // 处理溢出情况
        if (dividend > INT_MAX) {
            dividend = INT_MAX;
        }
        
        return sign * binarySearchQuotient(0, abs((long)dividend), abs((long)divisor), abs((long)dividend));
    }
    
private:
    int binarySearchQuotient(long low, long high, long val, long dividend) {
        long quotient = low + (high - low) / 2;
        if (((quotient + 1) * val > dividend && quotient * val <= dividend) || 
            ((quotient + 1) * val >= dividend && quotient * val < dividend)) {
            if ((quotient + 1) * val == dividend) {
                return quotient + 1;
            }
            return quotient;
        }
        if ((quotient + 1) * val > dividend && quotient * val > dividend) {
            return binarySearchQuotient(low, quotient - 1, val, dividend);
        }
        if ((quotient + 1) * val < dividend && quotient * val < dividend) {
            return binarySearchQuotient(quotient + 1, high, val, dividend);
        }
        return 0;
    }
};

class Solution {
public:
    int divide(int dividend, int divisor) {
        // 处理特殊情况
        if (dividend == INT_MIN && divisor == -1) {
            return INT_MAX;
        }
        
        int result = 0;
        int sign = (dividend > 0 && divisor > 0) || (dividend < 0 && divisor < 0) ? 1 : -1;
        long dvd = abs((long)dividend);
        long dvs = abs((long)divisor);
        
        while (dvd >= dvs) {
            long temp = dvs;
            long m = 1;
            while (temp << 1 <= dvd) {
                temp <<= 1;
                m <<= 1;
            }
            dvd -= temp;
            
            // 处理溢出情况
            if (result > INT_MAX - m) {
                return sign == 1 ? INT_MAX : INT_MIN;
            }
            result += m;
        }
        
        return sign * result;
    }
};

I'll cover the detailed basics required separately when discussing each version of the solution.

Go version

  1. Go language basics : Before understanding the Go version of the solution, you need to be familiar with the basics of the Go language, including variables, functions, conditional statements, loops, etc.

  2. Recursion : The Go version of the solution uses recursion to implement binary search, so you need to understand the concept and usage of recursion.

  3. Bit operations : Bit operations are used in the Go version to handle details such as symbols, edge cases, etc. Therefore, you need to understand the bitwise operators (eg <<, >>) and the basic principles of bitwise operations in Go.

  4. Handling edge cases : Knowing how to handle edge cases is one of the keys to solving this problem, since the input and output are restricted. The smallest and largest 32-bit signed integers need to be considered.

Python version

  1. Python language basics : You need to be familiar with the basics of the Python language, including variables, functions, conditional statements, loops, and integer overflow handling.

  2. Recursive : The recursive solution in the Python version uses recursive functions to implement binary search. You need to understand how to write recursive functions and how recursion works.

  3. Bitwise operations : Bitwise operations are also used in the Python version to handle symbols and edge cases. Understanding the bitwise operators (such as <<, >>) and the basic principles of bitwise operations in Python will help you understand the solution.

Java version

  1. Java Language Basics : Before understanding the Java version of the solution, you need to be familiar with the basics of the Java language, including classes, methods, conditional statements, loops, and integer overflow handling.

  2. Recursion : The Java version of the solution uses a recursive function to implement binary search. You need to understand how to write recursive functions and how recursion works.

  3. Bit Operations : Bit operations are used in Java version to handle symbols and edge cases. Understanding the bitwise operators (such as <<, >>) and the basic principles of bitwise operations in Java will help you understand the solution.

  4. Integer overflow handling : The Java version takes integer overflow situations into account and takes measures to prevent overflows. You need to understand the characteristics of integer overflow in Java and how to deal with it.

C++ version

  1. C++ language basics : Before understanding the C++ version of the solution, you need to be familiar with the basics of the C++ language, including classes, functions, conditional statements, loops, and integer overflow handling.

  2. Recursion : The C++ version of the solution uses a recursive function to implement binary search. You need to understand how to write recursive functions and how recursion works.

  3. Bit Operations : Bit operations are used in the C++ version to handle symbols and edge cases. Understanding the bitwise operators (such as <<, >>) and the basic principles of bitwise operations in C++ will help you understand the solution.

  4. Integer overflow handling : The C++ version considers integer overflow situations and takes measures to prevent overflows. You need to understand the characteristics of integer overflow in C++ and how to deal with it.

In summary, understanding each version of the solution requires some knowledge of programming language fundamentals, recursion, bitwise operations, and integer overflow handling. In addition, understanding the special requirements and boundary conditions of the problem is also key to solving this problem.

30. Substring with Concatenation of All Words

topic

You are given a string, s, and a list of words, words, that are all of the same length. Find all starting indices of
substring(s) in s that is a concatenation of each word in words exactly once and without any intervening characters.

Example 1:

Input:
  s = "barfoothefoobarman",
  words = ["foo","bar"]
Output: [0,9]
Explanation: Substrings starting at index 0 and 9 are "barfoor" and "foobar" respectively.
The output order does not matter, returning [9,0] is fine too.

Example 2:

Input:
  s = "wordgoodgoodgoodbestword",
  words = ["word","good","best","word"]
Output: []

The general idea of ​​the topic

Given a source string s, and then a string array, it is required to find the starting subscript of a continuous string composed of various combinations of the string array in the source string. If there are more than one, they need to be output in the result. .

Problem-solving ideas

This question may seem difficult, but there are two limitations that make it not particularly difficult. 1. The string lengths in the string array are all the same. 2.
The strings in the string array are required to be connected continuously, and the order can be any permutation and combination.

The idea for solving the problem is to first store all the strings in the string array into the map
and accumulate the number of occurrences. Then scan from the beginning of the source string, and each time when judging the strings in the string array, all the strings are used up (whether the count is 0),
if all are used up, and the length is exactly the total length of any permutation and combination of the string array , record the starting index of this combination. If it does not match, continue to examine the next character of the source string until the entire source string is scanned.
Below I will introduce the problem-solving ideas for each version:

Go version :

  1. Initialize variables :

    • Get sthe length of the input string ls, wordsthe length of the word array m, and the length of the word n.
    • Initialize an empty integer slice ansto store the result.
  2. Main loop :

    • Starting from sthe first n characters of the string, loop through the starting position.
    • Create a map differthat tracks the number of occurrences of each word within the current window.
  3. Iterate over the word listwords :

    • For each word in the word list, differincrease the number of occurrences in .
    • If the number of occurrences increases and becomes 0, delete the word differfrom .
  4. Main loop :

    • Starting from the current starting position, moving the length of one word at a time n, checking whether the substring contains all words.
    • If not, continue moving the window.
    • If differdoes not contain any words, the substring contains all words, and the current starting position is added to the result.
  5. Return results : Returns the slice that stores the results ans.

Python version :

  1. Initialize variables :

    • Initialize an empty list ansto store the results.
    • Calculate the length of the word word_len, the length of the word array total_words, the element count of the word array word_count, and sthe length of the input string s_len.
  2. Loop through the starting position of a word :

    • The outer loop iterates the starting position of the word, from 0 to word_len-1.
    • Internally maintains two pointers leftand j, as well as a counter countand a word counter current_count.
  3. Iterate over the strings :

    • The inner loop iterates over the string s, starting at the current starting position and moving one word length at a time word_len.
    • In the inner loop, check whether the current substring is a valid combination of the word array.
    • If it is a valid combination, the starting position is leftadded to the results list.
  4. Return results : Returns a list of stored results ans.

Java version :

  1. Initialize variables :

    • Initialize an empty list resto store the results.
    • Create a map mapthat stores words and their occurrences.
    • Get the length of the word array m.
  2. Loop through possible word starting positions :

    • The outer loop iterates the starting position of the word, from 0 to len(words[0])-1.
  3. Iterate over the strings :

    • The inner loop iterates over the string s, starting at the current starting position and moving one word length at a time.
    • In the inner loop, check whether the current substring is a valid combination of the word array.
    • If it is a valid combination, the starting position is added to the results list res.
  4. Return results : Returns a list of stored results res.

C++ version :

  1. Initialize variables :

    • Initialize an empty vector ansto store the results.
    • Get the length of the word array mand the length of the word wordsize.
    • Create a map mpthat stores words and their occurrences.
    • Get sthe length of the input string n.
  2. Loop through possible word starting positions :

    • The outer loop iterates the starting position of the word, from 0 to wordsize-1.
  3. Iterate over the strings :

    • The inner loop iterates over the string s, starting at the current starting position and moving one word length at a time.
    • In the inner loop, check whether the current substring is a valid combination of the word array.
    • If it is a valid combination, add the starting position to the resulting vector ans.
  4. Return results : Returns a vector storing the results ans.

These are the basic problem-solving ideas for each version. They all use the sliding window technique, starting from different starting positions, gradually moving the window and checking whether the substring meets the conditions. At the same time, they also utilize data structures (such as maps or counters) to count the occurrences of words for comparison. Different programming languages ​​differ in implementation details, but the overall idea is the same.

code

Go

func findSubstring(s string, words []string) (ans []int) {
    // 获取输入字符串 `s` 的长度
    ls, m, n := len(s), len(words), len(words[0])

    // 遍历字符串 `s` 的前 n 个字符
    for i := 0; i < n && i+m*n <= ls; i++ {
        // 使用 map `differ` 来跟踪子串中每个单词的出现次数
        differ := map[string]int{}

        // 遍历单词列表 `words`
        for j := 0; j < m; j++ {
            // 将子串中的每个单词加入到 `differ` 中,并统计出现次数
            differ[s[i+j*n:i+(j+1)*n]]++
        }

        // 遍历单词列表 `words`
        for _, word := range words {
            // 减少 `differ` 中对应单词的出现次数
            differ[word]--
            // 如果出现次数减少到 0,从 `differ` 中删除这个单词
            if differ[word] == 0 {
                delete(differ, word)
            }
        }

        // 从当前位置 `i` 开始,每次移动一个单词长度 `n`,检查子串是否包含所有单词
        for start := i; start < ls-m*n+1; start += n {
            if start != i {
                // 更新 `differ`,增加新单词的出现次数,减少旧单词的出现次数
                word := s[start+(m-1)*n : start+m*n]
                differ[word]++
                if differ[word] == 0 {
                    delete(differ, word)
                }
                word = s[start-n : start]
                differ[word]--
                if differ[word] == 0 {
                    delete(differ, word)
                }
            }
            // 如果 `differ` 中不包含任何单词,说明子串包含了所有单词
            if len(differ) == 0 {
                ans = append(ans, start)
            }
        }
    }
    return
}

Python

from collections import Counter


class Solution:
    def findSubstring(self, s: str, words: List[str]) -> List[int]:
        if not s or not words:
            return []

        ans = []
        word_len = len(words[0])
        total_words = len(words)
        word_count = Counter(words)
        s_len = len(s)

        for i in range(word_len):
            left = i
            count = 0
            current_count = Counter()

            for j in range(i, s_len - word_len + 1, word_len):
                word = s[j:j + word_len]
                if word in word_count:
                    current_count[word] += 1
                    count += 1

                    while current_count[word] > word_count[word]:
                        left_word = s[left:left + word_len]
                        current_count[left_word] -= 1
                        count -= 1
                        left += word_len

                    if count == total_words:
                        ans.append(left)
                else:
                    current_count.clear()
                    count = 0
                    left = j + word_len

        return ans

Java

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class Solution {
    public List<Integer> findSubstring(String s, String[] words) {
        Map<String, Integer> map = new HashMap<>();
        
        // 将单词数组中的单词以及它们的出现次数存储在 map 中
        for (String str : words) {
            map.put(str, map.getOrDefault(str, 0) + 1);
        }
        
        int len = words[0].length(); // 单词的长度
        List<Integer> res = new ArrayList<>(); // 存储结果的列表
        
        // 遍历字符串 s 中每个可能的起始位置
        for (int i = 0; i < len; i++) {
            Map<String, Integer> newmap = new HashMap<>(); // 存储当前窗口内的单词出现次数
            int count = 0; // 记录窗口内匹配的单词数量
            
            for (int j = i; j <= s.length() - len;) {
                String cur = s.substring(j, j + len); // 当前窗口内的单词
                
                // 如果当前单词在单词数组中且未超出其出现次数限制
                if (map.containsKey(cur) && (!newmap.containsKey(cur) || newmap.get(cur) < map.get(cur))) {
                    newmap.put(cur, newmap.getOrDefault(cur, 0) + 1); // 更新窗口内单词出现次数
                    count++; // 增加匹配的单词数量
                    
                    // 如果窗口内匹配的单词数量等于单词数组的长度,表示找到一个满足条件的子串
                    if (count == words.length) {
                        res.add(j - len * (words.length - 1)); // 记录子串的起始位置
                        count--;
                        String pre = s.substring(j - len * (words.length - 1), j - len * (words.length - 2));
                        newmap.put(pre, newmap.get(pre) - 1); // 更新窗口内单词出现次数
                    }
                    
                    j += len; // 移动窗口
                } 
                // 如果当前单词不在单词数组中
                else if (!map.containsKey(cur)) {
                    count = 0;
                    newmap.clear(); // 清空窗口内的单词记录
                    j += len;
                } 
                // 如果当前单词在单词数组中但超出其出现次数限制
                else {
                    String pre = s.substring(j - len * count, j - len * (count - 1));
                    newmap.put(pre, newmap.get(pre) - 1); // 更新窗口内单词出现次数
                    count--; // 减少匹配的单词数量
                }
            }
        }
        return res; // 返回结果列表
    }
}

Cpp

class Solution {
public:
    vector<int> findSubstring(string s, vector<string>& words) {
        const int n = s.length();            // 输入字符串的长度
        const int m = words.size();          // 单词数组的大小
        const int wordsize = words.front().length(); // 单词的长度

        unordered_map<string, int> mp;       // 用于存储单词以及它们的出现次数
        for (auto &word : words)
            mp[word]++;

        vector<int> ans;                     // 存储结果的向量

        for (int i = 0; i < wordsize; ++i) { // 对于每个可能的起始位置
            unordered_map<string, int> cnt;  // 用于存储当前窗口内的单词出现次数
            int start = i;                  // 记录符合条件的字符串的起始位置

            for (int j = i; j < n; j += wordsize) { // 遍历字符串 s 中所有单词
                string word = s.substr(j, wordsize);

                if (!mp.count(word)) {       // 如果遇到不在单词数组中的单词,直接清空前面所有的计数
                    cnt.clear();
                    start = j + wordsize;
                } else {
                    cnt[word] += 1;

                    while (cnt[word] > mp[word]) { // 某个单词的计数超过了在单词数组中的出现次数,从左边减
                        cnt[s.substr(start, wordsize)]--;
                        start += wordsize;
                    }

                    if (j - start == (m - 1) * wordsize) // 如果窗口内匹配的单词数量等于单词数组的长度,表示找到一个满足条件的子串
                        ans.push_back(start);
                }
            }
        }

        return ans;
    }
};

Basic knowledge required for each version of the code.

Go version :

  1. Go language basics :

    • Understand the basic syntax of Go language, including variables, loops, conditional statements, functions, etc.
  2. String processing :

    • Learn how to use string slicing and string concatenation operations to process strings.
    • Understand how string length is calculated.
  3. Map :

    • Understand the map data structure in Go, i.e. map, and how to add, remove, and retrieve elements from a map.
    • Familiarize yourself with using mappings to implement word counting and comparisons.
  4. Loops and conditional statements :

    • Learn how to use forloops and ifconditional statements to implement iteration and conditional judgment.
  5. Slicing operation :

    • Learn how to use slicing to operate on subsets of an array or string.
    • Understand how to iterate through slices.

Python version :

  1. Python language basics :

    • Familiar with the basic syntax of Python language, including variables, loops, conditional statements, functions, etc.
  2. String processing :

    • Learn how to use string slicing and string concatenation operations to process strings.
    • Know how to get the length of a string.
  3. Counter class :

    • Understand the class in Python collections.Counter, used to count the number of occurrences of elements.
    • Know how to use Counterobjects for word counting.
  4. Loops and conditional statements :

    • Understand how to use forloops and ifconditional statements to implement iteration and conditional judgment.
  5. List :

    • Be familiar with list data structures in Python and how to use lists to store and manipulate data.

Java version :

  1. Java language basics :

    • Familiar with the basic syntax of Java language, including variables, loops, conditional statements, functions, etc.
  2. String processing :

    • Learn how to use string slicing and string concatenation operations to process strings.
    • Know how to get the length of a string.
  3. Map :

    • Understand interfaces in Java java.util.Mapand how to use them HashMapto implement mapping.
    • Know how to add, remove, and retrieve elements from a map.
  4. Loops and conditional statements :

    • Learn how to use forloops and ifconditional statements to implement iteration and conditional judgment.
  5. List :

    • Be familiar with the list data structure in Java, that is java.util.List, and how to use lists to store and manipulate data.

C++ version :

  1. C++ language basics :

    • Understand the basic syntax of C++ language, including variables, loops, conditional statements, functions, etc.
  2. String processing :

    • Learn how to use string slicing and string concatenation operations to process strings.
    • Know how to get the length of a string.
  3. STL (Standard Template Library) :

    • Understand C++ STL std::unordered_mapfor implementing mapping.
    • Know how to add, remove, and retrieve elements from a map.
  4. Loops and conditional statements :

    • Learn how to use forloops and ifconditional statements to implement iteration and conditional judgment.
  5. Vector :

    • Be familiar with the vector data structure in C++, that is std::vector, and how to use vectors to store and manipulate data.

These are the basics required to understand and write code in the different language versions provided. Each version uses similar algorithms and data structures, but the specific syntax and library functions may vary.

31. Next Permutation

topic

Implementnext permutation, which rearranges numbers into the lexicographically next greater permutation of numbers.

If such an arrangement is not possible, it must rearrange it as the lowest possible order (i.e., sorted in ascending
order).

The replacement must be**in place**and use only constant extra
memory.

Example 1:

Input: nums = [1,2,3]
Output: [1,3,2]

Example 2:

Input: nums = [3,2,1]
Output: [1,2,3]

Example 3:

Input: nums = [1,1,5]
Output: [1,5,1]

Example 4:

Input: nums = [1]
Output: [1]

Constraints:

  • 1 <= nums.length <= 100
  • 0 <= nums[i] <= 100

The general idea of ​​the topic

Implement a function to get the next permutation, the algorithm needs to rearrange the given sequence of numbers into the next larger permutation in lexicographic order. If there is no next greater permutation, rearrange the numbers into the smallest permutation (i.e. in ascending order). Must
be modified in place, only additional constant space is allowed.

Problem-solving ideas

Go version problem-solving ideas:

  1. Find the next permutation : First, we want to find the next permutation, i.e. rearrange the given sequence of numbers so that it becomes the next greater permutation in lexicographic order.

  2. Find the position of the smaller number : traverse the integer slice from right to left numsand find the first nums[i] < nums[i+1]subscript that satisfies i. This position represents the position of the smaller number.

  3. Find the position of the larger number : If found i, [i+1, n)find the first nums[i] < nums[j]subscript satisfying from right to left in the descending interval j. This position represents the position of the larger number.

  4. Swap smaller numbers and larger numbers : swap nums[i]and nums[j]. After this step is completed, the interval [i+1, n)must be a descending interval.

  5. Flip the subarray : [i+1, n)Flip the elements in the range in place so that they are in ascending order, so that the next arrangement can be generated.

Python version problem-solving ideas:

The problem-solving ideas of the Python version are similar to the Go version, except that Python lists and objects are used.

  1. Find the next permutation : Same as the Go version, we first have to find the next permutation, i.e. rearrange the given sequence of numbers so that it becomes the next larger permutation in lexicographic order.

  2. Find the position of the smaller number : traverse the list of integers from right to left numsand find the first nums[i] < nums[i+1]subscript that satisfies i, which represents the position of the smaller number.

  3. Find the position of the larger number : If found , find the first subscript satisfying from right to left iin the descending interval , which represents the position of the larger number.[i+1, n)nums[i] < nums[j]j

  4. Swap smaller numbers and larger numbers : swap nums[i]and nums[j]. After this step is completed, the interval [i+1, n)must be a descending interval.

  5. Flip the subarray : [i+1, n)Flip the elements in the range in place so that they are in ascending order, so that the next arrangement can be generated.

Java version solution ideas:

The problem-solving ideas of the Java version are similar to those of the Go and Python versions, except that Java arrays and classes are used.

  1. Find the next permutation : Same as other versions, first find the next permutation, i.e. rearrange the given sequence of numbers to make it the next greater permutation in lexicographic order.

  2. Find the position of the smaller number : Traverse the integer array from right to left numsand find the first nums[i] < nums[i+1]subscript that satisfies i, which represents the position of the smaller number.

  3. Find the position of the larger number : If found , find the first subscript satisfying from right to left iin the descending interval , which represents the position of the larger number.[i+1, n)nums[i] < nums[j]j

  4. Swap smaller numbers and larger numbers : swap nums[i]and nums[j]. After this step is completed, the interval [i+1, n)must be a descending interval.

  5. Flip the subarray : [i+1, n)Flip the elements in the range in place so that they are in ascending order, so that the next arrangement can be generated.

C++ version problem-solving ideas:

The problem-solving ideas of the C++ version are similar to those of the Go, Python and Java versions, except that C++ arrays and classes are used.

  1. Find the next permutation : Same as other versions, first find the next permutation, i.e. rearrange the given sequence of numbers to make it the next greater permutation in lexicographic order.

  2. Find the position of the smaller number : Traverse the integer array from right to left numsand find the first nums[i] < nums[i+1]subscript that satisfies i, which represents the position of the smaller number.

  3. Find the position of the larger number : If found , find the first subscript satisfying from right to left iin the descending interval , which represents the position of the larger number.[i+1, n)nums[i] < nums[j]j

  4. Swap smaller numbers and larger numbers : swap nums[i]and nums[j]. After this step is completed, the interval [i+1, n)must be a descending interval.

  5. Flip the subarray : [i+1, n)Flip the elements in the range in place so that they are in ascending order, so that the next arrangement can be generated.

code

Go

// 解法一
// 定义一个函数 nextPermutation,用于生成下一个排列
func nextPermutation(nums []int) {
    // 定义两个变量 i 和 j,并初始化为 0
    i, j := 0, 0
    // 从倒数第二个元素开始向前遍历整数切片 nums,寻找第一个满足 nums[i] < nums[i+1] 的 i
    for i = len(nums) - 2; i >= 0; i-- {
        if nums[i] < nums[i+1] {
            break
        }
    }
    // 如果找到了 i,表示存在下一个排列
    if i >= 0 {
        // 从最后一个元素开始向前遍历整数切片 nums,寻找第一个满足 nums[j] > nums[i] 的 j
        for j = len(nums) - 1; j > i; j-- {
            if nums[j] > nums[i] {
                break
            }
        }
        // 交换 nums[i] 和 nums[j]
        swap(&nums, i, j)
    }
    // 对从 i+1 到末尾的部分进行翻转,以获得下一个排列
    reverse(&nums, i+1, len(nums)-1)
}

// 定义一个函数 reverse,用于翻转整数切片 nums 中从位置 i 到 j 的元素
func reverse(nums *[]int, i, j int) {
    // 使用双指针将元素从两端向中间逐个交换
    for i < j {
        swap(nums, i, j)
        i++
        j--
    }
}

// 定义一个函数 swap,用于交换整数切片 nums 中位置 i 和 j 的元素
func swap(nums *[]int, i, j int) {
    // 使用指针访问和交换切片中的元素值
    (*nums)[i], (*nums)[j] = (*nums)[j], (*nums)[i]
}

Python

class Solution:
    def nextPermutation(self, nums: List[int]) -> None:
        """
        Do not return anything, modify nums in-place instead.
        """
        # Step 1: Find the first decreasing element from right to left (i)
        i = len(nums) - 2
        while i >= 0 and nums[i] >= nums[i + 1]:
            i -= 1
        
        # Step 2: Find the first element larger than nums[i] from right to left (j)
        if i >= 0:
            j = len(nums) - 1
            while j > i and nums[j] <= nums[i]:
                j -= 1
            # Step 3: Swap nums[i] and nums[j]
            nums[i], nums[j] = nums[j], nums[i]
        
        # Step 4: Reverse the subarray to the right of i
        left, right = i + 1, len(nums) - 1
        while left < right:
            nums[left], nums[right] = nums[right], nums[left]
            left += 1
            right -= 1

Java

class Solution {
    public void nextPermutation(int[] nums) {
        // Step 1: Find the first decreasing element from right to left (i)
        int i = nums.length - 2;
        while (i >= 0 && nums[i] >= nums[i + 1]) {
            i--;
        }
        
        // Step 2: Find the first element larger than nums[i] from right to left (j)
        if (i >= 0) {
            int j = nums.length - 1;
            while (j > i && nums[j] <= nums[i]) {
                j--;
            }
            // Step 3: Swap nums[i] and nums[j]
            int temp = nums[i];
            nums[i] = nums[j];
            nums[j] = temp;
        }
        
        // Step 4: Reverse the subarray to the right of i
        int left = i + 1, right = nums.length - 1;
        while (left < right) {
            int temp = nums[left];
            nums[left] = nums[right];
            nums[right] = temp;
            left++;
            right--;
        }
    }
}

Cpp

class Solution {
public:
    void nextPermutation(vector<int>& nums) {
        // Step 1: Find the first decreasing element from right to left (i)
        int i = nums.size() - 2;
        while (i >= 0 && nums[i] >= nums[i + 1]) {
            i--;
        }
        
        // Step 2: Find the first element larger than nums[i] from right to left (j)
        if (i >= 0) {
            int j = nums.size() - 1;
            while (j > i && nums[j] <= nums[i]) {
                j--;
            }
            // Step 3: Swap nums[i] and nums[j]
            swap(nums[i], nums[j]);
        }
        
        // Step 4: Reverse the subarray to the right of i
        int left = i + 1, right = nums.size() - 1;
        while (left < right) {
            swap(nums[left], nums[right]);
            left++;
            right--;
        }
    }
};

basic knowledge

Go version:

  1. Go language basics : Understand the basic syntax, data types, function definition and use, slices and other related knowledge of the Go language.

  2. Pointers : Learn about the concept of pointers and how to use them in Go.

  3. Functions : Understand how to define and call functions, as well as their parameters and return values.

  4. Array slicing : Understand the concept of slicing and slicing operations in Go, including the creation and modification of slices.

Python version:

  1. Python Basics : Understand Python's basic syntax, lists, conditional statements, and loops.

  2. Classes and Objects : Understand how to define classes and create objects (define methods in Python classes).

  3. List Operations : Learn how to manipulate lists, including indexing, slicing, iterating, and modifying list elements.

Java version:

  1. Java Basics : Familiar with Java's basic syntax, arrays, loops and conditional statements.

  2. Classes and Methods : Learn how to define classes and methods, and how to use member variables and methods in classes.

  3. Array operations : Be familiar with the creation, traversal and modification operations of arrays in Java.

C++ version:

  1. C++ Basics : Understand the basic syntax, arrays, loops and conditional statements of C++.

  2. Functions : Understand how to define and call functions, as well as their parameters and return values.

  3. Array Operations : Learn how to manipulate arrays, including indexing, traversing, and modifying array elements.

32. Longest Valid Parentheses

topic

Given a string containing just the characters'('and')', find the length of the longest valid (well-formed)
parentheses substring.

Example 1:

Input: s = "(()"
Output: 2
Explanation: The longest valid parentheses substring is "()".

Example 2:

Input: s = ")()())"
Output: 4
Explanation: The longest valid parentheses substring is "()()".

Example 3:

Input: s = ""
Output: 0

Constraints:

  • 0 <= s.length <= 3 * 104
  • s[i]is'(', or')'.

The general idea of ​​the topic

Given a string containing only '(' and ')', find the length of the longest valid (well-formed and consecutive) bracket substring.

Problem-solving ideas

The following is a detailed introduction to the problem-solving ideas for each version:

Go version problem-solving ideas:

The Go version's problem-solving idea is to use the stack to handle the bracket matching problem, and use a double pointer method to calculate the length of the longest valid bracket substring. The main steps are as follows:

  1. Define a helper function max(a, b int) intthat returns the larger of two integers.

  2. Initialization left, rightand maxLengthvariables are used to record the number of left brackets, the number of right brackets, and the length of the longest valid bracket substring respectively. The initial values ​​are 0.

  3. Iterate over the input string s, from left to right:

    • If the current character is '(', increase leftthe counter.
    • If the current character is ')', increment rightthe counter.
    • If leftthe and rightcounters are equal, it means that a valid bracket substring is found, the length of the current valid bracket substring is calculated, and updated maxLength.
    • If rightis greater than left, resets the leftand rightcounters to 0 because the current bracket string cannot be matched.
  4. Reset the left, rightand maxLengthvariables to 0, and perform another right-to-left traversal to handle the situation where the number of right brackets is greater than the number of left brackets.

  5. Returns maxLengththe length of the longest valid bracket substring.

Python version problem-solving ideas:

The problem-solving idea of ​​the Python version is similar to that of the Go version. It also uses the stack to handle the bracket matching problem, and uses a double pointer method to calculate the length of the longest valid bracket substring. The main steps are as follows:

  1. Define a helper function max(a, b)that returns the larger of two numbers.

  2. Initialization left, rightand maxLengthvariables are used to record the number of left brackets, the number of right brackets, and the length of the longest valid bracket substring respectively. The initial values ​​are 0.

  3. Iterate over the input string s, from left to right:

    • If the current character is '(', increase leftthe counter.
    • If the current character is ')', increment rightthe counter.
    • If leftthe and rightcounters are equal, it means that a valid bracket substring is found, the length of the current valid bracket substring is calculated, and updated maxLength.
    • If rightis greater than left, resets the leftand rightcounters to 0 because the current bracket string cannot be matched.
  4. Reset the left, rightand maxLengthvariables to 0, and perform another right-to-left traversal to handle the situation where the number of right brackets is greater than the number of left brackets.

  5. Returns maxLengththe length of the longest valid bracket substring.

Java version solution ideas:

The problem-solving idea of ​​the Java version is similar to that of the Go and Python versions. It also uses the stack to handle the bracket matching problem, and uses a double pointer method to calculate the length of the longest valid bracket substring. The main steps are as follows:

  1. Initialization left, rightand maxLengthvariables are used to record the number of left brackets, the number of right brackets, and the length of the longest valid bracket substring respectively. The initial values ​​are 0.

  2. Iterate over the input string s, from left to right:

    • If the current character is '(', increase leftthe counter.
    • If the current character is ')', increment rightthe counter.
    • If leftthe and rightcounters are equal, it means that a valid bracket substring is found, the length of the current valid bracket substring is calculated, and updated maxLength.
    • If rightis greater than left, resets the leftand rightcounters to 0 because the current bracket string cannot be matched.
  3. Reset the left, rightand maxLengthvariables to 0, and perform another right-to-left traversal to handle the situation where the number of right brackets is greater than the number of left brackets.

  4. Returns maxLengththe length of the longest valid bracket substring.

C++ version problem-solving ideas:

The problem-solving idea of ​​the C++ version is similar to that of the Go, Python and Java versions. It also uses the stack to handle the bracket matching problem, and uses a double pointer method to calculate the length of the longest valid bracket substring. The main steps are as follows:

  1. Initialization left, rightand maxLengthvariables are used to record the number of left brackets, the number of right brackets, and the length of the longest valid bracket substring respectively. The initial values ​​are 0.

  2. Iterate over the input string s, from left to right:

    • If the current character is '(', increase leftthe counter.
    • If the current character is ')', increment rightthe counter.
    • If leftthe and rightcounters are equal, it means that a valid bracket substring is found, the length of the current valid bracket substring is calculated, and updated maxLength.
    • If rightis greater than left, resets the leftand rightcounters to 0 because the current bracket string cannot be matched.
  3. Reset the left, rightand maxLengthvariables to 0, and perform another right-to-left traversal to handle the situation where the number of right brackets is greater than the number of left brackets.

  4. Returns maxLengththe length of the longest valid bracket substring.

code

Go

func max(a, b int) int {
    // 返回两个整数中的较大值
    if a > b {
        return a
    }
    return b
}

// 解法二 双指针
func longestValidParentheses(s string) int {
    // 初始化左右指针和最大有效括号子串长度
    left, right, maxLength := 0, 0, 0
    for i := 0; i < len(s); i++ {
        // 如果当前字符是左括号 '(',增加左括号计数
        if s[i] == '(' {
            left++
        } else {
            // 如果当前字符是右括号 ')',增加右括号计数
            right++
        }
        // 如果左右括号计数相等,说明找到了一个有效的括号子串
        if left == right {
            // 计算当前有效括号子串的长度并更新最大长度
            maxLength = max(maxLength, 2*right)
        } else if right > left {
            // 如果右括号计数大于左括号计数,重置左右指针
            left, right = 0, 0
        }
    }
    // 重置左右指针
    left, right = 0, 0
    for i := len(s) - 1; i >= 0; i-- {
        // 从右向左遍历字符串,处理与上面相同的逻辑
        if s[i] == '(' {
            left++
        } else {
            right++
        }
        if left == right {
            maxLength = max(maxLength, 2*left)
        } else if left > right {
            left, right = 0, 0
        }
    }
    // 返回最大有效括号子串的长度
    return maxLength
}

Python

class Solution:
    def longestValidParentheses(self, s: str) -> int:
        def max(a, b):
            return a if a > b else b

        left, right, maxLength = 0, 0, 0

        # 从左向右遍历字符串
        for char in s:
            if char == '(':
                left += 1
            else:
                right += 1

            if left == right:
                maxLength = max(maxLength, 2 * right)
            elif right > left:
                left, right = 0, 0

        left, right = 0, 0

        # 从右向左遍历字符串
        for i in range(len(s) - 1, -1, -1):
            char = s[i]
            if char == '(':
                left += 1
            else:
                right += 1

            if left == right:
                maxLength = max(maxLength, 2 * left)
            elif left > right:
                left, right = 0, 0

        return maxLength

Java

class Solution {
    public int longestValidParentheses(String s) {
        int left = 0, right = 0, maxLength = 0;

        // 从左向右遍历字符串
        for (char c : s.toCharArray()) {
            if (c == '(') {
                left++;
            } else {
                right++;
            }

            if (left == right) {
                maxLength = Math.max(maxLength, 2 * right);
            } else if (right > left) {
                left = 0;
                right = 0;
            }
        }

        left = 0;
        right = 0;

        // 从右向左遍历字符串
        for (int i = s.length() - 1; i >= 0; i--) {
            char c = s.charAt(i);
            if (c == '(') {
                left++;
            } else {
                right++;
            }

            if (left == right) {
                maxLength = Math.max(maxLength, 2 * left);
            } else if (left > right) {
                left = 0;
                right = 0;
            }
        }

        return maxLength;
    }
}

Cpp

class Solution {
public:
    int longestValidParentheses(string s) {
        int left = 0, right = 0, maxLength = 0;

        // 从左向右遍历字符串
        for (char c : s) {
            if (c == '(') {
                left++;
            } else {
                right++;
            }

            if (left == right) {
                maxLength = max(maxLength, 2 * right);
            } else if (right > left) {
                left = 0;
                right = 0;
            }
        }

        left = 0;
        right = 0;

        // 从右向左遍历字符串
        for (int i = s.length() - 1; i >= 0; i--) {
            char c = s[i];
            if (c == '(') {
                left++;
            } else {
                right++;
            }

            if (left == right) {
                maxLength = max(maxLength, 2 * left);
            } else if (left > right) {
                left = 0;
                right = 0;
            }
        }

        return maxLength;
    }
};

Go version:

  1. Go language basics :

    • Variable declaration and initialization
    • Loop (for loop)
    • Conditional statement (if-else)
    • Function declaration and calling
    • Basic operations on arrays and slices
  2. The concept of stack :

    • Slices can be used in Go to simulate the behavior of the stack

Python version:

  1. Python language basics :

    • Variable declaration and initialization
    • Loop (for loop)
    • Conditional statement (if-else)
    • Function declaration and calling
    • Basic operations on strings
  2. The concept of stack :

    • Lists can be used in Python to simulate the behavior of stacks

Java version:

  1. Java language basics :

    • Concepts of classes and objects
    • Method declaration and invocation
    • Loop (for loop)
    • Conditional statement (if-else)
    • Basic operations on strings
  2. The concept of stack :

    • Collection classes (such as ArrayList or LinkedList) can be used in Java to simulate the behavior of the stack

C++ version:

  1. C++ language basics :

    • Variable declaration and initialization
    • Function declaration and calling
    • Loop (for loop)
    • Conditional statement (if-else)
    • Basic operations on strings
  2. The concept of stack :

    • In C++, you can use containers in the standard library (such as std::vector or std::deque) to simulate the behavior of the stack

33. Search in Rotated Sorted Array

topic

Suppose an array sorted in ascending order is rotated at some pivot unknown to you beforehand.

(i.e., [0,1,2,4,5,6,7] might become [4,5,6,7,0,1,2]).

You are given a target value to search. If found in the array return its index, otherwise return -1.

You may assume no duplicate exists in the array.

Your algorithm’s runtime complexity must be in the order of O(log n).

Example 1:

Input: nums = [4,5,6,7,0,1,2], target = 0
Output: 4

Example 2:

Input: nums = [4,5,6,7,0,1,2], target = 3
Output: -1

The general idea of ​​the topic

Suppose an array sorted in ascending order is rotated at some point unknown in advance. (For example, the array [0,1,2,4,5,6,7] might become [4,5,6,7,0,1,2]). Searches for a given target value and returns its index if it exists in the array, otherwise -1 is returned. You can assume that there are no duplicate elements in the array.

The time complexity of your algorithm must be O(log n) level.

Problem-solving ideas

The following will introduce the problem-solving ideas for each version:

Go version problem-solving ideas:

  1. Initialize two pointers lowand high, pointing to the beginning and end of the array respectively.
  2. Use a binary search loop to search for the target value.
  3. In each iteration, the index of the intermediate element is calculated mid.
  4. Check the relationship between the middle element and the target value:
    • If the middle element is equal to the target value, the index of the middle element is returned directly.
    • If the middle element is larger than the leftmost element (indicating that the left half is ordered), compare the size relationship between the target value and the middle element:
      • If the target value is greater than the middle element and less than or equal to the rightmost element, it means that the target value is in the right half of the ordered part and is updated low = mid + 1.
      • Otherwise, the target value is updated in the unordered part of the left half of the segment high = mid - 1.
    • If the middle element is less than or equal to the rightmost element (indicating that the right half is ordered), compare the size relationship between the target value and the middle element:
      • If the target value is greater than the middle element and less than or equal to the rightmost element, it means that the target value is in the right half of the ordered part and is updated low = mid + 1.
      • Otherwise, the target value is updated in the unordered part of the left half of the segment high = mid - 1.
    • If the middle element is equal to the leftmost element, it indicates that there may be duplicate elements, and the leftmost pointer can be lowmoved one position to the right.
    • If the middle element is equal to the rightmost element, it indicates that there may be duplicate elements, and the rightmost pointer can be highmoved one position to the left.
  5. Repeat steps 3 and 4 until is lowgreater than high, if the target value is not found at this time, -1 is returned.

Python version problem-solving ideas:

  1. Initialize two pointers lowand high, pointing to the beginning and end of the array respectively.
  2. Use a binary search loop to search for the target value.
  3. In each iteration, the index of the intermediate element is calculated mid.
  4. Check the relationship between the middle element and the target value:
    • If the middle element is equal to the target value, the index of the middle element is returned directly.
    • If the middle element is larger than the leftmost element (indicating that the left half is ordered), compare the size relationship between the target value and the middle element:
      • If the target value is greater than the middle element and less than or equal to the rightmost element, it means that the target value is in the right half of the ordered part and is updated low = mid + 1.
      • Otherwise, the target value is updated in the unordered part of the left half of the segment high = mid - 1.
    • If the middle element is less than or equal to the rightmost element (indicating that the right half is ordered), compare the size relationship between the target value and the middle element:
      • If the target value is greater than the middle element and less than or equal to the rightmost element, it means that the target value is in the right half of the ordered part and is updated low = mid + 1.
      • Otherwise, the target value is updated in the unordered part of the left half of the segment high = mid - 1.
    • If the middle element is equal to the leftmost element, it indicates that there may be duplicate elements, and the leftmost pointer can be lowmoved one position to the right.
    • If the middle element is equal to the rightmost element, it indicates that there may be duplicate elements, and the rightmost pointer can be highmoved one position to the left.
  5. Repeat steps 3 and 4 until is lowgreater than high, if the target value is not found at this time, -1 is returned.

Java version solution ideas:

  1. Initialize two pointers lowand high, pointing to the beginning and end of the array respectively.
  2. Use a binary search loop to search for the target value.
  3. In each iteration, the index of the intermediate element is calculated mid.
  4. Check the relationship between the middle element and the target value:
    • If the middle element is equal to the target value, the index of the middle element is returned directly.
    • If the middle element is larger than the leftmost element (indicating that the left half is ordered), compare the size relationship between the target value and the middle element:
      • If the target value is greater than the middle element and less than or equal to the rightmost element, it means that the target value is in the right half of the ordered part and is updated low = mid + 1.
      • Otherwise, the target value is updated in the unordered part of the left half of the segment high = mid - 1.
    • If the middle element is less than or equal to the rightmost element (indicating that the right half is ordered), compare the size relationship between the target value and the middle element:
      • If the target value is greater than the middle element and less than or equal to the rightmost element, it means that the target value is in the right half of the ordered part and is updated low = mid + 1.
      • Otherwise, the target value is updated in the unordered part of the left half of the segment high = mid - 1.
    • If the middle element is equal to the leftmost element, it indicates that there may be duplicate elements, and the leftmost pointer can be lowmoved one position to the right.
    • If the middle element is equal to the rightmost element, it indicates that there may be duplicate elements, and the rightmost pointer can be highmoved one position to the left.
  5. Repeat steps 3 and 4 until is lowgreater than high, if the target value is not found at this time, -1 is returned.

C++ version problem-solving ideas:

  1. Initialize two pointers lowand high, pointing to the beginning and end of the array respectively.
  2. Use a binary search loop to search for the target value.
  3. In each iteration, the index of the intermediate element is calculated mid.
  4. Check the relationship between the middle element and the target value:
    • If the middle element is equal to the target value, the index of the middle element is returned directly.
    • If the middle element is larger than the leftmost element (indicating that the left half is ordered), compare the size relationship between the target value and the middle element:
      • If the target value is greater than the middle element and less than or equal to the rightmost element, it means that the target value is in the right half of the ordered part and is updated low = mid + 1.
      • Otherwise, the target value is updated in the unordered part of the left half of the segment high = mid - 1.
    • If the middle element is less than or equal to the rightmost element (indicating that the right half is ordered), compare the size relationship between the target value and the middle element:
      • If the target value is greater than the middle element and less than or equal to the rightmost element, it means that the target value is in the right half of the ordered part and is updated low = mid + 1.
      • Otherwise, the target value is updated in the unordered part of the left half of the segment high = mid - 1.
    • If the middle element is equal to the leftmost element, it indicates that there may be duplicate elements, and the leftmost pointer can be lowmoved one position to the right.
    • If the middle element is equal to the rightmost element, it indicates that there may be duplicate elements, and the rightmost pointer can be highmoved one position to the left.
  5. Repeat steps 3 and 4 until is lowgreater than high, if the target value is not found at this time, -1 is returned.

The problem-solving ideas of these four versions are basically the same. They all use a variant of binary search to search for the target value in the rotating ordered array. The key is to correctly handle the update of pointers and the processing of boundary conditions in different situations. The time complexity of the algorithm is O(log n) since each iteration halves the search range. Different versions of the code are implemented differently, but the core logic is similar.

code

Go

func search(nums []int, target int) int {
    // 检查数组是否为空,如果是空数组则直接返回-1
    if len(nums) == 0 {
        return -1
    }
    // 初始化两个指针,分别指向数组的开头和结尾
    low, high := 0, len(nums)-1
    // 使用二分查找的循环来搜索目标值
    for low <= high {
        // 计算中间元素的索引
        mid := low + (high-low)>>1
        // 如果中间元素等于目标值,则直接返回中间元素的索引
        if nums[mid] == target {
            return mid
        } else if nums[mid] > nums[low] { // 如果中间元素在数值大的一部分区间里
            // 检查目标值是否在左半部分区间内,如果是则更新高指针
            if nums[low] <= target && target < nums[mid] {
                high = mid - 1
            } else {
                // 否则更新低指针
                low = mid + 1
            }
        } else if nums[mid] < nums[high] { // 如果中间元素在数值小的一部分区间里
            // 检查目标值是否在右半部分区间内,如果是则更新低指针
            if nums[mid] < target && target <= nums[high] {
                low = mid + 1
            } else {
                // 否则更新高指针
                high = mid - 1
            }
        } else {
            // 处理中间元素等于边界元素的情况,移动边界指针以去除重复元素
            if nums[low] == nums[mid] {
                low++
            }
            if nums[high] == nums[mid] {
                high--
            }
        }
    }
    // 如果未找到目标值,则返回-1
    return -1
}

Python

class Solution:
    def search(self, nums, target: int):
        # 如果数组为空,直接返回-1
        if len(nums) == 0:
            return -1
        # 如果数组只有一个元素,分两种情况判断
        elif len(nums) == 1:
            if nums[0] != target:
                return -1
            else:
                return 0
        
        # 找到旋转点的位置
        for i in range(len(nums) - 1):
            if nums[i] > nums[i + 1]:
                flag = i + 1
                break
        
        # 在旋转点左边进行二分查找
        left = self.binary_search(nums, target, 0, flag - 1)
        if left != -1:
            return left
        else:
            # 如果左边没有找到,就在旋转点右边进行二分查找
            right = self.binary_search(nums, target, flag, len(nums) - 1)
            if right == -1:
                return -1
            else:
                return right
    
    def binary_search(self, nums, target, left, right):
        l, r = left, right
        while l <= r:
            mid = l + (r - l) // 2
            if nums[mid] == target:
                return mid
            elif nums[mid] < target:
                l = mid + 1
            else:
                r = mid - 1
        return -1

Java

class Solution {
    public int search(int[] nums, int target) {
        if (nums == null || nums.length == 0) {
            return -1;
        }
        
        int low = 0, high = nums.length - 1;
        
        while (low <= high) {
            int mid = low + (high - low) / 2;
            
            if (nums[mid] == target) {
                return mid;
            } else if (nums[mid] > nums[low]) {
                if (nums[low] <= target && target < nums[mid]) {
                    high = mid - 1;
                } else {
                    low = mid + 1;
                }
            } else if (nums[mid] < nums[high]) {
                if (nums[mid] < target && target <= nums[high]) {
                    low = mid + 1;
                } else {
                    high = mid - 1;
                }
            } else {
                if (nums[low] == nums[mid]) {
                    low++;
                }
                if (nums[high] == nums[mid]) {
                    high--;
                }
            }
        }
        
        return -1;
    }
}

Cpp

class Solution {
public:
    int search(vector<int>& nums, int target) {
        if (nums.empty()) {
            return -1;
        }
        
        int low = 0, high = nums.size() - 1;
        
        while (low <= high) {
            int mid = low + (high - low) / 2;
            
            if (nums[mid] == target) {
                return mid;
            } else if (nums[mid] > nums[low]) {
                if (nums[low] <= target && target < nums[mid]) {
                    high = mid - 1;
                } else {
                    low = mid + 1;
                }
            } else if (nums[mid] < nums[high]) {
                if (nums[mid] < target && target <= nums[high]) {
                    low = mid + 1;
                } else {
                    high = mid - 1;
                }
            } else {
                if (nums[low] == nums[mid]) {
                    low++;
                }
                if (nums[high] == nums[mid]) {
                    high--;
                }
            }
        }
        
        return -1;
    }
};

When implementing algorithms in different programming languages, you need to master the following basic knowledge:

Go version:

  1. Function Definition and Calling : Learn how to define and call functions, including their parameters and return values.

  2. Arrays vs. Slices : There are no traditional arrays in Go, instead slices are used to handle dynamic arrays. You need to know how to declare, initialize, and manipulate slices.

  3. Loops and Conditional Statements : Learn how to use the forand ifstatements to implement loops and conditional judgments.

  4. Binary search : Understand the principles and implementation methods of binary search, including how to calculate the index of the intermediate element and update the pointer.

  5. Algorithmic Complexity : Understand the concept of algorithmic complexity, including time complexity and space complexity, and understand how to analyze the performance of an algorithm.

Python version:

  1. Classes and Methods : Learn how to define classes and class methods, and how to create instances of classes.

  2. Lists : Lists in Python are similar to dynamic arrays, requiring knowledge of how to declare, initialize, and manipulate lists.

  3. Loops and Conditional Statements : Learn how to use the forand ifstatements to implement loops and conditional judgments.

  4. Binary search : Understand the principles and implementation methods of binary search, including how to calculate the index of the intermediate element and update the pointer.

  5. Functional Recursion : Understand the concept of functional recursion and how to solve problems in recursion.

Java version:

  1. Classes and Methods : Learn how to define classes and class methods, and how to create instances of classes.

  2. Arrays : There are static arrays in Java and you need to know how to declare, initialize and manipulate arrays.

  3. Loops and Conditional Statements : Learn how to use the forand ifstatements to implement loops and conditional judgments.

  4. Binary search : Understand the principles and implementation methods of binary search, including how to calculate the index of the intermediate element and update the pointer.

  5. Algorithmic Complexity : Understand the concept of algorithmic complexity, including time complexity and space complexity, and understand how to analyze the performance of an algorithm.

C++ version:

  1. Classes and Methods : Learn how to define classes and class methods, and how to create instances of classes.

  2. Vectors : Vectors in C++ are similar to dynamic arrays, requiring knowledge of how to declare, initialize, and manipulate vectors.

  3. Loops and Conditional Statements : Learn how to use the forand ifstatements to implement loops and conditional judgments.

  4. Binary search : Understand the principles and implementation methods of binary search, including how to calculate the index of the intermediate element and update the pointer.

  5. Algorithmic Complexity : Understand the concept of algorithmic complexity, including time complexity and space complexity, and understand how to analyze the performance of an algorithm.

The above is the basic knowledge required to implement the search rotation sorting array algorithm, including knowledge of language features, data structure operations, loops and conditional judgments, binary search algorithms and algorithm complexity analysis. Different programming languages ​​have different syntax and library functions, but the core algorithm ideas and logic are similar.

34. Find First and Last Position of Element in Sorted Array

topic

Given an array of integers nums sorted in ascending order, find the starting and ending position of a given target value.

Your algorithm’s runtime complexity must be in the order of O(log n).

If the target is not found in the array, return [-1, -1].

Example 1:

Input: nums = [5,7,7,8,8,10], target = 8
Output: [3,4]

Example 2:

Input: nums = [5,7,7,8,8,10], target = 6
Output: [-1,-1]

The general idea of ​​the topic

Given an array of integers nums arranged in ascending order, and a target value target. Find the start and end position of the given target value in the array. The time complexity of your algorithm must be O(log n) level. If the target value does not exist in the array, [-1, -1] is returned.

Problem-solving ideas

Go version of problem-solving ideas:

  1. First, initialize two pointers l_ptrand r_ptr, which point to the start and end of the array respectively.

  2. Initialize an integer slice ansto store the range of target values, with an initial value of [-1, -1].

  3. Using the idea of ​​binary search, the search range is continuously narrowed in the loop until the range of the target value is found or the target value is determined not to exist.

  4. In each iteration, the index of the intermediate position is calculated mid.

  5. If the middle element is equal to the target value, update ans[0]and ans[1]are mid. Then, by looping to the left and right, the first and last positions of the target values ​​are found.

  6. If the element in the middle position is larger than the target value, move the right pointer r_ptrto mid - 1to narrow the search range to the left half.

  7. If the element in the middle position is smaller than the target value, move the left pointer to l_ptrto mid + 1narrow the search range to the right half.

  8. Finally, an array containing the target value range is returned ans.

Python version of problem-solving ideas:

The problem-solving ideas of the Python version are similar to the Go version, but the structure of functions and modules is used to organize the code:

  1. find_leftFirst, two auxiliary functions and are defined find_right, which are used to find the left and right boundaries of the target value respectively.

  2. In the main function searchRange, initialize two integers to store the left and right boundaries of the target value. These values ​​are found by calling helper functions.

  3. The auxiliary functions find_leftand find_rightuse the idea of ​​binary search to continuously narrow the search range until the boundary of the target value is found or the target value is determined not to exist.

  4. When the left boundary is found, continue searching to the right and find the right boundary.

  5. Finally, a result list containing the target value range is returned.

Java version of problem-solving ideas:

The problem-solving ideas of the Java version are similar to the Go version, but the structure of classes and methods is used to organize the code:

  1. In Solutionthe class, a method is defined searchRangeto solve the problem.

  2. In searchRangethe method, initialize two integers that store the left and right boundaries of the target value.

  3. Use whilea loop to narrow the search range until you find the range of the target value or determine that the target value does not exist.

  4. In each iteration, the index of the intermediate position is calculated mid.

  5. If the element at the middle position is equal to the target value, update the left and right boundaries, and then expand left and right by looping to find the first and last positions of the target value.

  6. If the element in the middle position is greater than the target value, narrow the search range to the left half and move the right pointer r_ptrto mid - 1.

  7. If the element in the middle position is smaller than the target value, narrow the search range to the right half and move the left pointer l_ptrto mid + 1.

  8. Finally, an array of integers containing the target range of values ​​is returned.

C++ version of problem-solving ideas:

The problem-solving ideas of the C++ version are similar to the Java version, but the structure of classes and methods is used to organize the code:

  1. In Solutionthe class, a method is defined searchRangeto solve the problem.

  2. In searchRangethe method, initialize two integers that store the left and right boundaries of the target value.

  3. Use whilea loop to narrow the search range until you find the range of the target value or determine that the target value does not exist.

  4. In each iteration, the index of the intermediate position is calculated mid.

  5. If the element at the middle position is equal to the target value, update the left and right boundaries, and then expand left and right by looping to find the first and last positions of the target value.

  6. If the element in the middle position is greater than the target value, narrow the search range to the left half and move the right pointer r_ptrto mid - 1.

  7. If the element in the middle position is smaller than the target value, narrow the search range to the right half and move the left pointer l_ptrto mid.

  8. Finally, an array of integers containing the target range of values ​​is returned.

These are detailed descriptions of the solution ideas for each version, hopefully this will help you better understand how each solution works. If you have any further questions, please feel free to ask.

code

Go

func searchRange(nums []int, target int) []int {
    // 获取数组的长度
    n := len(nums)
    // 初始化左指针为 0
    l_prt := 0
    // 初始化右指针为数组长度减一
    r_prt := n - 1
    
    // 初始化答案数组,用于存储目标值的范围
    ans := []int{-1, -1}  
    
    // 在左指针小于等于右指针的条件下进行循环
    for l_prt <= r_prt {
        // 计算中间位置的索引
        mid := ((r_prt - l_prt) >> 1) + l_prt
        
        // 如果中间位置的元素等于目标值
        if nums[mid] == target {
            // 更新答案的左边界和右边界为中间位置
            ans[0] = mid
            ans[1] = mid
            
            // 从中间位置向左扩展,找到目标值的第一个位置
            for ans[0] > 0 && nums[ans[0]-1] == target {
                ans[0]--
            }
            
            // 从中间位置向右扩展,找到目标值的最后一个位置
            for ans[1] < n - 1 && nums[ans[1] + 1] == target {
                ans[1]++
            }
            
            // 跳出循环,因为已经找到了目标值的范围
            break
        } else if nums[mid] > target {
            // 如果中间位置的元素大于目标值,缩小搜索范围到左半部分
            r_prt = mid - 1
        } else {
            // 如果中间位置的元素小于目标值,缩小搜索范围到右半部分
            l_prt = mid + 1
        }
    }
    
    // 返回包含目标值范围的答案数组
    return ans
}

Python

class Solution:
    def searchRange(self, nums: List[int], target: int) -> List[int]:
        # 辅助函数:查找目标值的左边界
        def find_left():
            start, end = 0, len(nums) - 1
            res = -1  # 初始化结果为-1,表示未找到
            while start <= end:
                mid = (start + end) // 2  # 计算中间位置的索引
                
                if nums[mid] < target:
                    start = mid + 1  # 如果中间值小于目标值,缩小搜索范围到右半部分
                
                elif nums[mid] > target:
                    end = mid - 1  # 如果中间值大于目标值,缩小搜索范围到左半部分
                
                else:
                    res = mid  # 找到目标值,更新结果为当前位置
                    end = mid - 1  # 继续向左搜索更左边的位置
            return res
        
        # 辅助函数:查找目标值的右边界
        def find_right():
            start, end = 0, len(nums) - 1
            res = -1  # 初始化结果为-1,表示未找到
            while start <= end:
                mid = (start + end) // 2  # 计算中间位置的索引
                
                if nums[mid] < target:
                    start = mid + 1  # 如果中间值小于目标值,缩小搜索范围到右半部分
                
                elif nums[mid] > target:
                    end = mid - 1  # 如果中间值大于目标值,缩小搜索范围到左半部分
                
                else:
                    res = mid  # 找到目标值,更新结果为当前位置
                    start = mid + 1  # 继续向右搜索更右边的位置
            return res
        
        # 返回包含目标值范围的结果数组,左边界和右边界分别由辅助函数找到
        return [find_left(), find_right()]

Java

class Solution {
    public int[] searchRange(int[] nums, int target) {
        // 获取数组的长度
        int n = nums.length;
        // 初始化左指针为0
        int l_ptr = 0;
        // 初始化右指针为数组长度减一
        int r_ptr = n - 1;
        
        // 初始化答案数组,用于存储目标值的范围
        int[] ans = {-1, -1};
        
        // 在左指针小于等于右指针的条件下进行循环
        while (l_ptr <= r_ptr) {
            // 计算中间位置的索引
            int mid = (r_ptr - l_ptr) / 2 + l_ptr;
            
            // 如果中间位置的元素等于目标值
            if (nums[mid] == target) {
                // 更新答案的左边界和右边界为中间位置
                ans[0] = mid;
                ans[1] = mid;
                
                // 从中间位置向左扩展,找到目标值的第一个位置
                while (ans[0] > 0 && nums[ans[0] - 1] == target) {
                    ans[0]--;
                }
                
                // 从中间位置向右扩展,找到目标值的最后一个位置
                while (ans[1] < n - 1 && nums[ans[1] + 1] == target) {
                    ans[1]++;
                }
                
                // 跳出循环,因为已经找到了目标值的范围
                break;
            } else if (nums[mid] > target) {
                // 如果中间位置的元素大于目标值,缩小搜索范围到左半部分
                r_ptr = mid - 1;
            } else {
                // 如果中间位置的元素小于目标值,缩小搜索范围到右半部分
                l_ptr = mid + 1;
            }
        }
        
        // 返回包含目标值范围的答案数组
        return ans;
    }
}

Cpp

class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        int ret;  // 用于存储目标值的起始位置
        int l = 0, r = nums.size() - 1;  // 初始化左右指针,表示搜索范围
        
        // 第一个循环:查找目标值的起始位置
        while (l < r) {
            int mid = (l + r) / 2;  // 计算中间位置的索引
            if (nums[mid] < target) {
                l = mid + 1;  // 如果中间值小于目标值,缩小搜索范围到右半部分
            } else {
                r = mid;  // 否则,缩小搜索范围到左半部分
            }
        }
        
        // 如果找到的位置超出数组范围或者该位置的值不等于目标值,返回{-1, -1}
        if (l == nums.size() || nums[l] != target) {
            return {-1, -1};
        }
        
        ret = l;  // 记录目标值的起始位置
        
        l = 0;
        r = nums.size() - 1;
        
        // 第二个循环:查找目标值的结束位置
        while (l < r) {
            int mid = (l + r + 1) / 2;  // 计算中间位置的索引
            if (nums[mid] > target) {
                r = mid - 1;  // 如果中间值大于目标值,缩小搜索范围到左半部分
            } else {
                l = mid;  // 否则,缩小搜索范围到右半部分
            }
        }
        
        // 返回包含目标值范围的结果数组,包括起始位置和结束位置
        return {ret, l};
    }
};

Required basics for each version:

Basics of the Go version:

  1. Slices : Slices in the Go language are dynamic arrays and are very commonly used to process array data. In this solution, numsis an integer slice, and ansis also an integer slice that stores the range of target values.

  2. Loops and conditional statementsfor : Loops and conditional statements are used in the solution ifto implement logical control, for example, to continuously narrow the search scope in binary search.

  3. Binary Search : The key to this problem is to use the binary search algorithm. You need to understand the basic principle of binary search, that is, by comparing the size of the intermediate element with the target value, the search range is reduced by half until the target is found or the target is determined not to exist.

Basics of the Python version:

  1. Lists : Lists in Python are similar to arrays and are used to store a set of elements. In this solution, numsis a list of integers, and ansis also a list of integers that stores the range of target values.

  2. Functions and modulesfind_left : Two auxiliary functions and are defined in the solution find_right, and functions are used to organize the code. You also need to understand the concept of Python modules, such as the import of Listtypes in typingmodules.

  3. Loops and conditional statements : Like the Go version, the Python version also uses forloops and ifconditional statements to implement logic control.

  4. Binary search : You also need to understand the basic principles and implementation methods of binary search.

Basics of the Java version:

  1. Arrays : Java uses arrays to store a set of elements. In this solution, numsis an integer array, and ansis also an integer array that stores the range of target values.

  2. Classes and methods : The Java version uses a class Solutionthat contains a method searchRangeto solve the problem. Need to know how to define classes and methods and call them.

  3. Loops and conditional statements : Like other versions, the Java version also uses whileloops and ifconditional statements to implement logical control.

  4. Binary search : Understand the basic principles and implementation methods of binary search.

Basics of the C++ version:

  1. Vectors : Vectors in C++ are similar to dynamic arrays and are used to store a set of elements. In this solution, numsis a vector of integers, and ansis also an integer vector storing the range of target values.

  2. Classes and Methods : Like the Java version, the C++ version uses a class Solutionthat contains a method searchRangeto solve a problem. Need to know how to define classes and methods and call them.

  3. Loops and conditional statements : Like other versions, the C++ version also uses whileloops and ifconditional statements to implement logic control.

  4. Binary search : Understand the basic principles and implementation methods of binary search.

35. Search Insert Position

topic

Given a sorted array and a target value, return the index if the target is found. If not, return the index where it
would be if it were inserted in order.

You may assume no duplicates in the array.

Example 1:

Input: [1,3,5,6], 5
Output: 2

Example 2:

Input: [1,3,5,6], 2
Output: 1

Example 3:

Input: [1,3,5,6], 7
Output: 4

Example 4:

Input: [1,3,5,6], 0
Output: 0

The general idea of ​​the topic

Given a sorted array and a target value, find the target value in the array and return its index. If the target value does not exist in the array, returns the position at which it will be inserted sequentially.

You can assume that there are no duplicate elements in the array.

Problem-solving ideas

Here are the problem-solving ideas for each version:

Go version problem-solving ideas:

  1. Define two pointers lowand high, which point to the beginning and end of the array respectively.

  2. Use a loop to perform a binary search:

    • To calculate the index of an intermediate element mid, use (low + (high - low) >> 1)Calculate.
    • If the middle element nums[mid]is greater than or equal to the target value target, highmove the pointer to mid - 1, indicating that the search will continue in the left half.
    • Otherwise, if the middle element is less than the target value target:
      • Checks whether the next element of the middle element is greater than or equal to the target value targetor whether the end of the array has been reached.
      • If so, it indicates that the target value should be inserted after the current position, and is returned mid + 1as the index of the insertion position.
      • Otherwise, lowmove the pointer to mid + 1to continue searching in the right half.
  3. If the appropriate position is still not found at the end of the loop, it means that the target value should be inserted at the beginning of the array, and 0 is returned.

Python version problem-solving ideas:

  1. Define two pointers lowand high, which point to the beginning and end of the array respectively.

  2. Use whilea loop to perform a binary search:

    • To calculate the index of an intermediate element mid, use (low + (high - low) // 2)Calculate.
    • If the middle element nums[mid]is greater than or equal to the target value target, highmove the pointer to mid - 1, indicating that the search will continue in the left half.
    • Otherwise, if the middle element is less than the target value target:
      • Checks whether the next element of the middle element is greater than or equal to the target value targetor whether the end of the array has been reached.
      • If so, it indicates that the target value should be inserted after the current position, and is returned mid + 1as the index of the insertion position.
      • Otherwise, lowmove the pointer to mid + 1to continue searching in the right half.
  3. If the appropriate position is still not found at the end of the loop, it means that the target value should be inserted at the beginning of the array, and 0 is returned.

Java version solution ideas:

  1. Define two pointers lowand high, which point to the beginning and end of the array respectively.

  2. Use whilea loop to perform a binary search:

    • To calculate the index of an intermediate element mid, use (low + (high - low) / 2)Calculate.
    • If the middle element nums[mid]is greater than or equal to the target value target, highmove the pointer to mid - 1, indicating that the search will continue in the left half.
    • Otherwise, if the middle element is less than the target value target:
      • Checks whether the next element of the middle element is greater than or equal to the target value targetor whether the end of the array has been reached.
      • If so, it indicates that the target value should be inserted after the current position, and is returned mid + 1as the index of the insertion position.
      • Otherwise, lowmove the pointer to mid + 1to continue searching in the right half.
  3. If the appropriate position is still not found at the end of the loop, it means that the target value should be inserted at the beginning of the array, and 0 is returned.

C++ version problem-solving ideas:

  1. Define two pointers lowand high, which point to the start and end of the vector (dynamic array) respectively.

  2. Use whilea loop to perform a binary search:

    • To calculate the index of an intermediate element mid, use (low + (high - low) / 2)Calculate.
    • If the middle element nums[mid]is greater than or equal to the target value target, highmove the pointer to mid - 1, indicating that the search will continue in the left half.
    • Otherwise, if the middle element is less than the target value target:
      • Checks whether the next element of the middle element is greater than or equal to the target value targetor whether the end of the vector has been reached.
      • If so, it indicates that the target value should be inserted after the current position, and is returned mid + 1as the index of the insertion position.
      • Otherwise, lowmove the pointer to mid + 1to continue searching in the right half.
  3. If the appropriate position is still not found at the end of the loop, it means that the target value should be inserted at the beginning of the vector and 0 is returned.

In general, each version of the problem-solving idea is based on a variant of the binary search algorithm, which approaches the insertion position of the target value by constantly updating the lowand pointers. highDepending on the programming language, the details of the syntax and data structures will vary, but the basic idea remains the same.

code

Go

func searchInsert(nums []int, target int) int {
    // 定义两个指针,low 指向数组的起始位置,high 指向数组的末尾位置
    low, high := 0, len(nums)-1
    
    // 使用循环执行二分查找
    for low <= high {
        // 计算中间元素的索引
        mid := low + (high-low)>>1
        
        // 如果中间元素大于等于目标值
        if nums[mid] >= target {
            // 将 high 指针移动到中间元素的前一个位置
            high = mid - 1
        } else {
            // 如果中间元素小于目标值
            // 检查中间元素的下一个元素是否大于等于目标值,或者是否已经到达数组末尾
            if (mid == len(nums)-1) || (nums[mid+1] >= target) {
                // 如果是,说明目标值应该插入到当前位置的后面,返回当前位置的下一个索引
                return mid + 1
            }
            // 否则,将 low 指针移动到中间元素的下一个位置
            low = mid + 1
        }
    }
    
    // 如果循环结束仍然没有找到合适的位置,说明目标值应该插入到数组的开头,返回0
    return 0
}

Python

class Solution:
    def searchInsert(self, nums: List[int], target: int) -> int:
        # 定义两个指针,low指向数组的起始位置,high指向数组的末尾位置
        low, high = 0, len(nums) - 1

        # 使用循环执行二分查找
        while low <= high:
            # 计算中间元素的索引
            mid = low + (high - low) // 2

            # 如果中间元素大于等于目标值
            if nums[mid] >= target:
                # 将high指针移动到中间元素的前一个位置
                high = mid - 1
            else:
                # 如果中间元素小于目标值
                # 检查中间元素的下一个元素是否大于等于目标值,或者是否已经到达数组末尾
                if mid == len(nums) - 1 or nums[mid + 1] >= target:
                    # 如果是,说明目标值应该插入到当前位置的后面,返回当前位置的下一个索引
                    return mid + 1
                # 否则,将low指针移动到中间元素的下一个位置
                low = mid + 1

        # 如果循环结束仍然没有找到合适的位置,说明目标值应该插入到数组的开头,返回0
        return 0

Java

class Solution {
    public int searchInsert(int[] nums, int target) {
        // 定义两个指针,low指向数组的起始位置,high指向数组的末尾位置
        int low = 0, high = nums.length - 1;
        
        // 使用循环执行二分查找
        while (low <= high) {
            // 计算中间元素的索引
            int mid = low + (high - low) / 2;
            
            // 如果中间元素大于等于目标值
            if (nums[mid] >= target) {
                // 将high指针移动到中间元素的前一个位置
                high = mid - 1;
            } else {
                // 如果中间元素小于目标值
                // 检查中间元素的下一个元素是否大于等于目标值,或者是否已经到达数组末尾
                if (mid == nums.length - 1 || nums[mid + 1] >= target) {
                    // 如果是,说明目标值应该插入到当前位置的后面,返回当前位置的下一个索引
                    return mid + 1;
                }
                // 否则,将low指针移动到中间元素的下一个位置
                low = mid + 1;
            }
        }
        
        // 如果循环结束仍然没有找到合适的位置,说明目标值应该插入到数组的开头,返回0
        return 0;
    }
}

Cpp

class Solution {
public:
    int searchInsert(vector<int>& nums, int target) {
        // 定义两个指针,low指向数组的起始位置,high指向数组的末尾位置
        int low = 0, high = nums.size() - 1;
        
        // 使用循环执行二分查找
        while (low <= high) {
            // 计算中间元素的索引
            int mid = low + (high - low) / 2;
            
            // 如果中间元素大于等于目标值
            if (nums[mid] >= target) {
                // 将high指针移动到中间元素的前一个位置
                high = mid - 1;
            } else {
                // 如果中间元素小于目标值
                // 检查中间元素的下一个元素是否大于等于目标值,或者是否已经到达数组末尾
                if (mid == nums.size() - 1 || nums[mid + 1] >= target) {
                    // 如果是,说明目标值应该插入到当前位置的后面,返回当前位置的下一个索引
                    return mid + 1;
                }
                // 否则,将low指针移动到中间元素的下一个位置
                low = mid + 1;
            }
        }
        
        // 如果循环结束仍然没有找到合适的位置,说明目标值应该插入到数组的开头,返回0
        return 0;
    }
};

When understanding and writing each version of the code, you need to master the following basics:

  1. Go version:
  • Slices : Arrays and slices are very important data structures in Go. Slices are dynamic arrays used to store a sequence of elements of the same type.
  • Loops and conditional statementsfor : You need to understand the usage of loops and conditional statements in Go ifin order to write loops and conditional judgments in your code.
  • Functions : You need to know how to define and call functions, as well as their parameters and return values.
  • Binary Search Algorithm : Understanding how the binary search algorithm works is important to understanding this code.
  1. Python version:
  • Lists : Lists in Python are ordered data structures that can store multiple data types. In this problem, list is used to store integer elements.
  • Loops and conditional statementswhile : You need to understand how to use loops and ifconditional statements in Python in order to write loops and conditional judgments in code.
  • Classes and methodsclass : How and methods are defined in Python . In this version, the code is encapsulated in a class.
  • Binary Search Algorithm : Understanding how the binary search algorithm works is important to understanding this code.
  1. Java version:
  • Array : Array in Java is a static data structure and you need to know how to declare, initialize and access array elements.
  • Loops and conditional statementswhile : You need to understand how to use loops and ifconditional statements in Java in order to write loops and conditional judgments in code.
  • Classes and Methods : How classes and methods are defined in Java. In this version, the code is encapsulated in a class.
  • Binary Search Algorithm : Understanding how the binary search algorithm works is important to understanding this code.
  1. C++ version:
  • Vectors : A vector in C++ is a dynamic array, similar to a list in Python or a slice in Go.
  • Loops and conditional statementswhile : You need to understand the use of loops and conditional statements in C++ ifin order to write loops and conditional judgments in code.
  • Classes and Methods : How classes and methods are defined in C++. In this version, the code is encapsulated in a class.
  • Binary Search Algorithm : Understanding how the binary search algorithm works is important to understanding this code.

In general, no matter which version of the programming language you choose, you need to be familiar with arrays, loops, conditional statements, and binary search algorithms. In addition, it is also very important to understand the syntax and standard library functions of a specific programming language in order to be able to write and understand the code correctly. If you are unfamiliar with a specific language, it is recommended to learn the basics of the language before trying to understand and code the corresponding version.

Guess you like

Origin blog.csdn.net/qq_42531954/article/details/132795500