LeetCode sword finger Offer II retrospective summary

Posted by Loryman on Wed, 26 Jan 2022 00:21:48 +0100

  • 📚 Blog home page: ⭐ This is a little Yibai blog duck~ ⭐ ️
  • 👉 Welcome to pay attention ❤️ give the thumbs-up 👍 Collection ⭐ Comments 📝
  • 😜 Xiaoyibai is preparing for his internship. He often updates the interview questions and LeetCode solutions. Friends with similar interests are welcome to communicate with each other~
  • 💙 If you have any questions, please correct them. Remember to pay attention. Thank you~

Previous articles:

Retrospective introduction:

Backtracking is a special case of priority search, also known as heuristics. It is often used in depth first search where node states need to be recorded
As the name suggests, the core of backtracking method is backtracking. When searching for a node, if we find that the current node (and its child nodes) is not the demand target, we go back to the original node to continue the search, and restore the modified state of the current node. The advantage is that we can always only modify the overall state of the graph, rather than creating a new graph to store the state every time we traverse.
In terms of specific writing, like the ordinary depth first search, it has the steps of [modify current node state] → [recursive child node],
Only there are more backtracking steps, which become [modify current node state] → [recursive child node] → [change current node state].

The two consecutive questions of these six questions are of the same type, covering most of the basic ideas of backtracking method. It is recommended to compare them

079. All subsets (template questions)

Title:

Given an integer array nums, the elements in the array are different from each other. Returns all possible subsets (power sets) of the array.
The solution set cannot contain duplicate subsets. You can return the solution set in any order.

Example:

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

Tips:

  • 1 <= nums.length <= 10
  • -10 <= nums[i] <= 10
  • All elements in nums are different from each other

Idea:

You can't repeat the search type. Take one data
For the template question, I provide you with two templates. One is with for, which is added when you choose, and the other is the idea of whether to choose or not

class Solution {
public:
    vector<vector<int>> ans;
    vector<int> nums;
    vector<int> cur;
    int n;
    vector<vector<int>> subsets(vector<int>& nums) {
        this->nums = nums;
        n = nums.size();
        dfs(0);
        return ans;
    }
    // Template, search without looking back
    void dfs(int index) {
        ans.emplace_back(cur);
        for(int i = index; i < n; i++) {
            cur.emplace_back(nums[i]);
            dfs(i + 1);
            cur.pop_back();
        }
    }
    // Template without for
    void dfs2(int index) {
        if(index == nums.size()) {
            ans.emplace_back(cur);
            return ;
        }
        // choose
        cur.emplace_back(nums[index]);
        dfs(index + 1);
        cur.pop_back();
        // Not selected
        dfs(index + 1);
    }
};

080. Combination of k elements

Title:

Given two integers n and k, returns the combination of all possible k numbers in 1... N.

Example:

Input: n = 4, k = 2
Output:
[
[2,4],
[3,4],
[2,3],
[1,2],
[1,3],
[1,4],
]

Tips:

  • 1 <= n <= 20
  • 1 <= k <= n

Idea:

Compared with the previous question 079 There are many restrictions on all subsets. Originally, there was no limit on the number of subsets, and different combinations were taken
Now the limit can only take two numbers, so just add a number limit k to judge the parameter

class Solution {
public:
    vector<int> cur;
    vector<vector<int>> ans;
    int n;
    vector<vector<int>> combine(int n, int k) {
        this->n = n;
        dfs(1, k);
        return ans;
    }
    // Backtracking is almost the same as question 79, with the number limit added
    void dfs(int num, int k) {
        if(k == 0) {
            ans.emplace_back(cur);
            return ;
        }
        for(int i = num; i <= n; i++) {
            cur.emplace_back(i);
            dfs(i + 1, k - 1);
            cur.pop_back();
        }
    }
};

081. It is allowed to repeatedly select the combination of elements

Title:

