Algorithm and Data Structure Interview Guide - Subsets and Questions

subset sum problem

No duplicate elements

!!! question

给定一个正整数数组 `nums` 和一个目标正整数 `target` ,请找出所有可能的组合,使得组合中的元素和等于 `target` 。给定数组无重复元素,每个元素可以被选取多次。请以列表形式返回这些组合,列表中不应包含重复组合。

For example, the input set { 3 , 4 , 5 } \{3, 4, 5\}{ 3,4,5 } and the target integer9 99 , the solution is{ 3 , 3 , 3 } , { 4 , 5 } \{3, 3, 3\}, \{4, 5\}{ 3,3,3},{ 4,5 } . The following two points need to be noted.

  • Elements in the input collection can be repeatedly selected an unlimited number of times.
  • Subsets do not distinguish the order of elements, such as { 4 , 5 } \{4, 5\}{ 4,5 } and{ 5 , 4 } \{5, 4\}{ 5,4 } is the same subset.

Refer to the full permutation solution

Similar to the total permutation problem, we can imagine the generation process of the subset as the result of a series of selections, and update the "sum of elements" in real time during the selection process. When the sum of elements is equal to , the subset is recorded in the result list target.

Different from the total permutation problem, the elements in the set of this problem can be selected infinitely , so there is no need to use selecteda Boolean list to record whether the element has been selected. We can make slight modifications to the full arrangement code and initially obtain the problem-solving code.

=== “Python”

```python title="subset_sum_i_naive.py"
[class]{}-[func]{backtrack}

[class]{}-[func]{subset_sum_i_naive}
```

=== “C++”

```cpp title="subset_sum_i_naive.cpp"
[class]{}-[func]{backtrack}

[class]{}-[func]{subsetSumINaive}
```

=== “Java”

```java title="subset_sum_i_naive.java"
[class]{subset_sum_i_naive}-[func]{backtrack}

[class]{subset_sum_i_naive}-[func]{subsetSumINaive}
```

=== “C#”

```csharp title="subset_sum_i_naive.cs"
[class]{subset_sum_i_naive}-[func]{backtrack}

[class]{subset_sum_i_naive}-[func]{subsetSumINaive}
```

=== “Go”

```go title="subset_sum_i_naive.go"
[class]{}-[func]{backtrackSubsetSumINaive}

[class]{}-[func]{subsetSumINaive}
```

=== “Swift”

```swift title="subset_sum_i_naive.swift"
[class]{}-[func]{backtrack}

[class]{}-[func]{subsetSumINaive}
```

=== “JS”

```javascript title="subset_sum_i_naive.js"
[class]{}-[func]{backtrack}

[class]{}-[func]{subsetSumINaive}
```

=== “TS”

```typescript title="subset_sum_i_naive.ts"
[class]{}-[func]{backtrack}

[class]{}-[func]{subsetSumINaive}
```

=== “Dart”

```dart title="subset_sum_i_naive.dart"
[class]{}-[func]{backtrack}

[class]{}-[func]{subsetSumINaive}
```

=== “Rust”

```rust title="subset_sum_i_naive.rs"
[class]{}-[func]{backtrack}

[class]{}-[func]{subset_sum_i_naive}
```

=== “C”

```c title="subset_sum_i_naive.c"
[class]{}-[func]{backtrack}

[class]{}-[func]{subsetSumINaive}
```

=== “Zig”

```zig title="subset_sum_i_naive.zig"
[class]{}-[func]{backtrack}

[class]{}-[func]{subsetSumINaive}
```

Enter the array [3, 4, 5] [3, 4, 5] to the above code[3,4,5 ] and target element9 99 , the output results are[3, 3, 3], [4, 5], [5, 4] [3, 3, 3], [4, 5], [5, 4][3,3,3],[4,5],[5,4 ] . Although it successfully found all the sums of 9 9A subset of 9 , but there are duplicate subsets[4, 5] [4, 5][4,5 ] and[ 5 , 4 ] [5, 4][5,4]

This is because the search process is selection-order sensitive, whereas subsetting is not selection-order sensitive. As shown in the figure below, first select 4 4Choose5 after 4 55 and first pick5 5Choose4 after 5 44 are two different branches, but both correspond to the same subset.

Insert image description here

