leetcode回溯算法-排列组合子集专题-刷题记录

组合相关题目:

排列相关题目:

子集相关题目:

1. 回溯枚举的实现(排列与组合)

对于n个元素进行排列或组合,数组长度为k。

组合的代码如下:

    def con(self,arr,cur,n,k):


        #递归停止条件,长度为k或是枚举到第n个元素
        if len(arr)==k:
            self.res.append(arr[:])
            return
        if cur==n+1:
            return

        #枚举主体,枚举元素从当前位置开始
        for i in range(cur,n+1):
            arr.append(i)
            self.con(arr,i,n,k)
            arr.pop()    

排列的代码如下:

    def con(self,arr,cur,n,k):


        #递归停止条件,长度为k或是枚举到第n个元素
        if len(arr)==k:
            self.res.append(arr[:])
            return
        if cur==n+1:
            return

        #枚举主体,枚举元素从第一位开始
        for i in range(cur,n+1):
            arr.append(i)
            self.con(arr,i,n,k)
            arr.pop() 

对比可发现,排列组合的不同之处主要在于枚举主题代码的for循环范围。

由于组合是有序的,(1,2,3)与(3,2,1)是一个结果,那我们就需要枚举对象有序出场,譬如对于本题中n个数需要从小到大出场。则对于当前位置的数字,下一位置的数一定大于当前位的数,那么枚举就要从当前位开始。

组合是无序的,下一位的数与当前位的数没有联系,则下一位的数可以从第0个元素枚举。

 

 

 

2. 元素不可重复与结果不可重复

元素不可重复:假设给定元素[1,7,5,3,1,5]这6个数,给出长度为3的排列与组合,并规定所有元素只能用一次。

结果不可重复:假设给定元素[1,7,5,3,1,5]这6个数,给出长度为3的排列与组合,并规定输出结果不可重复。

元素不可重复的组合:

    def con(self,arr,cur,nums,k):


        #递归停止条件,长度为k或是枚举到第n个元素
        if len(arr)==k:
            self.res.append(arr[:])
            return
        if cur==len(nums):
            return

        #枚举主体,枚举元素从当前位置开始
        for i in range(cur,n+1):
            arr.append(i)
            self.con(arr,i+1,n,k)
            arr.pop() 

与之前不同的是,下一位的元素从cur+1开始。

元素不可重复的排列:

        def con(self, arr, cur, nums, k, vis):

            # 递归停止条件,长度为k或是枚举到第n个元素
            if len(arr) == k:
                self.res.append(arr[:])
                return
            if cur == len(nums):
                return

            # 枚举主体,枚举元素从当前位置开始
            for i in range(cur, n + 1):
                if vis[i] == 1: continue
                arr.append(i)
                vis[i]=1
                self.con(arr, i + 1, n, k)
                arr.pop()
                vis[i]=0

在排列中,需要标志每一个元素是否出现过,以vis数组为标志,如果vis[i]==1则代表第i个元素已经使用。(如果给定元素没有重复的,直接判断就行,不需要vis数组做标记)

 

在结果不可重复这一命题下,我们首先讨论结果不可重复的排列:

结果不可重复比元素不可重复的要求要高,在上述题目中,元素不可重复可以有答案[1,1,3],[1,1,3](第一个元素“1”与第二个元素“1”位置打颠倒)。而在结果不可重复下,则认为两者是一种情况。首先想到的简单解法是每次将结果加入self.res变量时判断一下即可。然后超时了[doge]

自然,判断是偷懒的。一般这种情况需要我们对所有元素进行排序。然后我们规定对于相同的元素“1”,排在前面的必须在前面出场,排在后面的必须在后面出场。这样就排除了相同的元素,互换了位置,导致结果有重复的问题了。(元素不可重复与结果不可重复的差异也就仅在于此)

对此的实现方法是:

if i>0 and nums[i]==nums[i-1] and vis[i-1]==0:
   return

这句话的翻译是:如果第i个元素与第i-1个元素相同,并且第i-1个元素未被访问使用,则第i个元素不能使用。这句话,拒绝了相同元素排在后面的先被使用,从而只能是排在前面的先被使用。(天才设计这是,正话反说也不太好想得到)

结果不可重复的排列如下:

    def con(self,arr,nums,vis):
        #递归停止条件
        if len(arr)==len(nums):
            self.res.append(arr[:])
            return

        for i in range(len(nums)):
            #如果当前点已被访问,则跳过
            if vis[i]==1:
                continue
            
            if i>0 and nums[i]==nums[i-1] and vis[i-1]==0:
                continue

            vis[i]=1
            arr.append(nums[i])
            self.con(arr,nums,vis)
            arr.pop()
            vis[i] = 0

那么,对于结果不可重复的组合实现方式也类似:

    def con(self,arr,nums,cur,target):
       #递归停止条件
        if sum(arr) == target:
            self.res.append(arr[:])
            return
        if sum(arr) > target:
            return
        if cur >=len(nums):
            return

        #递归主体
        for i in range(cur,len(nums)):

            if i>cur and nums[i]==nums[i-1]:
                continue
            arr.append(nums[i])
            self.con(arr,nums,i+1,target)
            arr.pop()

需要注意的是,其判断语句有所不同。

if i>cur and nums[i]==nums[i-1]:
                continue

 

该句的翻译是,对于当前位,如果枚举的元素i与i-1相同,且 i != cur,则代表相同的元素 i-1已经在该位枚举过了。因此跳过。

 

posted @ 2022-07-04 19:12  范德麦韦  阅读(26)  评论(0)    收藏  举报