Given a positive integer array of candidates without duplicate elements and a positive integer target, find out all the unique combinations in candidates that can make the number and target the target number.
The number in candidates can be selected repeatedly without limit. If the number of at least one selected number is different, the two combinations are unique.
For a given input, the number of unique combinations with guaranteed sum as target is less than 150.

Example:

Input: candidates = [2,3,6,7], target = 7
Output: [[7], [2,2,3]]

Tips:

  • 1 <= candidates.length <= 30
  • 1 <= candidates[i] <= 200
  • Each element in the candidate is unique.
  • 1 <= target <= 500

Idea:

This question has two limitations: it is allowed to select the same element repeatedly; The selected elements and are target

  • The first limitation: we only need to change from the original data fetching from the next data to continue fetching from itself
  • The second limitation: every time we take an element, we subtract this element from the sum. When it is 0, it means that the sum is just the target
  • If it is greater than 0, it means that it exceeds. There is no need to continue the search and return. Because I subtracted it when passing parameters, I don't need to add this element again during backtracking
class Solution {
public:
    vector<int> candidates;
    vector<vector<int>> ans;
    vector<int> cur;
    int n;
    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        this->candidates = candidates;
        n = candidates.size();
        dfs(0, target);
        return ans;
    }
    // Slightly different from the previous two questions, the numbers in candidates can be selected repeatedly without restriction
    void dfs(int index, int target) {
        // Sum > target return
        if(target < 0) return ;
        // Add answers when total = = target
        if(target == 0) {
            ans.emplace_back(cur);
            return ;
        }
        for(int i = index; i < n; i++) {
            cur.emplace_back(candidates[i]);
            // Because it can be reused, we continue to traverse from our own position until it returns with > = target, and then add the next element
            dfs(i, target - candidates[i]);
            cur.pop_back();
        }
    }
};

082. Combination with duplicate element set

Title:

Given an integer array of candidates that may have duplicate numbers and a target number target, find all combinations in candidates that can make the sum of numbers target.
Each number in candidates can only be used once in each combination, and the solution set cannot contain duplicate combinations.

Example:

Input: candidates = [10,1,2,7,6,1,5], target = 8,
Output:
[
[1,1,6],
[1,2,5],
[1,7],
[2,6]
]

Tips:

  • 1 <= candidates.length <= 100
  • 1 <= candidates[i] <= 50
  • 1 <= target <= 30

Idea:

This question is compared with 081 The combination of elements that allow repeated selection has the following changes
1, The same element cannot be taken repeatedly
2, Duplicate combinations cannot exist

  • The first one: the element dfs(i + 1, target - candidates[i]) after this element is taken every time;
  • Second: the difficulty lies in this. Let's sort the array to facilitate duplicate checking. Sort (candidates. Begin()) end());
    Select skip if (I - 1 > = index & & candidates [I - 1] = = candidates [i]) continue when adjacent to the same element;

For example [1,2,2,2,5], if the first 2 is selected, it becomes [1,2]. Its next option is also 2. Skip it, because if it is selected, it will still be [1,2]

class Solution {
public:
    vector<vector<int>> ans;
    vector<int> candidates;
    vector<int> cur;
    int n;
    vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
        this->n = candidates.size();
        // Sorting is required to facilitate skipping when encountering the same element
        sort(candidates.begin(), candidates.end());
        //quickSort(candidates, 0, candidates.size() - 1);
        this->candidates = candidates;
        dfs(0, target);
        return ans;
    }
    void dfs(int index, int target) {
        if(target < 0) return ;
        if(target == 0) {
            ans.emplace_back(cur);
            return;
        }
        for(int i = index; i < n; i++) {
            // If the current number is the same as the previous number, it is not selected
            if(i - 1 >= index  && candidates[i - 1] == candidates[i]) continue;
            cur.emplace_back(candidates[i]);
            dfs(i + 1, target - candidates[i]);
            cur.pop_back();
        }
    }
    // Just write more. It's no faster than sort function. It's embarrassing
    void quickSort(vector<int>& candidates, int l, int r) {
        if(l >= r) return ;
        int i = l, j = r, mid = candidates[((r - l) >> 1) + l];
        do{
            while(candidates[i] < mid) i++;
            while(candidates[j] > mid) j--;
            if(i <= j) swap(candidates[i++], candidates[j--]);
        }while(i <= j);
        quickSort(candidates, l, j);
        quickSort(candidates, i, r);
    }
};

