Leetcode-回溯典型题

本文探讨了数组的下一个排列算法,通过寻找升序对并反转子数组来找到字典序上的下一个排列。同时,介绍了组合总和问题的解决方案,使用回溯法在给定数组中找出所有可能的组合,满足数字之和为目标值。此外,还涉及子集生成,通过回溯法获取给定数组的所有子集。这些问题展示了在信息技术中常见的数组操作和搜索策略。
摘要由CSDN通过智能技术生成

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
    评论
LeetCode-Editor是一种在线编码工具,它提供了一个用户友好的界面编写和运行代码。在使用LeetCode-Editor时,有时候会出现乱码的问。 乱码的原因可能是由于编码格式不兼容或者编码错误导致的。在这种情况下,我们可以尝试以下几种解决方法: 1. 检查文件编码格式:首先,我们可以检查所编辑的文件的编码格式。通常来说,常用的编码格式有UTF-8和ASCII等。我们可以将编码格式更改为正确的格式。在LeetCode-Editor中,可以通过界面设置或编辑器设置来更改编码格式。 2. 使用正确的字符集:如果乱码是由于使用了不同的字符集导致的,我们可以尝试更改使用正确的字符集。常见的字符集如Unicode或者UTF-8等。在LeetCode-Editor中,可以在编辑器中选择正确的字符集。 3. 使用合适的编辑器:有时候,乱码问可能与LeetCode-Editor自身相关。我们可以尝试使用其他编码工具,如Text Editor、Sublime Text或者IDE,看是否能够解决乱码问。 4. 查找特殊字符:如果乱码问只出现在某些特殊字符上,我们可以尝试找到并替换这些字符。通过仔细检查代码,我们可以找到导致乱码的特定字符,并进行修正或替换。 总之,解决LeetCode-Editor乱码问的方法有很多。根据具体情况,我们可以尝试更改文件编码格式、使用正确的字符集、更换编辑器或者查找并替换特殊字符等方法来解决这个问
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值