Jianzhi offer questions 1-11: Brush questions week1[C + + solution]

1. Find out the duplicate numbers in the array

Title Link: https://www.acwing.com/problem/content/14/

Idea 1: sorting

ac Code: time complexity O ( n × l o g n ) O(n\times logn) O(n×logn)
Idea: here is to sort the array, and then compare two adjacent elements. If they are equal, the number will be returned directly.

class Solution {
public:
    int duplicateInArray(vector<int>& nums) {
        if(nums.empty()) return -1;
        int n = nums.size();
        sort(nums.begin(), nums.end());
        if(nums[0] < 0 || nums[n - 1] > n - 1) return -1;
        for(int i = 0; i < n - 1; i ++){
            if(nums[i + 1] == nums[i]) return nums[i];
        }
        return  -1;
    }
};

Idea 2: hash table

Use the unordered container in C + +_ Map counts the number of elements. If the number of elements is greater than 1, it can be output directly.

ac Code: time complexity O ( n × l o g n ) O(n\times logn) O(n × logn), spatial complexity O ( 1 ) O(1) O(1) because a hash table with length n is used.

class Solution {
public:
    int duplicateInArray(vector<int>& nums) {
        int n = nums.size();
        unordered_map<int, int> mp;
        for(int i = 0; i < n; i ++)
            if(nums[i] < 0 || nums[i] > n -1) return -1;
        for(int i = 0; i < n; i ++){
            mp[nums[i]] ++;
            if(mp[nums[i]] > 1) return nums[i];
        }
        return -1;
    }
};

Idea 3: in situ exchange

Time complexity O ( n ) O(n) O(n), spatial complexity O ( 1 ) O(1) O(1).

Idea: will x = n u m s [ i ] x=nums[i] x=nums[i] and subscript x x Value at x n u m s [ x ] nums[x] Compare nums[x]. If different, exchange nums[i] and nums[x], so that the value x can be placed in the place of subscript X. The next time x appears and finds x = = n u m s [ x ] x == nums[x] When x==nums[x], X is the value that appears twice.

The popular explanation is that the legal position of the value 2 is where the subscript is 2. If it is elsewhere, it needs to be exchanged to the legal position (the index is 2). When will this process end? Until 2 appears at other index positions, that is, duplication occurs; Or exchange n times, but there is no repetition, and the algorithm stops.

For example, if the value 2 is in the index 0 0 0, and n u m s [ 2 ] = = 2 nums[2] == 2 nums[2]==2. At this time, that is, 2 repeats. Just return 2.

The better explanation here is the analogy of "one radish, one pit" in the leetcode problem solution.

class Solution {
public:
    int duplicateInArray(vector<int>& nums) {
        if(nums.empty()) return -1;
        int n = nums.size();
        for(int i = 0; i < n; i ++)
            if(nums[i] < 0 || nums[i] > n - 1) return -1;
        for(int i = 0; i < n; i ++){
            while(i != nums[i] && nums[i] != nums[nums[i]]) 
                swap(nums[i], nums[nums[i]]);
            if(i != nums[i] && nums[i] == nums[nums[i]]) return nums[i];
        }
        return  -1;
    }
};

A little introduction

class Solution {
public:
    int duplicateInArray(vector<int>& nums) {
        int n = nums.size();
        for(auto &x : nums) if(x < 0 || x > n - 1) return -1;
        for(int i = 0; i < n; i ++){
            int x = nums[i];
            while( x != i && x != nums[x]) swap(x, nums[x]);
            if(x != i && x == nums[x]) return x;
        }
        return -1;
    }
};

2. Do not modify the array to find duplicate numbers

Title Link: https://www.acwing.com/problem/content/15/

Idea: drawer principle + dichotomy

This question requires that the original array cannot be modified

Problem solving ideas

We use dichotomy here. Since each number is between 1 and N, and there are n+1 numbers in total, at least one number is repeated. This is based on the drawer principle.

Drawer principle:
If n+1 items are put in n drawers, at least one drawer will put 2 items.

For the string of data with the maximum value of n-1, we divide it into two segments, namely a = [ 1 , n 2 ] a=[1, \frac{n}{2}] a=[1,2n] and b = [ n 2 + 1 , n ] b=[\frac{n}{2}+1,n] b=[2n​+1,n]. Please note that this is a binary of the logarithm, not an array subscript. Later, we will count the number of values in this interval.

