【代码随想录】d29-回溯算法-part05-python

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)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值