前言>写此文章的主旨中心是为了给阅读的同学提供一个只有干货+实战的心得体会, 方便没有自己的学习路线的同学能够快速了解一些问题的核心以及解决问题的思路.
当然了这些内容也不是自己思考出来的,也是通过学习别人的内容总结归纳出来的,所以对于博主来说也是一个学习成长以及记录的过程, 有一些不足之处还请谅解, 希望大家都能共同进步, 汲取更多的知识, 早日找到中意的工作~
排列, 组合, 子集 问题都是针对给定的数组, 按照一定的规则选取出来组成一定的形式满足题意.
主体的题型形式大致分为以下几种:
延伸形式均以组合形式描述.
1)元素无重复不可复选: 比如组合[2,3,6,7] 组成和为7的组合 只有7一种.
2)元素可重复不可复选,此种形式每个元素仅可使用一次: 比如组合[2,2,3,6,7] 组成和为7的组合, 可以有 [2,2,3] ,[7]
3)元素不重复可复选:比如组合[2,3,6,7] 组成和为7的组合 同样有 [2,2,3]与7两种
4)元素可重复可复选: 由于复选即相当于重复, 所以此种形式可以与第三种形式归为一类.
下面以几道实际的应用场景题举例:
1) 比如leetcode 216组合总和III 明确的元素不重复且不可复选问题
2) 比如leetcode 40组合总和II 明确的元素可重复不可复选的问题
3) 比如leetcode 39组合总和 明确的元素无重复可复选的问题
以上只是对比了一下对于各种题形的变种, 下面我们聊聊 排列 / 组合 / 子集他们之间的区别
首先本质上都是以回溯的方式进行的遍历操作
差别仅在于
排列 的方式 无论如何都是选所有元素往下走, 我们通常都是以一个 访问过的boolean类型数组 vis[] 来表示对应位置是否被访问过
而 组合 和 子集 问题, 他们不需要全部的元素 以leetcode47全排列II举例 (46题我们之前已举证)
比如子集 在选择路径上 我们的选择种类在逐层递减, 每一次进入递归 选择路径都会相应的减少
比如组合 也是同样的问题, 它与子集的差别仅在于, 子集的遍历, 每一次进入递归函数的路径都是结果, 无需加入结束条件, 而组合的问题 需要满足结束条件才能记录为结果的一条路径 这就是二者的根本差别, 关于组合的问题, 上述描述中已经举例, 此处不再赘述.
此处延伸一个技巧问题:
用来解决可重复元素的剪枝, 代码上主要使用我们上文提到的vis数组
即 if(vis[i] || (i > 0 && nums[i] == nums[i - 1] && !vis[i]))
理解起来的意思就两重
1)已经访问过的元素 不再继续加入
2)当前元素等于前置元素, 且前置元素并没有被访问过 不再继续加入.