6
6
7
7
## 模板
8
8
9
- ``` go
10
- result = []
11
- func backtrack (选择列表,路径):
9
+ ``` c++
10
+ vector<T> result;
11
+ void backtrack (选择列表,路径):
12
12
if 满足结束条件:
13
- result.add (路径)
13
+ result.push_back (路径);
14
14
return
15
15
for 选择 in 选择列表:
16
16
做选择
@@ -30,73 +30,53 @@ func backtrack(选择列表,路径):
30
30
31
31

32
32
33
- ``` go
34
- func subsets (nums []int ) [][]int {
35
- // 保存最终结果
36
- result := make ([][]int , 0 )
37
- // 保存中间结果
38
- list := make ([]int , 0 )
39
- backtrack (nums, 0 , list, &result)
40
- return result
33
+ ```c++
34
+ vector<vector<int>> subsets(vector<int>& nums) {
35
+ vector<vector<int>> result;
36
+ vector<int> track;
37
+ backtrack(nums, 0, track, result);
38
+ return result;
41
39
}
42
40
43
- // nums 给定的集合
44
- // pos 下次添加到集合中的元素位置索引
45
- // list 临时结果集合(每次需要复制保存)
46
- // result 最终结果
47
- func backtrack (nums []int , pos int , list []int , result *[][]int ) {
48
- // 把临时结果复制出来保存到最终结果
49
- ans := make ([]int , len (list))
50
- copy (ans, list)
51
- *result = append (*result, ans)
52
- // 选择、处理结果、再撤销选择
53
- for i := pos; i < len (nums); i++ {
54
- list = append (list, nums[i])
55
- backtrack (nums, i+1 , list, result)
56
- list = list[0 : len (list)-1 ]
57
- }
41
+ void backtrack(const vector<int> &nums, int pos, vector<int> &track, vector<vector<int>> &result) {
42
+ // 插入当前组合
43
+ result.push_back(track);
44
+ for (int i = pos; i < nums.size(); ++i) {
45
+ // 每个值,都有两种可能,包含和不包含
46
+ // 先压入
47
+ track.push_back(nums[i]);
48
+ // 递归处理包含当前值的情况
49
+ backtrack(nums, i + 1, track, result);
50
+ // 弹出,向后遍历,处理不包含的情况
51
+ track.pop_back();
52
+ }
58
53
}
59
54
```
60
55
61
56
### [ subsets-ii] ( https://leetcode-cn.com/problems/subsets-ii/ )
62
57
63
58
> 给定一个可能包含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。说明:解集不能包含重复的子集。
64
59
65
- ``` go
66
- import (
67
- " sort"
68
- )
69
-
70
- func subsetsWithDup (nums []int ) [][]int {
71
- // 保存最终结果
72
- result := make ([][]int , 0 )
73
- // 保存中间结果
74
- list := make ([]int , 0 )
75
- // 先排序
76
- sort.Ints (nums)
77
- backtrack (nums, 0 , list, &result)
78
- return result
60
+ ``` c++
61
+ // 对数据进行排序来判断重复元素
62
+ vector<vector<int >> subsetsWithDup (vector<int > &nums) {
63
+ vector<vector<int >> result;
64
+ vector<int > track;
65
+ sort(nums.begin(), nums.end());
66
+ backtrack(nums, 0, track, result);
67
+ return result;
79
68
}
80
69
81
- // nums 给定的集合
82
- // pos 下次添加到集合中的元素位置索引
83
- // list 临时结果集合(每次需要复制保存)
84
- // result 最终结果
85
- func backtrack (nums []int , pos int , list []int , result *[][]int ) {
86
- // 把临时结果复制出来保存到最终结果
87
- ans := make ([]int , len (list))
88
- copy (ans, list)
89
- *result = append (*result, ans)
90
- // 选择时需要剪枝、处理、撤销选择
91
- for i := pos; i < len (nums); i++ {
92
- // 排序之后,如果再遇到重复元素,则不选择此元素
93
- if i != pos && nums[i] == nums[i-1 ] {
94
- continue
95
- }
96
- list = append (list, nums[i])
97
- backtrack (nums, i+1 , list, result)
98
- list = list[0 : len (list)-1 ]
99
- }
70
+ void backtrack(const vector<int > &nums, int pos, vector<int > &track, vector<vector<int >> &result) {
71
+ result.push_back(track);
72
+ for (int i = pos; i < nums.size(); ++i) {
73
+ if (i != pos && nums[ i] == nums[ i - 1] ) {
74
+ continue;
75
+ }
76
+ track.push_back(nums[ i] );
77
+ backtrack(nums, i + 1, track, result);
78
+ track.pop_back();
79
+ }
100
80
}
101
81
```
102
82
@@ -106,40 +86,51 @@ func backtrack(nums []int, pos int, list []int, result *[][]int) {
106
86
107
87
思路:需要记录已经选择过的元素,满足条件的结果才进行返回
108
88
109
- ``` go
110
- func permute (nums []int ) [][]int {
111
- result := make ([][]int , 0 )
112
- list := make ([]int , 0 )
113
- // 标记这个元素是否已经添加到结果集
114
- visited := make ([]bool , len (nums))
115
- backtrack (nums, visited, list, &result)
116
- return result
89
+ ```c++
90
+ /*
91
+ * 这个问题可以看作有 n 个排列成一行的空格,从左往右依此填入题目给定的 n 个数,每个数只能使用一次
92
+ *
93
+ * 回溯法
94
+ * 不同于上面的子集问题,撤销选择时弹出了事,这里撤销后还要留待后续组合使用
95
+ * 并且需要检查数字是否已经填入过
96
+ *
97
+ * 思路:
98
+ * backtrack(fillingPos, result),fillingPos为当前填入的位置
99
+ * 若fillingPos == n,则已经完成排列
100
+ * 否则,填入"未填入过"的数字,递归处理下一个位置,最后撤销当前选择,尝试以其他"未填入过"的数字来填入fillingPos
101
+ *
102
+ * 优化:
103
+ * 不使用额外的数组来维护已经填入过的数字,降低空间复杂度
104
+ * 对于nums、当前位置fillingPos
105
+ * [0, fillingPos - 1]——已填入过的数字
106
+ * [fillingPos, n - 1]——未填入过的数字
107
+ * 每次选择时把nums[i]与nums[fillingPos]进行交换
108
+ * 撤销选择时把nums[i]与nums[fillingPos]再交换一次即可
109
+ *
110
+ * 既然 [fillingPos, n - 1] 为未填入的数字
111
+ * 偷个鸡,撤销选择时不交换,直接 ++i 可否?
112
+ * 并不能
113
+ * 因为原本的 nums[i] 填入 fillingPos 这一可能性已经处理过了
114
+ * 如果不交换回来,for循环遍历的时候会再用到同一个数字!
115
+ */
116
+ vector<vector<int>> permute(vector<int> &nums) {
117
+ vector<vector<int>> result;
118
+ backtrack(result, nums, 0, nums.size());
119
+ return result;
117
120
}
118
121
119
- // nums 输入集合
120
- // visited 当前递归标记过的元素
121
- // list 临时结果集(路径)
122
- // result 最终结果
123
- func backtrack (nums []int , visited []bool , list []int , result *[][]int ) {
124
- // 返回条件:临时结果和输入集合长度一致 才是全排列
125
- if len (list) == len (nums) {
126
- ans := make ([]int , len (list))
127
- copy (ans, list)
128
- *result = append (*result, ans)
129
- return
122
+ void backtrack(vector<vector<int>> &result, vector<int> &nums, int fillingPos, int len) {
123
+ if (fillingPos == len) {
124
+ // n个数字都已排列完,保存并退出
125
+ result.emplace_back(nums);
126
+ return;
130
127
}
131
- for i := 0 ; i < len (nums); i++ {
132
- // 已经添加过的元素,直接跳过
133
- if visited[i] {
134
- continue
135
- }
136
- // 添加元素
137
- list = append (list, nums[i])
138
- visited[i] = true
139
- backtrack (nums, visited, list, result)
140
- // 移除元素
141
- visited[i] = false
142
- list = list[0 : len (list)-1 ]
128
+ for (int i = fillingPos; i < len; ++i) {
129
+ // 把nums[i]填入到当前位置
130
+ swap(nums[i], nums[fillingPos]);
131
+ backtrack(result, nums, fillingPos + 1, len);
132
+ // 把顺序恢复回去
133
+ swap(nums[i], nums[fillingPos]);
143
134
}
144
135
}
145
136
```
@@ -148,47 +139,41 @@ func backtrack(nums []int, visited []bool, list []int, result *[][]int) {
148
139
149
140
> 给定一个可包含重复数字的序列,返回所有不重复的全排列。
150
141
151
- ``` go
152
- import (
153
- " sort"
154
- )
155
-
156
- func permuteUnique (nums []int ) [][]int {
157
- result := make ([][]int , 0 )
158
- list := make ([]int , 0 )
159
- // 标记这个元素是否已经添加到结果集
160
- visited := make ([]bool , len (nums))
161
- sort.Ints (nums)
162
- backtrack (nums, visited, list, &result)
163
- return result
142
+ ``` c++
143
+ vector<vector<int >> permuteUnique (vector<int > &nums) {
144
+ vector<vector<int >> result;
145
+ vector<int > current;
146
+ vector<bool > visited(nums.size());
147
+ sort(nums.begin(), nums.end());
148
+ backtrack(nums, nums.size(), visited, current, result);
149
+ return result;
164
150
}
165
151
166
- // nums 输入集合
167
- // visited 当前递归标记过的元素
168
- // list 临时结果集
169
- // result 最终结果
170
- func backtrack (nums []int , visited []bool , list []int , result *[][]int ) {
171
- // 临时结果和输入集合长度一致 才是全排列
172
- if len (list) == len (nums) {
173
- subResult := make ([]int , len (list))
174
- copy (subResult, list)
175
- *result = append (*result, subResult)
176
- }
177
- for i := 0 ; i < len (nums); i++ {
178
- // 已经添加过的元素,直接跳过
179
- if visited[i] {
180
- continue
181
- }
182
- // 上一个元素和当前相同,并且没有访问过就跳过
183
- if i != 0 && nums[i] == nums[i-1 ] && !visited[i-1 ] {
184
- continue
185
- }
186
- list = append (list, nums[i])
187
- visited[i] = true
188
- backtrack (nums, visited, list, result)
189
- visited[i] = false
190
- list = list[0 : len (list)-1 ]
191
- }
152
+ void backtrack(vector<int > &nums, int len, vector<bool > &visited,
153
+ vector<int > ¤t, vector<vector<int >> &result) {
154
+ if (current.size() == nums.size()) {
155
+ result.emplace_back(current);
156
+ return;
157
+ }
158
+ for (int i = 0; i < nums.size(); ++i) {
159
+ // 已经在组合中
160
+ if (visited[ i] ) {
161
+ continue;
162
+ }
163
+ // 与上一个元素相同,并且没有访问过就跳过
164
+ // 没访问过?
165
+ // 重复元素,只有第一个元素考虑排列组合,后面的不再考虑以免重复
166
+ // 一旦第一个重复的元素被pop并设置visited为false,则其所有排列组合都已考虑完毕
167
+ // 跳过后续的重复元素
168
+ if (i > 0 && nums[ i] == nums[ i - 1] && !visited[ i - 1] ) {
169
+ continue;
170
+ }
171
+ current.emplace_back(nums[ i] );
172
+ visited[ i] = true;
173
+ backtrack(nums, len, visited, current, result);
174
+ visited[ i] = false;
175
+ current.pop_back();
176
+ }
192
177
}
193
178
```
194
179
0 commit comments