For example, if the maximum value n-1 = 7, then the interval [ 1 , 7 ] [1,7] The interval of [1,7] points is a = [ 1 , 4 ] a=[1,4] a=[1,4] and b = [ 5 , 7 ] b=[5,7] b=[5,7]. Then traverse the array and count the number of numbers in two intervals. Among them, the number of numbers in one interval must be greater than the length of the interval. Suppose this interval is a = [ 1 , 4 ] a=[1, 4] a=[1,4], its interval length is 4 - 1 + 1 = 4, and it is assumed that the number of elements in a counted in the original array is 5. According to the drawer principle, there must be duplicate values in this interval. Then we can divide the interval [ 1 , 7 ] [1,7] [1,7] reduced to [ 1 , 4 ] [1,4] [1,4], and then repeat the above steps until the length of an interval becomes 1. At this time, this value is the answer.

  • Time complexity O ( n l o g n ) O(nlogn) O(nlogn): This is because the length of the array is halved each time l o g n logn Log n times, and the whole array needs to be traversed each time, so the time complexity is n l o g n nlogn nlogn;

  • Spatial complexity O ( 1 ) O(1) O(1), because other structures such as arrays or hash tables are not used.

acwing

ac code

class Solution {
public:
    int duplicateInArray(vector<int>& nums) {
        int l = 1, r = nums.size() - 1;
        while(l < r){
            int mid = l + r >> 1; // [l, mid], [mid + 1, r]
            int s = 0; // Number of left half edges
            for(auto x :nums) s += x >= l && x <= mid;
            if(s > mid - l + 1) r = mid;
            else l = mid + 1;
        }
        return r;
    }
};

3. Search in two-dimensional array

Title Link: https://www.acwing.com/problem/content/16/

Idea: thinking question (select the value in the upper right corner)

Select the value a in the upper right corner of the grid every time. In this way, the row and column of the value form a monotonic sequence. If target < A, the column can be discarded; If target > A, the row can be discarded.

AC code

Time complexity O ( m + n ) O(m + n) O(m+n), where m and N are the dimensions of two-dimensional array and the spatial complexity O ( 1 ) O(1) O(1)

class Solution {
public:
    bool searchArray(vector<vector<int>> array, int target) {
        if(array.empty()) return false;
        int n =  array.size(), m = array[0].size();
        int i = 0, j = m - 1; // Coordinates of the upper right corner
        while(i < n && j >= 0){
            if(array[i][j] == target) return true;
            else if(target < array[i][j]) j--; // Delete the last column
            else i ++; //Delete the top line
        }
        return false;
    }
};

4. Replace spaces

Title Link: https://www.acwing.com/problem/content/17/

Idea: grammar questions

Open a new string res, traverse str, replace it with "% 20" when encountering spaces, and add it to the end of res. if it is not a space, add it to the end of res.

AC code

Time complexity O ( n ) O(n) O(n), spatial complexity O ( n ) O(n) O(n)

class Solution {
public:
    string replaceSpaces(string &str) {
        string res;
        for(auto &x : str){
            if(x == ' ') res += "%20";
            else res += x;
        }
        return res;
    }
};

5. Print the linked list from end to end

Traverse the linked list, put its elements into the vector (named RES) in turn, and then output the vector in reverse order. The reverse order here uses the reverse order iterator return vector < int > (res.rbegin(), res.rend());

Time complexity O ( n ) O(n) O(n), spatial complexity O ( n ) O(n) O(n)

AC code

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    vector<int> printListReversingly(ListNode* head) {
        vector<int> res;
        while(head){
            res.push_back(head->val);
            head = head->next;
        }
        return vector<int>(res.rbegin(),res.rend());
    }
};

6. Rebuild binary tree

Title Link: https://www.acwing.com/problem/content/23/

thinking

According to the pre order traversal and middle order traversal of binary tree, the process of recursive tree building is mainly investigated.
dfs(pl, pr, il, ir) is used here
pl represents the left end point of preorder traversal, and pr represents the right end point of preorder traversal;
il represents the left end point of mid order traversal, and ir represents the right end point of mid order traversal.
The return value is the tree root.

