c语言全排列算法_回溯算法(backtracking )解决子集和、全排列、组合数之和等问题...

12e4c5b34b2201c717058df157e97c25.png

目录

  • LeetCode 78. Subsets
    • 回溯解法
    • 递归解法
  • LeetCode 90. Subsets II
    • 回溯解法
    • 递归解法
  • LeetCode 46. Permutations
  • LeetCode 47. Permutations II
  • LeetCode 784. Letter Case Permutation
  • LeetCode 39. Combination Sum
  • LeetCode 40. Combination Sum II
  • LeetCode 131. Palindrome Partitioning
  • 回溯问题一般解法

LeetCode 78. Subsets

题目要求

Given a set of **distinct** integers, *nums*, return all possible subsets (the power set).
**Note:** The solution set must not contain duplicate subsets.
  • 参数数组中无重复数字
  • 结果集中要求无重复子集

回溯解法

class 

另一种风格的dfs+回溯,更好理解,但会稍微慢一些。

class 

递归解法

很多回溯问题可以用递归解决,所以我就将我写的递归贴出来吧

也叫BFS(?)。

思路

当我们遍历数组中的每一个元素,我们可以选择也可以不选择:

  • 如果我们不选择,那么就保持结果集中不变。
  • 如果我们选择,那么就出现已有结果集中的所有子集分别加上这个元素,就生成了包含这个元素的新的子集;

将以上两种情况加起来就是对当前元素处理过的新的结果集。

对了,因为空集 [], 也是符合的,并且我们要使用它对结果集初始化。{[]}

比如对于数组{1, 2, 3},遍历到第一个元素后结果集为 {[], [1]}, 遍历到第二元素结果集变为:{[], [1], [2], [1, 2]}, 以此类推。

class 

LeetCode 90.Subsets II

回溯解法

参数数组中可能含有重复数字(这是与上一题(78)中不一样的地方):

1. 子集中可以有重复数字,但返回的结果中不能含有相同的子集 所以我们用直接用链表作为结果集就有可能使前后两个相同的子集先后放入,导致出现重复子集,因此这里我们使用set作为结果集,以免相同子集加入,返回时转化为链表

2. 此外,由于参数提供的数组并不是有序的,这就导致出现很多含有相同数据集但顺序不同的子集,所以,我们可以在dfs前对数组进行一个排序,这样可以保证只要两个子集包含相同的数字,那么他们的顺序也会相同,故不可以连续加入结果集中

```java

class 

for循环方式回溯

```java

class 

递归解法

我们再用递归的方式做一下,因为数组中包含重复数字,所以我们遍历时,如果第i个元素和第i+1个元素相同,就会出现重复。

比如数组{1, 2, 2}
初始 :{[]}
第1个元素:{[], [1]}
第2个元素:{[], [1], [2], [1, 2]}
第3个元素:{[], [1], [2], [1, 2],[2], [1, 2], [2, 2], [1, 2, 2]} //出现重复
那么规避的方式也很简单:当我们判断出当前元素和上一个元素相同时,我们仅仅将其插入到上一步生成的元素。
对应上面的例子就是仅仅将其插入或不插入这些子集:{[2], [1, 2]}
那么遍历完第3个元素后的正确结果应该是:{[], [1], [2], [1, 2], [2, 2], [1, 2, 2]}

```java

class 

LeetCode 46. Permutations

题目要求:给定一个不包含重复数字的数据集,要求返回这个数据集的全排列。

思路

整体还是一个 dfs+回溯 的思路

用一个链表存储新加入的数字,当链表长度等于数组长度,表明生成一个新的排列

由于原数据集(数组)中无重复数字),那么我们在判断一个数字是否已经加入时就可以直接判断链表中是否已经包含这个数字,若不包含,则证明还没加入;

但是,当遇到数据集中含有重复数字的时候,就不可以这样判断了,因为一个数字可能有两次或多次,当我们判断链表中已经有了这个数字,那这个数字第二次出现就五分啊加入,

这时候我们可以用一个标记数组(boolean类型的数组)标记每个数字是否已经加入。

```java

class 

LeetCode 47. Permutations II

题目要求:给定一个可能含重复数字的数据集,要求返回这个数据集的全排列。

思路

整体还是一个 dfs+回溯 的思路

用一个链表存储新加入的数字,当链表长度等于数组长度,表明生成一个新的排列

遇到数据集中含有重复数字的时候,就不可以这样判断了,因为一个数字可能有两次或多次,当我们判断链表中已经有了这个数字,那这个数字第二次出现就五分啊加入,

这时候我们可以用一个标记数组(boolean类型的数组)标记每个数字是否已经加入。

两外需要注意的是,当遇到两个重复的数据,先加入a后加入b和先加入b后加入a会出现相同的排列,所以需要使用集合类型保存结果集,防止重复排列加入。

```java

class 

for循环方式回溯

好处:可以在遍历时就对重复情况进行处理

```java

class 

LeetCode 784. Letter Case Permutation

题目要求:给定一个字符串,将其中的字母变为大写或者小写可以变成另一个字符串,要求返回所有可能的字符串的集合。

思路:典型的dfs+回溯,只是在每次处理时需要判断此处的字符是字母还是数字,数字直接追加,字母则可以分为大写和小写。

```java

class 

LeetCode 39. Combination Sum

题目要求:给一个无重复数字的候选数据集和一个目标数字,要求从候选数据集中选出相加的和等于目标数字的数据集的所有情况,候选数据集中的数字可以被重复使用。

思路

这还是一个“组合”问题,对于每个元素,选还是不选,对于计算机来说当然是每种情况都要考虑,所以我们依然考虑dfs+回溯的思想,但是本题中每个数字可以被重复使用,dfs考虑起来不是那么方便。所以我们换一种方式。

采用迭代+dfs+回溯的方式。也就是for循环方式的回溯。

dfs每次需要一个起使偏移量,那么我们每次递归的时候将起使偏移量设置为原值就实现了数字的重复使用。

```java

class 

LeetCode 40. Combination Sum II

本题是39题的变种,和 39 题有两点不同:

  • 提供的候选数可能重复;
  • 每个数字不可重复使用。

因此做出相应修改就可以。

```java

class 

每个数字不可重复修改还可以通过另一种方式:

for 循环的目的是: 每次循环找出以第i个数字开始的序列,所以当第i个数字和第i-1个数字相同时,由于后续的回溯处理是一样的,就会生成一系列重复的序列

因此,这种情况下可以直接跳过:

```java

class 

当然这种数字不重复使用的,也可以用我们笨笨的dfs+回溯,那就要考set去重了

```java

class 

LeetCode 131. Palindrome Partitioning

题目要求:将一个给定的字符串,分割成若干个子字符串,使得每一个字符串都为回文字符串,返回所有的分割情况。

```java

class 

## 回溯问题一般解法

```java

// 回溯思想解法一般可以归纳为以下思路:

回溯问题的很多变种还是很有意思的,一般也能用递归方式解决。本文将继续完善。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值