Leetcode93
https://leetcode.cn/problems/restore-ip-addresses/
有效 IP 地址 正好由四个整数(每个整数位于 0
到 255
之间组成,且不能含有前导 0
),整数之间用 '.'
分隔。
- 例如:
"0.1.2.201"
和"192.168.1.1"
是 有效 IP 地址,但是"0.011.255.245"
、"192.168.1.312"
和"192.168@1.1"
是 无效 IP 地址。
给定一个只包含数字的字符串 s
,用以表示一个 IP 地址,返回所有可能的有效 IP 地址,这些地址可以通过在 s
中插入 '.'
来形成。你 不能 重新排序或删除 s
中的任何数字。你可以按 任何 顺序返回答案。
思路:
说实话 这道题没太懂。。。。
等我二刷再来啃
Leetcode78子集
https://leetcode.cn/problems/subsets/
给你一个整数数组 nums
,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。
解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。
思路:
如果把 子集问题、组合问题、分割问题都抽象为一棵树的话,那么组合问题和分割问题都是收集树的叶子节点,而子集问题是找树的所有节点!
其实子集也是一种组合问题,因为它的集合是无序的,子集{1,2} 和 子集{2,1}是一样的。
那么既然是无序,取过的元素不会重复取,写回溯算法的时候,for就要从startIndex开始,而不是从0开始!
什么时候for可以从0开始呢?
求排列问题的时候,就要从0开始,因为集合是有序的,{1, 2} 和{2, 1}是两个集合。
同时 转换成树形结构来看这道题,一共有两个维度需要考虑,一个是树层去重,一个是树枝去重。
如图所示:
回溯三部曲:
递归参数:nums,startindex表示下一次循环从哪儿开始
终止条件:如果startindex的位置已经超出了我们数组的长度,就代表已经数组中的元素已经取完了,就直接return
单层循环逻辑:每次递归i要从i+1的位置开始搜索,因为我们一个元素不能使用多次
代码:
def subsets(self, nums: List[int]) -> List[List[int]]:
result = []
path = []
def traversal(nums,startindex):
#这里是浅拷贝 !!!!!赋值的是地址 有一个大坑
result.append(path[:])
#递归终止条件
if startindex >=len(nums):
return
for i in range(startindex,len(nums)):
path.append(nums[i])
traversal(nums,i+1)
path.pop()
traversal(nums,0)
return result
Leetcode90子集
给你一个整数数组 nums
,其中可能包含重复元素,请你返回该数组所有可能的子集(幂集)。
解集 不能 包含重复的子集。返回的解集中,子集可以按 任意顺序 排列。
思路:首先这道题是有一个重复的元素,并且要求不能有重复的子集,那么就说明了这里会产生一个去重的逻辑,既然是有去重,还有数组,那么首先要对数组进行一个排序的操作,这样子相邻的元素才会挨在一起,方便我们对去重做出判断
同时,和上一道题一样我们转换为树形结构的思路来想,这里有两个维度需要考虑。一个是树枝,一个是树层
从图中可以看出,我们的树枝部分是可以取重复元素的,但是树层不可以取重复元素,也就是说我们的去重逻辑应该放在for循环的下面,当前元素如果等于前一个元素,并且i>startindex的时候
我们跳过本次循环。这就是本题最关键的去重部分
def subsetsWithDup(self, nums: List[int]) -> List[List[int]]:
result = []
path = []
def traversal(nums,startindex):
nums.sort()
result.append(path[:])
#终止条件
if startindex>=len(nums):
return
for i in range(startindex,len(nums)):
if i > startindex and nums[i]==nums[i-1]:
continue
path.append(nums[i])
traversal(nums,i+1)
path.pop()
traversal(nums,0)
return result