Finally, you need to add root - > left = left and root - > right = right to the total root node to rebuild the binary tree.

Time complexity O ( n ) O(n) O(n), spatial complexity O ( n ) O(n) O(n)

AC code

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    unordered_map<int, int> hash;// Hash table, which stores the location of the traversal data in the middle order
    vector<int> preorder, inorder;
    TreeNode* buildTree(vector<int>& _preorder, vector<int>& _inorder) {
        preorder = _preorder, inorder = _inorder;
        for(int i =0; i < inorder.size(); i ++) hash[inorder[i]] = i;
        return dfs(0, preorder.size() - 1, 0, inorder.size() - 1);
    }
    
    TreeNode* dfs(int pl, int pr, int il, int ir){
        if(pl > pr) return nullptr;
        auto root = new TreeNode(preorder[pl]); 
        int k = hash[root->val]; // Position in middle order traversal
        auto left = dfs(pl + 1, k - il + pl, il, k - 1);
        auto right = dfs(k - il + pl + 1, pr, k + 1, ir);
        root->left = left, root->right = right;
        return root;
    }
};

7. Next node of binary tree

Commonly known as "the successor of the tree"
Title Link: https://www.acwing.com/problem/content/31/

thinking

Time complexity O ( H ) O(H) O(H),H is the height and spatial complexity of the tree O ( 1 ) O(1) O(1)

The node source is discussed in two cases.

First, if the source node has a right subtree, the subsequent node of the middle order traversal is the leftmost point in the left subtree.

Second, the source node has no right subtree. How to deal with it at this time? You need to traverse up along the parent node until you find the first point. This point is the left son of its parent node. This point is marked as temp, and the parent node of this point is the successor of source.

For example, here we take node e as the source, which has no right subtree, which is obviously the second case. We traverse upward, source = e - > father, that is, source becomes B. at this time, we find that B is the left son of A, reaches the boundary condition, and A is the successor of E.

ac code

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode *father;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL), father(NULL) {}
 * };
 */
class Solution {
public:
    TreeNode* inorderSuccessor(TreeNode* p) {
        if(p->right){ //p-node has right subtree
            p = p->right;
            while(p->left) p = p->left;
            return p;
        }
        // p node has no right subtree
        // Traverse up and find that the first current point is the left son of the parent node
        while(p->father && p == p->father->right) p = p->father;
        return p->father;
    }
};

8. Implement queue with two stacks

thinking

Two stacks are used to simulate queue behavior: queue first in first out.
Specific operation:
For the push operation of the queue, you can directly stack 1.
For the pop operation of the queue, pop all stack1 elements into stack2, and then top in stack2. After use, press all the elements in stack2 into stack1.

Spatial complexity O ( n ) O(n) O(n), time complexity:

  • push yes O ( 1 ) O(1) O(1)
  • pop is O ( n ) O(n) O(n)
  • peek is O ( n ) O(n) O(n)
  • empty yes O ( 1 ) O(1) O(1)

ac code

class MyQueue {
public:
    stack<int> stk, cache;
    /** Initialize your data structure here. */
    MyQueue() {
        
    }
    
    /** Push element x to the back of queue. */
    void push(int x) {
        stk.push(x);
    }
    
    void copy(stack<int> &a, stack<int> &b){
        while(a.size()){
            b.push(a.top());
            a.pop();
        }
    }
    
    /** Removes the element from in front of queue and returns that element. */
    int pop() {
        copy(stk, cache);
        int res = cache.top();
        cache.pop();
        copy(cache, stk);
        return res;
    }
    
    /** Get the front element. */
    int peek() {
        copy(stk, cache);
        int res = cache.top();
        copy(cache, stk);
        return res;
    }
    
    /** Returns whether the queue is empty. */
    bool empty() {
        return stk.empty();
    }
};

/**
 * Your MyQueue object will be instantiated and called as such:
 * MyQueue obj = MyQueue();
 * obj.push(x);
 * int param_2 = obj.pop();
 * int param_3 = obj.peek();
 * bool param_4 = obj.empty();
 */

9. Fibonacci series

thinking

Open a large array, save the value of each index, traverse and recurs.
Time complexity O ( n ) O(n) O(n), spatial complexity O ( n ) O(n) O(n)

