1. 491.递增子序列
1.1题目及讲解
给你一个整数数组 nums ,找出并返回所有该数组中不同的递增子序列,递增子序列中 至少有两个元素 。你可以按 任意顺序 返回答案。
数组中可能含有重复元素,如出现两个整数相等,也可以视作递增序列的一种特殊情况。
题目链接/文章讲解:https://programmercarl.com/0491.%E9%80%92%E5%A2%9E%E5%AD%90%E5%BA%8F%E5%88%97.html
视频讲解:https://www.bilibili.com/video/BV1EG4y1h78v
1.2代码实现
思路:
- 包含重复元素,那么就需要去重
- 要找递增子序列,那么就不能对原序列排序了,之前的去重方式也就不适用了
- 同一父节点下的同层上使用过的元素就不能再使用了
- 这里的去重是针对树层的,可以每层定义一个集合,同一层中,取过的元素就放到集合中
- 要找子序列,那么同一个元素就不能重复用,需要使用startindex字段
方法一:使用集合去重
#使用集合去重
nums = [4,6,7,7]
path = []
res = []
def back(startindex):
if len(path) > 1:
res.append(path[:])
seen = set()
for i in range(startindex, len(nums)):
if (path and nums[i] < path[-1]) or nums[i] in seen:
continue
path.append(nums[i])
seen.add(nums[i])
back(i + 1)
path.pop() # 这里不需要回溯seen,因为seen在每层都定义了一个空集合,回溯是发生在不同层中
back(0)
print(res)
注意:
seen = set() 是记录本层元素是否重复使用,新的一层seen都会重新定义(清空),所以要知道seen 只负责本层,那么跨层时也就不需要回溯了
方法二:使用数组去重
nums = [4,6,7,7]
path = []
res = []
def back(startindex):
if len(path) > 1:
res.append(path[:])
used = [0]*201 # 使用数组来进行去重操作,题目说数值范围[-100, 100]
for i in range(startindex, len(nums)):
# 这里用used[nums[i]+100]是因为0-99的索引需要用来存放负数,从索引下标100开始存放0-100的数字
if (path and nums[i] < path[-1]) or used[nums[i]+100] == 1:
continue
path.append(nums[i])
used[nums[i]+100] = 1
back(i + 1)
path.pop() # 这里不需要回溯seen,因为seen在每层都定义了一个空集合,回溯是发生在不同层中
back(0)
print(res)
总结:
数组,set,map都可以做哈希表,而且数组干的活,map和set都能干,但如果数值范围小的话能用数组尽量用数组。
2. 46. 全排列
2.1题目及讲解
给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。
题目链接/文章讲解:https://programmercarl.com/0046.%E5%85%A8%E6%8E%92%E5%88%97.html
视频讲解:https://www.bilibili.com/video/BV19v4y1S79W
2.2代码实现
思路:
- 全排列需要考虑顺序[1,2]和[2,1]是两个不同的结果,所以树层方向不需要startindex来控制元素的选取(startIndex 是为了防止出现重复的组合)
- 树枝方向,前面取过的元素不能重复取,也就是一个元素不能被使用两次,所以需要一个used数组,标记已经选择的元素
nums = [1,2,3]
path = []
res = []
used = [0]*len(nums)
def back():
if len(path)==len(nums):
res.append(path[:])
for i in range(0, len(nums)):
if used[i]:
continue
path.append(nums[i])
used[i] = 1
back()
path.pop()
used[i] = 0
back()
print(res)
3. 47. 全排列 II
3.1题目及讲解
给定一个可包含重复数字的序列 nums ,按任意顺序 返回所有不重复的全排列。
题目链接/文章讲解:https://programmercarl.com/0047.%E5%85%A8%E6%8E%92%E5%88%97II.html
视频讲解:https://www.bilibili.com/video/BV1R84y1i7Tm
3.2代码实现
思路:
序列存在重复元素,与上一题全排列相比,多了去重的操作
- 需要在树层方向进行去重,[4,7,6,7],这里不去重一定会出现[4,7](4和第一个7的子序列)和[4,7](4和第二个7的子序列)的重复结果
- 注意去重前需要对序列进行排序,这样才方便通过相邻的节点来判断是否重复使用了
nums = [1,1,2]
path = []
res = []
used = [0]*len(nums)
nums.sort()
def back():
if len(path) == len(nums):
res.append(path[:])
for i in range(0,len(nums)):
if i > 0 and nums[i] == nums[i-1] and used[i-1] == 0: # 树层去重
continue
if used[i]: # 判断元素是否已经被取过,已经取过的数不再重复取
continue
path.append(nums[i])
used[i] = 1
back()
path.pop()
used[i] = 0
back()
print(res)