In order to remove duplicate subsets, a straightforward idea is to deduplicate the result list . However, this method is very inefficient for two reasons.

  • When there are many array elements, especially when is targetlarge, the search process will produce a large number of repeated subsets.
  • Comparing the similarities and differences of subsets (arrays) is very time-consuming. You need to sort the array first and then compare the similarities and differences of each element in the array.

Duplicate subset pruning

We consider deduplication through pruning during the search process . Observe the figure below, duplicate subsets are generated when array elements are selected in different orders, such as the following situation.

  1. When choosing 3 3 in the first and second rounds respectively3 and4 44 , all subsets containing these two elements will be generated, recorded as[3, 4, …] [3, 4, \dots][3,4,]
  2. After that, when 4 4 is selected in the first round4 , then 3 3 should be skipped in the second round3 , because this selection produces the subset[ 4 , 3 , … ] [4, 3, \dots][4,3,] and1.the subsets generated in are exactly duplicates.

In the search, the choices at each level are tried one by one from left to right, so the branches further to the right are pruned.

  1. Choose 3 3 in the first two rounds3 and5 55 , generate subset[3, 5, …] [3, 5, \dots][3,5,]
  2. Pick 4 4 in first two rounds4 and5 55 , generate subset[4, 5, …] [4, 5, \dots][4,5,]
  3. If you choose 5 5 in the first round5 ,then 3 3 should be skipped in the second round3 and4 44 , because the subset[ 5 , 3 , … ] [5, 3, \dots][5,3,] and[ 5 , 4 , … ] [5, 4, \dots][5,4,] is an exact duplicate of the subset described in stepsand1..2.

Insert image description here

To summarize, given the input array [x 1, x 2, …, xn] [x_1, x_2, \dots, x_n][x1,x2,,xn] , assuming the selection sequence during the search process is[ xi 1 , xi 2 , … , xim ] [x_{i_1}, x_{i_2}, \dots, x_{i_m}][xi1,xi2,,xim] , then the selection sequence needs to satisfyi 1 ≤ i 2 ≤ ⋯ ≤ im i_1 \leq i_2 \leq \dots \leq i_mi1i2im, selection sequences that do not meet this condition will cause duplication and should be pruned .

Code

To implement this pruning, we initialize variables startthat indicate the starting point of the traversal. When making a choice xi x_{i}xiAfter that, set the next round from index iii starts traversing. In this way, the selection sequence satisfiesi 1 ≤ i 2 ≤ ⋯ ≤ im i_1 \leq i_2 \leq \dots \leq i_mi1i2im, thus ensuring that the subset is unique.

In addition, we have also made the following two optimizations to the code.

  • Before starting the search, sort the array first nums. When traversing all selections, the loop ends directly when the sum of the subsets exceedstarget , because the following elements are larger, and the sums of their subsets will definitely exceed target.
  • Omitting elements and variables total, count the sum of elements bytarget performing subtraction on , when targetequals 0 0Record the solution at 0 o'clock.

=== “Python”

```python title="subset_sum_i.py"
[class]{}-[func]{backtrack}

[class]{}-[func]{subset_sum_i}
```

=== “C++”

```cpp title="subset_sum_i.cpp"
[class]{}-[func]{backtrack}

[class]{}-[func]{subsetSumI}
```

=== “Java”

```java title="subset_sum_i.java"
[class]{subset_sum_i}-[func]{backtrack}

[class]{subset_sum_i}-[func]{subsetSumI}
```

=== “C#”

```csharp title="subset_sum_i.cs"
[class]{subset_sum_i}-[func]{backtrack}

[class]{subset_sum_i}-[func]{subsetSumI}
```

=== “Go”

```go title="subset_sum_i.go"
[class]{}-[func]{backtrackSubsetSumI}

[class]{}-[func]{subsetSumI}
```

=== “Swift”

```swift title="subset_sum_i.swift"
[class]{}-[func]{backtrack}

[class]{}-[func]{subsetSumI}
```

=== “JS”

```javascript title="subset_sum_i.js"
[class]{}-[func]{backtrack}

[class]{}-[func]{subsetSumI}
```

=== “TS”

```typescript title="subset_sum_i.ts"
[class]{}-[func]{backtrack}

[class]{}-[func]{subsetSumI}
```

=== “Dart”

```dart title="subset_sum_i.dart"
[class]{}-[func]{backtrack}

[class]{}-[func]{subsetSumI}
```

=== “Rust”