AC code

class Solution {
public:
    int a[39];
    int Fibonacci(int n) {
        a[1] = 1, a[2] = 1;
        for(int i = 3; i <= 39; i ++) a[i] = a[i - 1] + a[i - 2];
        return a[n];
    }
};

10. Rotate the minimum number of the array

Idea 1: Violence

Violent practices:
Time complexity O ( n ) O(n) O(n), spatial complexity O ( 1 ) O(1) O(1)

ac code

class Solution {
public:
    int findMin(vector<int>& nums) {
        if(nums.empty()) return -1;
        int res = nums[0];
        for(auto &x : nums) if(x <= res) res = x;
        return res;
    }
};

Idea 2: dichotomy

After the whole array is rotated, the trend is shown in the figure below.

In the first half, meet n u m s [ i ] ≥ n u m s [ 0 ] nums[i] \ge nums[0] nums[i] ≥ nums[0], the latter half (excluding the last paragraph) may be equal to n u m s [ 0 ] nums[0] nums[0]) does not satisfy this property. The dividing point is the smallest value in the array.

You can use dichotomy to find the dividing point. The specific operation steps are as follows:

  • Preprocess and delete the last paragraph n u m s [ 0 ] nums[0] Value of num [0].
  • After deletion, if the last number is greater than n u m s [ 0 ] nums[0] nums[0], then the rest is a monotonically increasing sequence, and the minimum value is n u m s [ 0 ] nums[0] nums[0].
  • After deletion, if the last number is less than n u m s [ 0 ] nums[0] nums[0], then conduct dichotomy to find the dividing point.

Dichotomous time complexity O ( l o g n ) O(logn) O(logn), delete the last paragraph. The worst case is O ( n ) O(n) O(n), so the total time complexity is O ( n ) O(n) O(n), spatial complexity O ( 1 ) O(1) O(1).

ac code

class Solution {
public:
    int findMin(vector<int>& nums) {
        if(nums.empty()) return -1;
        int n = nums.size() - 1;
        int a = nums[0];
        while(n > 0 && nums[n] == a)  n --;
        if(nums[n] >= a) return a;
        int l = 0, r = n;
        while(l < r){
            int mid = l + r >> 1; // [l, mid] and [mid + 1, r]
            if(nums[mid] < a) r = mid;
            else l = mid + 1;
        }
        return nums[r];
        
    }
};

11. Path in matrix

Title Link: https://www.acwing.com/problem/content/21/

thinking

Depth first search dfs
Enumeration string starting point, total n 2 n^2 n2, and then whether each starting point dfs can form a character path. Note that you can't go back in dfs search.

Time complexity O ( n 2 k 3 ) O(n^2 k^3) O(n2k3),k is the number of str characters - 1, space complexity O ( 1 ) O(1) O(1)

class Solution {
public:
    bool hasPath(vector<vector<char>>& matrix, string &str) {
        if(matrix.empty()) return false;
        int n = matrix.size(), m = matrix[0].size();
        for(int i = 0; i < n; i ++)
            for(int j = 0; j < m; j ++)
                if(dfs(matrix, str, 0, i, j)) return true;
        return false;
    }
    
    bool dfs(vector<vector<char>>& matrix, string &str, int u, int x, int y){
        if(u == str.size()) return true;
        if(matrix[x][y] != str[u]) return false;
        //Special judgment matrix = a, and str = a
        if(matrix[x][y] == str[u] && u == str.size() - 1) return true;
        char t = matrix[x][y];
        matrix[x][y] = '%';
        int dx[4] ={1, 0, -1, 0}, dy[4] = {0, 1, 0, -1};
        for(int i = 0; i < 4; i ++){
            int a = x + dx[i], b = y + dy[i];
            if(a >= 0 && a < matrix.size() && b >= 0 && b <matrix[0].size()){
                if(dfs(matrix, str, u + 1, a, b)) return true;
            }
        }
        matrix[x][y] = t; // Restore the site
        return false;
    }
};

summary

After brushing a certain amount of questions and seeing a certain amount of models, the feeling of brushing questions is cultivated. Almost everyone has to take this step.

Added by fatmikey on Sat, 29 Jan 2022 04:13:58 +0200