Leetcode算法——48、全排列II

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/HappyRocking/article/details/84758784

给定一系列数字,可能会包含重复数字,返回所有可能的唯一的排列。

示例:

Input: [1,1,2]
Output:
[
  [1,1,2],
  [1,2,1],
  [2,1,1]
]

思路

本题与上一题 Leetcode算法——46、全排列 很相似,不同之处在于,上一题的元素都是无重复的,而本题的元素允许重复,但是要求返回无重复的排列。

上一题我们使用了 4 种方法来解决全排列问题,本题我们也用同样 4 种方法来解决,但是需要加入去重的操作。

1、字典序法

使用字典序法不断寻找当前排序的下一个排序,直至返回到了起始序列。

字典序法与是否有重复元素无关,因为他的结果是按照从小到大的顺序返回的,无论元素是否重复,返回的结果都不会重复。

因此使用字典序法不用做任何修改,同时适用于上一题和本题。

2、深度优先搜索

使用递归法,维护一个结果数组、以及每个元素对应的剩余元素集合。每次依次从剩余的元素集合中取出一个,并且分别追加到每一个已取序列的最后,直至剩余元素集合为空。

注意,如果当前取出的一个,在之前的取法中已经取出过同样的元素值(即深度树已经有了一个同样的分叉),则跳过,防止重复。

为了让相同的元素值只可能是相邻元素,我们需要提前对数组进行排序。

3、广度优先搜索

维护一个状态列表,里面的元素是所有状态词典,词典中有两个key,记录已经搜索到的和尚未搜索到的。

每次循环,对于所有状态词典,对每个尚未搜索到的数字,都拼接到已经搜索到的数组的末尾,然后形成一个新的状态列表。

这样,每次循环,所有状态词典中的已搜索到的序列长度+1,尚未搜索到的集合长度-1。

因此,只需要循环 len(nums) 次,所有状态都表示搜索完毕了。

同样地,如果当前取出的一个,在之前的取法中已经取出过同样的元素值(即广度树已经有了一个同样的分叉),则跳过,防止重复。

4、分治法

设nums的长度为n,则前n项的全排列 = 第n个数字依次插入到前n-1项的全排列的两两之间的缝隙中(包括首尾两端)。

但是注意,如果有重复元素,则可能有些缝隙的插入是重复的。比如数组为 [1,2,2],前 n-1 项的全排列为 [1,2] 和 [2,1]。以 [1,2] 为例,将第 n 项插入到缝隙中,那么插入到 1 和 2 之间,和插入到 2 的后面,结果都是 [1,2,2],是重复的,只能保留一个。

如果要避免重复,只需要判断插入到缝隙之后,新元素的左边是否和新元素相等。如果相等,则不再继续这个缝隙的插入以及之后所有缝隙的插入。注意是结束之后所有缝隙的插入,而不仅仅是跳过继续下一个缝隙。

python实现

def permuteUnique(nums):
    """
    :type nums: List[int]
    :rtype: List[List[int]]
    深度优先搜索。
    """
    def get_rest(result_list, pre_list, rest_list):
        '''
        递归,对rest_list进行全排列,并分别与pre_list拼接,最终加入到result_list中
        '''
        if not rest_list:
            result_list.extend(pre_list)
        for i in range(len(rest_list)):
            if i > 0 and rest_list[i] == rest_list[i-1]: # 防止重复
                continue
            new_list = rest_list[:i] + rest_list[i+1:] # 去掉第i个元素
            get_rest(result_list, [x+[rest_list[i]] for x in pre_list], new_list)
         
    nums_sort = sorted(nums) # 排序,便于判断是否重复
    result_list = []
    get_rest(result_list, [[]], nums_sort)
    return result_list

def permuteUnique2(nums):
    """
    :type nums: List[int]
    :rtype: List[List[int]]
    广度优先搜索。
    """
    nums_sort = sorted(nums)
    state_list = [{'pre':[],'rest':nums_sort}]
    for _ in range(len(nums)): # 循环len(nums)次
        new_state_list = []
        for dic in state_list:
            rest_list = dic['rest']
            for i in range(len(rest_list)):
                if i > 0 and rest_list[i] == rest_list[i-1]: # 防止重复
                    continue
                new_list = rest_list[:i] + rest_list[i+1:] # 去掉第i个元素
                new_state_list.append({'pre':dic['pre']+[rest_list[i]],'rest':new_list})
        state_list = new_state_list
    return [x['pre'] for x in state_list]

def permuteUnique3(nums):
    """
    :type nums: List[int]
    :rtype: List[List[int]]
    分治法。
    """
    def get_permutes(n):
        '''
        得到nums前n项的全排列。
        '''
        # 递归结束条件
        if n == 1:
            return [[nums[0]]]
        
        # 得到前n-1项的全排列
        pre_list = get_permutes(n-1)
        # 将第 n 个元素,依次插入到 pre_list 中的元素缝隙中
        result_list = []
        for permute in pre_list:
            for i in range(len(permute)+1):
                if i > 0 and nums[n-1] == permute[i-1]: # 防止重复
                    # 不能是continue。一旦碰到相同数字,则后面所有空隙的插入都略过。
                    break
                result_list.append(permute[:i] + [nums[n-1]] + permute[i:])
        return result_list
    nums.sort()
    return get_permutes(len(nums))

if '__main__' == __name__:
    nums = [1,1,2,2]
    print(permuteUnique3(nums))

猜你喜欢

转载自blog.csdn.net/HappyRocking/article/details/84758784