083. Full Permutation without duplicate element set

Title:

Given an integer array nums without duplicate numbers, all possible permutations are returned. You can return answers in any order.

Example:

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

Tips:

  • 1 <= nums.length <= 6
  • -10 <= nums[i] <= 10
  • All integers in nums are different from each other

Idea:

Keywords: all possible full permutations without duplicate numbers
The full arrangement cannot only consider the following elements, not the front elements, as in the previous questions

  • Therefore, we need an array of tags, vis, to mark the accessed elements to prevent repeated access
  • Each time, the traversal starts from the beginning. The traversed vis is true and skipped
class Solution {
public:
    vector<vector<int>> ans;
    vector<int> cur;
    vector<int> nums;
    int n;
    // Mark accessed numbers
    vector<bool> vis;
    vector<vector<int>> permute(vector<int>& nums) {
        this->nums = nums;
        this->n = nums.size();
        vis.resize(n, 0);
        dfs(0);
        return ans;
    }
    // Compared with the previous questions, there are more tag arrays vis, and for starts from 0. The parameters passed by dfs are not subscripts, but the number in cur. In this way, it is OK not to pass parameters
    void dfs(int count) {
        if(count == n) {
            ans.emplace_back(cur);
            return ;
        }
        // The full arrangement starts from 0 and can be added back
        for(int i = 0; i < n; i++) {
            if(vis[i] == true) continue;
            cur.emplace_back(nums[i]);
            vis[i] = 1;
            dfs(count + 1);
            vis[i] = 0;
            cur.pop_back();
        }
    }
};

084. Full permutation with sets of repeating elements

Title:
Given an integer set nums that can contain repeated numbers, return all its non repeated full permutations in any order.

Example:
Input: num = [1,1,2]
Output:
[[1,1,2],
[1,2,1],
[2,1,1]]

Tips:

  • 1 <= nums.length <= 8
  • -10 <= nums[i] <= 10

Idea:

Keyword: full arrangement with repeated numbers and no repetition.
If the elements in the array are the same, it means that there will be repeated full arrangement, so we are in the sword finger Offer II 083 There is no repetition based on the full arrangement of the set of elements
Sword finger Offer II 082 For the combination containing the set of repeated elements, the decision marked by vis is added on the basis of de duplication
If two adjacent elements are the same, and the previous element has been used, it means that this arrangement has been used, so there is no need to continue to search later
if((i - 1 >= 0 && nums[i - 1] == nums[i]) && vis[i-1] == 1) break;

class Solution {
public:
    vector<vector<int>> ans;
    vector<int> cur;
    vector<int> nums;
    int n;
    vector<bool> vis;
    vector<vector<int>> permuteUnique(vector<int>& nums) {
        // Sorting is convenient for duplicate checking
        sort(nums.begin(), nums.end());
        this->nums = nums;
        this->n = nums.size();
        vis.resize(n, 0);
        dfs();
        return ans;
    }
    // It's very similar to question 82, but because this will look back, you need to determine the tag array
    void dfs() {
        // Just like the previous question, it's OK to pass a count. It's not passed here. Compare it
        if(cur.size() == n) {
            ans.emplace_back(cur);
            return ;
        }
        for(int i = 0; i < n; i++) {
            if(vis[i] == 1)
                continue;
            // Compared with question 82, if the two numbers are equal and the previous number has been marked, it means that the following is repeated
            if((i - 1 >= 0 && nums[i - 1] == nums[i]) && vis[i-1] == 1)
                break;
            cur.emplace_back(nums[i]);
            vis[i] = 1;
            dfs();
            vis[i] = 0;
            cur.pop_back();
        }
        
    }
};

Topics: C++ Algorithm leetcode dfs