```rust title="subset_sum_i.rs"
[class]{}-[func]{backtrack}

[class]{}-[func]{subset_sum_i}
```

=== “C”

```c title="subset_sum_i.c"
[class]{}-[func]{backtrack}

[class]{}-[func]{subsetSumI}
```

=== “Zig”

```zig title="subset_sum_i.zig"
[class]{}-[func]{backtrack}

[class]{}-[func]{subsetSumI}
```

As shown in the figure below, the array [3, 4, 5] [3, 4, 5][3,4,5 ] and target element9 99The overall traceback process after entering the above code.

Insert image description here

Consider the case of repeated elements

!!! question

给定一个正整数数组 `nums` 和一个目标正整数 `target` ,请找出所有可能的组合,使得组合中的元素和等于 `target` 。**给定数组可能包含重复元素,每个元素只可被选择一次**。请以列表形式返回这些组合,列表中不应包含重复组合。

Compared with the previous question, the input array of this question may contain repeated elements , which introduces new problems. For example, given the array [4, 4^, 5] [4, \hat{4}, 5][4,4^,5 ] and target element9 99 , then the output result of the existing code is[ 4 , 5 ] , [ 4 ^ , 5 ] [4, 5], [\hat{4}, 5][4,5],[4^,5 ] , a duplicate subset appears.

The reason for this duplication is that equal elements are selected multiple times in a round . In the picture below, there are three choices in the first round, two of which are 4 44 , two repeated search branches will be generated, thus outputting repeated subsets; similarly, the two4 44 will also produce duplicate subsets.

Insert image description here

Equal element pruning

To solve this problem, we need to restrict equal elements to be selected only once per round . The implementation is clever: since the array is sorted, equal elements are adjacent. This means that in a certain round of selection, if the current element is equal to the element to its left, it means that it has already been selected, so the current element is skipped directly.

At the same time, each array element in this question can only be selected once . Fortunately, we can also use variables startto satisfy this constraint: when making a choice xi x_{i}xiAfter that, set the next round from index i + 1 i + 1i+1 starts traversing backward. This removes duplicate subsets and avoids repeated selection of elements.

Code

=== “Python”

```python title="subset_sum_ii.py"
[class]{}-[func]{backtrack}

[class]{}-[func]{subset_sum_ii}
```

=== “C++”

```cpp title="subset_sum_ii.cpp"
[class]{}-[func]{backtrack}

[class]{}-[func]{subsetSumII}
```

=== “Java”

```java title="subset_sum_ii.java"
[class]{subset_sum_ii}-[func]{backtrack}

[class]{subset_sum_ii}-[func]{subsetSumII}
```

=== “C#”

```csharp title="subset_sum_ii.cs"
[class]{subset_sum_ii}-[func]{backtrack}

[class]{subset_sum_ii}-[func]{subsetSumII}
```

=== “Go”

```go title="subset_sum_ii.go"
[class]{}-[func]{backtrackSubsetSumII}

[class]{}-[func]{subsetSumII}
```

=== “Swift”

```swift title="subset_sum_ii.swift"
[class]{}-[func]{backtrack}

[class]{}-[func]{subsetSumII}
```

=== “JS”

```javascript title="subset_sum_ii.js"
[class]{}-[func]{backtrack}

[class]{}-[func]{subsetSumII}
```

=== “TS”

```typescript title="subset_sum_ii.ts"
[class]{}-[func]{backtrack}

[class]{}-[func]{subsetSumII}
```

=== “Dart”

```dart title="subset_sum_ii.dart"
[class]{}-[func]{backtrack}

[class]{}-[func]{subsetSumII}
```

=== “Rust”

```rust title="subset_sum_ii.rs"
[class]{}-[func]{backtrack}

[class]{}-[func]{subset_sum_ii}
```

=== “C”

```c title="subset_sum_ii.c"
[class]{}-[func]{backtrack}

[class]{}-[func]{subsetSumII}
```

=== “Zig”

```zig title="subset_sum_ii.zig"
[class]{}-[func]{backtrack}

[class]{}-[func]{subsetSumII}
```

The following figure shows the array [4, 4, 5] [4, 4, 5][4,4,5 ] and target element9 9The backtracking process of 9 includes a total of four pruning operations. Please combine the diagrams with code comments to understand the entire search process and how each pruning operation works.

Insert image description here

Guess you like

Origin blog.csdn.net/zy_dreamer/article/details/132865504