Leetcode 77 Combinations的几种解法 Python3

原题:题目链接
给定两个整数 n 和 k,返回 1 … n 中所有可能的 k 个数的组合。在这里插入图片描述
二话不说,直接开始怼代码 (^ ▽ ^)

解法一

利用回溯的思想,如示例中的情况。我们先选出1,然后从[2,3,4]中分别挑出一个数与之结合,直到temp数组长度满足要求,则退出递归。之后再考虑选出2,从剩下的[3,4]中分别挑出一个数与之结合…一直进行,当函数进行到选出4之后,我们显然无法从一个空数组[]中选出任何一个数,因此退出。代码如下:

def combine(n, k):
	if n < k:
		# 如果被选集合小于要选出的个数 直接返回
		return
	# 结果集
	res = []
	# 待扩充的集合
	temp = []
	# 回溯函数模板
	def backtrace(start, n, k, temp, res):
		if len(temp) == k:
			# 加入结果集 注意深拷贝
			res.append(temp[:])
			return
		for i in range(start, n+1):
			temp.append(i)
			backtrace(i+1, n, k, temp, res)
			# 回溯弹出
			temp.pop()
	backtrace(1, n, k, temp, res)
	return res 

上述代码能通,只是比较低效,似乎有可以优化的空间。为此需要仔细分析该回溯方法的一种可行的剪枝方法,先看看这行代码:

for i in range(start, n+1)

我们其实是没必要每次都遍历到n的。举个栗子,当n=5,k=3的时候,很明显,如果确定了temp的第一个数为1的话,当temp=[1,4,5]并加入结果集的时候就应该直接退出,考虑第一个数为2的情形了。但上面的代码却做了以下操作:

  1. pop5,此时temp=[1,4]
  2. pop4,此时temp=[1]
  3. push5,此时temp=[1,5]
  4. pop5,此时temp=[1]

很容易看出这几个操作是多余的。因此我们需要找一个i的上界,让循环提前退出即可。我们需要保证,在剩余可加入数集中的数的个数需>=temp数组中还需要添加的数的个数。设该当前考虑的i值为limit,则有:

n+1 - limit >= (k - temp.size())

因此,limit的上界为n+1-(k-len(temp)),因此就有如下的优化代码:

def combine(n, k):
	if n < k:
		# 如果被选集合小于要选出的个数 直接返回
		return
	# 结果集
	res = []
	# 待扩充的集合
	temp = []
	# 回溯函数模板
	def backtrace(start, n, k, temp, res):
		if len(temp) == k:
			# 加入结果集 注意深拷贝
			res.append(temp[:])
			return
		for i in range(start, n+2-(k-len(temp))):
			temp.append(i)
			backtrace(i+1, n, k, temp, res)
			# 回溯弹出
			temp.pop()
	backtrace(1, n, k, temp, res)
	return res 

从提交结果可以看出,算法效率的确提高不少。

解法二

参考的代码

链接

整体上就是上面那位大神写的代码,只是我在刚开始看的时候花了一些些时间(毕竟菜鸟┭┮﹏┭┮)。在此仅将其做一些注释,来日方长,还需学习。

def combine(self, n: int, k: int) -> List[List[int]]:
        if n < k:
            return
        res = list()
        nums = [x for x in range(1, n+1)]
        ind = [i for i in range(k)]
        res.append([nums[i] for i in ind])
        while True:
            for i in reversed(range(k)):
                if ind[i] < n + i - k:
                    # n + i - k为当前ind[i]的上界
                    # 如果彼此相等 则考虑更新ind的下一个位置
                    break
            else:
                # ind里面的值都达到了对应的上界 则直接返回结果
                return res
            # 考虑加入下一个值
            ind[i] += 1
            # 更新ind中i后面的所有值 
            # 类似于开启回溯程序中的新的for循环 可以达到去重的效果
            for j in range(i+1, k):
                ind[j] = ind[j-1] + 1
            #加入解集
            res.append([nums[i] for i in ind])

本质上的思想同回溯算法一样。只是该迭代版本通过不断地改变加入解集的索引值来达到扩充temp数组的目的。具体的注释加入到代码中,如果有不对的或者不清楚,欢迎提出来。这样彼此都能学习提高。
第一篇博客任务达成!

扫描二维码关注公众号,回复: 8567300 查看本文章

请原谅以往无知的自己吧,你毕竟拥有了现在,干就完事了!!!

发布了3 篇原创文章 · 获赞 4 · 访问量 232

猜你喜欢

转载自blog.csdn.net/weixin_37538742/article/details/103937147
今日推荐