Leetcode-回溯典型题

LC031.下一个排列Next Permutation

一、题目

整数数组的一个 排列  就是将其所有成员以序列或线性顺序排列。

例如,arr = [1,2,3] ,以下这些都可以视作 arr 的排列:[1,2,3]、[1,3,2]、[3,1,2]、[2,3,1] 。
整数数组的 下一个排列 是指其整数的下一个字典序更大的排列。更正式地,如果数组的所有排列根据其字典顺序从小到大排列在一个容器中,那么数组的 下一个排列 就是在这个有序容器中排在它后面的那个排列。如果不存在下一个更大的排列,那么这个数组必须重排为字典序最小的排列(即,其元素按升序排列)。

例如,arr = [1,2,3] 的下一个排列是 [1,3,2] 。
类似地,arr = [2,3,1] 的下一个排列是 [3,1,2] 。
而 arr = [3,2,1] 的下一个排列是 [1,2,3] ,因为 [3,2,1] 不存在一个字典序更大的排列。
给你一个整数数组 nums ,找出 nums 的下一个排列。

必须 原地 修改,只允许使用额外常数空间。

示例 1:

输入:nums = [1,2,3]
输出:[1,3,2]
示例 2:

输入:nums = [3,2,1]
输出:[1,2,3]
示例 3:

输入:nums = [1,1,5]
输出:[1,5,1]

提示:

1 <= nums.length <= 100
0 <= nums[i] <= 100

二、实现方法

 这老哥题解比啥都清楚力扣icon-default.png?t=M0H8https://leetcode-cn.com/problems/next-permutation/solution/xia-yi-ge-pai-lie-suan-fa-xiang-jie-si-lu-tui-dao-/

func main()  {
	nums:=[]int{1,2,3}
	nextPermutation(nums)
	fmt.Println(nums)
}

func nextPermutation(nums []int){
	n:=len(nums)
	if n==1{
		return
	}
	i,j,k:=n-2,n-1,n-1
	// 找升序对
	// find: A[i]<A[j]
	for i>=0&&nums[i]>=nums[j]{
		i--
		j--
	}
	if i >= 0 { // 不是最后一个排列
		// find: A[i]<A[k]
		for nums[i] >= nums[k] {
			k--
		}
		// swap A[i], A[k]
		nums[i], nums[k] = nums[k], nums[i]
	}

	// reverse A[j:end]
	for i, j := j, len(nums)-1; i < j; i, j = i+1, j-1 {
		nums[i], nums[j] = nums[j], nums[i]
	}
}

 


LeetCode039.组合总和Combination Sum

一、题目

给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。

candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。 

对于给定的输入,保证和为 target 的不同组合数少于 150 个。

 

示例 1:

输入:candidates = [2,3,6,7], target = 7
输出:[[2,2,3],[7]]
解释:
2 和 3 可以形成一组候选,2 + 2 + 3 = 7 。注意 2 可以使用多次。
7 也是一个候选, 7 = 7 。
仅有这两种组合。
示例 2:

输入: candidates = [2,3,5], target = 8
输出: [[2,2,2,2],[2,3,3],[3,5]]
示例 3:

输入: candidates = [2], target = 1
输出: []
 

提示:

1 <= candidates.length <= 30
1 <= candidates[i] <= 200
candidate 中的每个元素都 互不相同
1 <= target <= 500

二、实现方法

方法一、回溯+剪枝

如果允许重复:

// 时间复杂度:O(S),其中 S 为所有可行解的长度之和。O(n×2^n)是一个比较松的上界,但实际运行情况是远远小于这个上界的。
// 空间复杂度:O(target)。除答案数组外,空间复杂度取决于递归的栈深度,在最差情况下需要递归 O(target) 层。
// 优化方法:剪枝--sum + candidates[i] <= target
func combinationSum(candidates []int, target int) [][]int {
	var res [][]int
	var path []int
	backtracking(&res,path,candidates,target,0,0)
	return res
}

func backtracking(res *[][]int,path []int,candidates []int,target int,startindex,sum int){
	// 结束条件
	if sum==target{
		temp:=make([]int,len(path))
		copy(temp,path)
		*res=append(*res,temp)
	}
	if sum>target{
		return
	}
	// 优化后
	for i:=startindex;i<len(candidates)&&sum + candidates[i] <= target;i++{
		// 优化前
		//for i:=startindex;i<len(candidates);i++{
		sum+=candidates[i]
		path=append(path,candidates[i])
		backtracking(res,path,candidates,target,i,sum)
		sum-=candidates[i]
		path=path[:len(path)-1]
	}
	return
}

如果不允许重复:(LC040)

加了一个map或者数组uesd 记录在该枝有没有用到,key为candidates的下标

在回溯前需要排序,如果当前值和前一个值相同,并且上一个值已经取值了,就可以取

举例(1,1);

只取第一个1和只取第二个1,虽然不是同一个1,但是path会重复,(1,x,x),(1,x,x);

所以如果要取第二个1,就是两个1都要取,前一个1没取值,第二个1不可以取,(1,1,x);

if i>0&&candidates[i]==candidates[i-1]&&used[i-1]==false{
		continue
}
// 时间复杂度:O(S),其中 S 为所有可行解的长度之和。O(n×2^n)是一个比较松的上界,但实际运行情况是远远小于这个上界的。
// 空间复杂度:O(n)。除答案数组外,我们需要 O(n) 的空间存储used。
// 优化方法:剪枝--sum + candidates[i] <= target
func combinationSum(candidates []int, target int) [][]int {
	var res [][]int
	var path []int
	sort.Ints(candidates)
	used:=make(map[int]bool)
	backtracking(&res,path,candidates,target,0,0,used)
	return res
}

func backtracking(res *[][]int,path []int,candidates []int,target int,startindex,sum int,used map[int]bool){
	// 结束条件
	if sum==target{
		temp:=make([]int,len(path))
		copy(temp,path)
		*res=append(*res,temp)
	}
	if sum>target{
		return
	}
	// 优化后
	for i:=startindex;i<len(candidates)&&sum + candidates[i] <= target;i++{
	// 优化前
	//for i:=startindex;i<len(candidates);i++{
		// 检查是否和上一个是否重复
		if i>0&&candidates[i]==candidates[i-1]&&used[i-1]==false{
			continue
		}
		used[i]=true
		sum+=candidates[i]
		path=append(path,candidates[i])
		// startIndex变为i+1,从下一元素开始
		backtracking(res,path,candidates,target,i+1,sum,used)
		sum-=candidates[i]
		path=path[:len(path)-1]
		used[i]=false
	}
	return
}

 


LC078.子集Subsets

一、题目

给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。

解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。

 

示例 1:

输入:nums = [1,2,3]
输出:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]
示例 2:

输入:nums = [0]
输出:[[],[0]]
 

提示:

1 <= nums.length <= 10
-10 <= nums[i] <= 10
nums 中的所有元素 互不相同

二、解决方法

方法一:回溯 

func main()  {
	nums:=[]int{1,2,3}
	fmt.Println(Subsets(nums))
}

func Subsets(nums []int) [][]int {
	var res [][]int
	var path []int
	backtracking(&res,path,nums,0)
	return res
}

func backtracking(res *[][]int,path []int,nums []int,startindex int){
	// 注意要申请长度
	temp:=make([]int,len(path))
	copy(temp,path)
	*res=append(*res,temp)
	for i:=startindex;i<len(nums);i++{
		path=append(path,nums[i])
		// startindex为当前下标+1
		backtracking(res,path,nums,i+1)
		path=path[:len(path)-1]
	}
	return
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值