Code Caprice Training Camp day24| 77. Combination

@TOC


foreword

Code Random Record Algorithm Training Camp day24


1. Leetcode 77. Combinations

1. Topic

Given two integers n and k, return all possible combinations of k numbers in the range [1, n].

You can return answers in any order.

Example 1:

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

Example 2:

Input: n = 1, k = 1 Output: [[1]]

hint:

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

Source: LeetCode Link: https://leetcode.cn/problems/combinations

2. Problem-solving ideas

Method 1: Non-recursive (dictionary order method) to implement combined enumeration

Tips: This method is more complicated to understand than "Method 1". It is suggested that readers can use examples to simulate this process on scratch paper when they encounter something they do not understand.

The non-recursive version here is not simply using the stack to simulate recursion into non-recursive: we hope to eliminate the extra space cost caused by the recursive stack through appropriate means.

Assuming that we record the selected position in the original sequence as 11, and the unselected position as 00, a binary number can be constructed for each scheme. We arrange the original sequence from largest to smallest (ie {n,n−1,⋯1,0}{n,n−1,⋯1,0}). Let's take a look at the example of n=4n=4, k=2k=2: The binary number scheme corresponding to the selected number in the original sequence 43[2][1]43[2][1] 00110011 2,12, 1 4[3]2[1]4[3]2[1] 01010101 3,13,1 4[3][2]14[3][2]1 01100110 3,23,2 [4]32[1 ][4]32[1] 10011001 4,14,1 [4]3[2]1[4]3[2]1 10101010 4,24,2 [4][3]21[4][3]21 11001100 4,34,3

We can see that the "corresponding binary number" column contains all binary numbers composed of kk 11s and n−kn−k 00s, and they are arranged in lexicographical order. This gives us some inspiration, we can enumerate through a certain method, so that the generated sequence is incremented according to the lexicographical order. We can consider our binary number xx, which consists of kk 11s and n−kn−k 00s, how to find the next number next(x)next(x) in its lexicographic order, here are two cases :

规则一:xx 的最低位为 11,这种情况下,如果末尾由 tt 个连续的 11,我们直接将倒数第 tt 位的 11 和倒数第 t+1t+1 位的 00 替换,就可以得到 next(x)next(x)。如 0011→01010011→0101,0101→01100101→0110,1001→10101001→1010,1001111→10101111001111→1010111。
规则二:xx 的最低位为 00,这种情况下,末尾有 tt 个连续的 00,而这 tt 个连续的 00 之前有 mm 个连续的 11,我们可以将倒数第 t+mt+m 位置的 11 和倒数第 t+m+1t+m+1 位的 00 对换,然后把倒数第 t+1t+1 位到倒数第 t+m−1t+m−1 位的 11 移动到最低位。如 0110→10010110→1001,1010→11001010→1100,1011100→11000111011100→1100011。

So far, we can write a simple program, using a 0/10/1 array of length nn to represent the binary number corresponding to the selection scheme. In the initial state, the lowest kk bits are all 11, and the rest of the bits are all 00, and then By continuously seeking nextnext through the above schemes, all schemes can be constructed.

We can further optimize the implementation. Let's look at the example of n=5n=5, k=3k=3. According to the above strategy, we can get this table: Binary number scheme 0011100111 3,2,13,2,1 0101101011 4, 2,14,2,1 0110101101 4,3,14,3,1 0111001110 4,3,24,3,2 1001110011 5,2,15,2,1 1010110101 5,3,15,3,1 1011010110 5, 3,25,3,2 1100111001 5,4,15,4,1 1101011010 5,4,25,4,2 1110011100 5,4,35,4,3

In the simple method, we use binary numbers to construct solutions, and binary numbers need to be iterated to obtain nextnext. Consider directly transforming the scheme to get the next scheme without using binary numbers. Assuming that the kk numbers of a scheme from low to high are {a0,a1,⋯,ak−1}{a0​,a1​,⋯,ak−1​}, we can find the first jj from low to high So that aj+1≠aj+1aj​+1​=aj+1​, we know that the number that appears in the aa sequence must be 11 in the binary number, which means it is selected, then aj+1≠aj +1aj​+1​=aj+1​ means that ajaj​ and aj+1aj+1​ have 00 in the middle of the corresponding binary bits, that is, the two 11s are not continuous. We push the 11 corresponding to ajaj​ to the high position, which corresponds to aj←aj+1aj←aj​+1, and for all i∈[0,j−1]i∈[0,j−1] aiai​ Restore the value to i+1i+1, that is, the jj 11s are moved to the lowest jj bits of the binary number. This seems to only take into account "rule two" above. But in fact, "rule 1" is a special case of "rule 2" at t=0t=0 , so doing so is equivalent to simulating according to two rules.

When implementing, we can use an array temptemp to store the aa sequence. At the beginning, we first store 11 to kk into this array in order, and their corresponding subscripts are 00 to k−1k−1. For the convenience of calculation, we need to place a sentinel n+1n+1 at the position of the subscript kk (thinking question: why is it n+1n+1?). Then the temptemp sequence is transformed according to this rule, and the sub-array formed by the elements of the first kk bits (ie except the last sentinel) is added to the answer each time. Every time we transform, we find out the jj of the first aj+1≠aj+1aj​+1​=aj+1​, so that ajaj​ increases by 11, and at the same time for i∈[0,j−1 The aiai of ]i∈[0,j−1] resets the number. This loops until all the elements in temptemp are the largest kk elements in nn.

Looking back at this thinking question, it is for us to judge the exit condition of the service. How do we judge that the enumeration has reached the termination condition? In fact, it is not directly judged by temptemp. We will look at the position of jj found each time. If j=kj=k, it means that all the numbers in [0,k−1][0,k−1] are The last kk digits that are smaller than the kk-th digit. At this time, we cannot find any scheme whose lexicographical order is greater than the current scheme. End the enumeration.

3. Code implementation

```java class Solution { List temp = new ArrayList(); List> ans = new ArrayList>();

public List<List<Integer>> combine(int n, int k) {
    List<Integer> temp = new ArrayList<Integer>();
    List<List<Integer>> ans = new ArrayList<List<Integer>>();
    // 初始化
    // 将 temp 中 [0, k - 1] 每个位置 i 设置为 i + 1,即 [0, k - 1] 存 [1, k]
    // 末尾加一位 n + 1 作为哨兵
    for (int i = 1; i <= k; ++i) {
        temp.add(i);
    }
    temp.add(n + 1);

    int j = 0;
    while (j < k) {
        ans.add(new ArrayList<Integer>(temp.subList(0, k)));
        j = 0;
        // 寻找第一个 temp[j] + 1 != temp[j + 1] 的位置 t
        // 我们需要把 [0, t - 1] 区间内的每个位置重置成 [1, t]
        while (j < k && temp.get(j) + 1 == temp.get(j + 1)) {
            temp.set(j, j + 1);
            ++j;
        }
        // j 是第一个 temp[j] + 1 != temp[j + 1] 的位置
        temp.set(j, temp.get(j) + 1);
    }
    return ans;
}

}

```

Guess you like

Origin blog.csdn.net/HHX_01/article/details/131285427