Leetcode 排列

1、31. 下一个排列

Leetcode
从右向左遍历,遇到第一个峰值(比前一个大的数),即: x[i + 1] > x[i] ,从 i 后面找刚好比 x[i] 大的数与 x[i] 交换,然后将 i 后面的数升序排列。把 nums[i] 与其后序列中稍大于 nums[i] 的数交换,接着再逆序 nums[i + 1] … nums[n]。

class Solution:
    def nextPermutation(self, nums: List[int]) -> None:
        n = len(nums)
        for i in range(n -2, -1, -1):
            if nums[i + 1] > nums[i]: # 逆序找第一个峰值
                for j in range(n -1, i, -1): 
                    if nums[i] < nums[j]:# 逆序找第一个大于 nums[i] 的数交换
                        nums[i], nums[j] = nums[j], nums[i]
                        # i 后升序排列
                        k = i + 1
                        break
                break
        else:
            k = 0
        
        l = n - 1  
        while k < l:
            nums[k], nums[l] = nums[l], nums[k]
            k += 1
            l -= 1

2、46. 全排列

Leetcode
「回溯」算法采用试错的思想,它尝试分步的去解决一个问题。
「深度优先搜索」(Depth-First-Search,DFS)是一种用于遍历或搜索树或图的算法。
「状态变量」:depth or index used path

方法一:回溯

class Solution:
    def permute(self, nums: List[int]) -> List[List[int]]:
        ## 回溯
        def backtrack(depth=0, path=[]):
            if depth == n: 
                res.append(path[:])
                return
            for i in range(n):
                if not used[i]:
                    path.append(nums[i])
                    used[i] = True
                    backtrack(depth + 1, path)
                    path.pop()
                    used[i] = False

        ## 回溯
        def backtrack1(depth=0):
            if depth == n:
                res.append(nums[:])
              
            for i in range(depth, n):
                nums[depth], nums[i] = nums[i], nums[depth]
                backtrack1(depth + 1)
                nums[depth], nums[i] = nums[i], nums[depth]

        ## 深度优先搜索
        def dfs(depth=0, path=[], tmp=nums):
            if depth == n:
                res.append(path)
                return
            for i in range(len(tmp)):
                dfs(depth + 1, path + [tmp[i]], tmp[:i] + tmp[i + 1:])
        
        n, res = len(nums), []        
        
        used = [False] * n
        backtrack()

        # backtrack1()

        # dfs()

        return res

itertools

class Solution:
    def permute(self, nums: List[int]) -> List[List[int]]:
        return list(permutations(nums))

3、47. 全排列 II

Leetcode

class Solution:
    def permuteUnique(self, nums: List[int]) -> List[List[int]]:
        def backtrack(depth=0, path=[]):
            if depth == n: 
                res.append(path[:])
                return
            for i in range(n):               
                if used[i] or (i > 0 and nums[i] == nums[i - 1] and used[i - 1]): continue
                
                path.append(nums[i])
                used[i] = True
                backtrack(depth + 1, path)
                path.pop()
                used[i] = False
        
        n, res = len(nums), []  
        nums.sort()      
        
        used = [False] * n
        backtrack()

        return res
        # return list(set(permutations(nums)))

526. 优美的排列

Leetcode

方法一:回溯

定义函数 backtrack(index),向位置 index 放入数。在当前函数中,首先找到一个符合条件的未被使用过的数,然后递归地执行 backtrack(index - 1),当该函数执行完毕,回溯到当前层,再尝试下一个符合条件的未被使用过的数。注意:这里是从 n 回溯到 1,把 1 放在最后比较好,因为 1 的选项最多。

用列表 seen 标记被用过的数,选中 x,将 seen[x] 标记为 true,回溯完成后,再将其置为 false。

用字典 match 保存每个位置的符合条件的数。当尝试向位置 index 放入数时,只需要遍历 match[index]。

class Solution:
    def countArrangement(self, n: int) -> int:
		# from collections import defaultdict
		# match = defaultdict(list)
		# for i in range(1,n+1):
		#     for j in range(1, n+1):
		#         if i % j ==0 or j % i == 0:
		#             match[i].append(j)
        match = {i:[j for j in range(1, n+1) if not i%j or not j%i] for i in range(1, n+1)}         
        res, seen = 0, [False]*(n+1)

        def backtrack(index: int) -> None:
            if index == 1:
                nonlocal res
                res += 1
                return            
            
            # for x in range(1,n+1):
            #     if not seen[x] and (not x%index or not index%x):
            for x in match[index]:
                if not seen[x]:
                    seen[x] = True
                    backtrack(index - 1)
                    seen[x] = False
                   
        backtrack(n)
        return res

方法二:回溯

class Solution:
    def countArrangement(self, n: int) -> int:
        d = {i:[j for j in range(1, n+1) if not i%j or not j%i] for i in range(1, n+1)} 
       
        def backtrack(idx, visited):
            if not idx:  return 1 

            ans = 0
            for x in d[idx]:
                if not visited[x]:
                    visited[x] = True
                    ans += backtrack(idx - 1, visited)
                    visited[x] = False

            return ans

        return backtrack(n, [False] * (n + 1))

方法三:回溯 + 状态压缩

class Solution:
    def countArrangement(self, n: int) -> int:
        d = {i:[j for j in range(1, n+1) if not i%j or not j%i] for i in range(1, n+1)}        

        def backtrack(idx, v):
            if idx == 0: return 1
            res = 0
            for x in d[idx]:
                if 1 << x & v == 0:                   
                    res += backtrack(idx - 1, 1 << x | v)
                   
            return res       

        return backtrack(n, 0)

方法四:回溯 + 状态压缩 + 记忆

class Solution:
    def countArrangement(self, n: int) -> int:
        d = {i:[j for j in range(1, n + 1) if not i % j or not j % i] for i in range(1, n + 1)} 
        memo = [0] * (1 << n + 1)
       
        def backtrack(idx, visited):
            if not idx:  return 1                 
            if memo[visited]:
                return memo[visited]                
            ans = 0
            for x in d[idx]:
                if ((1 << x) & visited) == 0:
                    ans += backtrack(idx - 1,  (1 << x) | visited)
                    memo[visited] = ans 
                          
            return ans

        return backtrack(n, 0)

方法五:动态规划 + 状态压缩

由于排列的长度 n <= 15,因此用一个位数为 n 的二进制数 mask 表示排列中的数被选取的情况。若 mask 中的第 i 位为 1(从 0 开始编号),则数 i+1 已经被选取,否则就还未被选取。利用 mask 表示选取数的过程的状态,以 n = 4, mask = 0110 为例,这代表数 2, 3 都已经被选取,并以任意顺序放置在排列中前两个位置。

「状态压缩」的基本操作:
当需要检查 k 是否被使用时,使用位运算 a = (mask >> k) & 1,来获取 mask 中第 k 位的二进制表示,如果 a 为 1 代表值为 k 的数字已被使用,如果为 0 则未被访问。

定义 f[mask] 表示当前状态为 state 的所有方案数
f[0] = 1 没有数被选择的方案只有一个
状态转移方程 f[mask] = ∑ i = 0 n \sum_{i=0}^n i=0n f[mask&(¬(1<<i))]
最终答案为 f[(1 << n) - 1]

class Solution:
    def countArrangement(self, n: int) -> int:
        f = [0] * (1 << n)
        f[0] = 1
        for mask in range(1, 1 << n):
            num = bin(mask).count("1")
            for i in range(n):
                if mask & (1 << i) and (num % (i+1) == 0 or (i+1) % num == 0):
                    f[mask] += f[mask ^ (1 << i)]
        
        return f[(1 << n) - 1]

4、 60. 排列序列

5、 266. 回文排列

6、 267. 回文排列 II

7、 432. 全 O(1) 的数据结构

8、 441. 排列硬币

9、 484. 寻找排列

10、 526. 优美的排列

11、 567. 字符串的排列

12、 634. 寻找数组的错位排列

13、 642. 设计搜索自动补全系统

14、 667. 优美的排列 II

15、 748. 最短补全词

16、 765. 情侣牵手

17、 784. 字母大小写全排列

18、 903. DI 序列的有效排列

19、 937. 重新排列日志文件

20、 1030. 距离顺序排列矩阵单元格

21、 1053. 交换一次的先前排列

22、 1061. 按字典序排列最小的等效字符串

23、 1175. 质数排列

24、 1238. 循环码排列

25、 1277. 统计全为 1 的正方形子矩阵

26、 1284. 转化为全零矩阵的最少反转次数

27、 1409. 查询带键的排列

28、 1451. 重新排列句子中的单词

29、 1470. 重新排列数组

30、 1493. 删掉一个元素以后全为 1 的最长子数组

31、 1504. 统计全 1 子矩形

32、 1528. 重新排列字符串

33、 1589. 所有排列中的最大和

34、 1592. 重新排列单词间的空格

35、 1727. 重新排列后的最大子矩阵

36、 1734. 解码异或后的排列

37、 1772. 按受欢迎程度排列功能

38、 1806. 还原排列的最少操作步数

39、 1832. 判断句子是否为全字母句

40、 1846. 减小和重新排列数组后的最大元素

41、 1866. 恰有 K 根木棍可以看到的排列数目

42、 1920. 基于排列构建数组

43、 1982. 从子集的和还原数组

44、 LCP 11. 期望个数统计

45、 LCP 23. 魔术排列

46、 剑指 Offer 09. 用两个栈实现队列

47、 剑指 Offer 10- I. 斐波那契数列

48、 剑指 Offer 25. 合并两个排序的链表

49、 剑指 Offer 31. 栈的压入、弹出序列

50、 剑指 Offer 33. 二叉搜索树的后序遍历序列

51、 剑指 Offer 37. 序列化二叉树

52、 剑指 Offer 38. 字符串的排列

53、 剑指 Offer 44. 数字序列中某一位的数字

54、 剑指 Offer 45. 把数组排成最小的数

55、 剑指 Offer 53 - I. 在排序数组中查找数字 I

56、 剑指 Offer 57 - II. 和为s的连续正数序列

57、 剑指 Offer 59 - II. 队列的最大值

58、 剑指 Offer II 006. 排序数组中两个数字之和

59、 剑指 Offer II 026. 重排链表

60、 剑指 Offer II 029. 排序的循环链表

61、 剑指 Offer II 034. 外星语言是否排序

62、 剑指 Offer II 043. 往完全二叉树添加节点

63、 剑指 Offer II 048. 序列化与反序列化二叉树

64、 剑指 Offer II 070. 排序数组中只出现一次的数字

65、 剑指 Offer II 075. 数组相对排序

66、 剑指 Offer II 077. 链表排序

67、 剑指 Offer II 078. 合并排序链表

68、 剑指 Offer II 083. 没有重复元素集合的全排列

69、 剑指 Offer II 084. 含有重复元素集合的全排列

70、 剑指 Offer II 093. 最长斐波那契数列

71、 剑指 Offer II 095. 最长公共子序列

72、 剑指 Offer II 097. 子序列的数目

73、 剑指 Offer II 104. 排列的数目

74、 剑指 Offer II 115. 重建序列

75、 剑指 Offer II 119. 最长连续序列

76、 面试题 01.04. 回文排列

77、 面试题 08.07. 无重复字符串的排列组合

78、 面试题 08.08. 有重复字符串的排列组合

6:字母大小写的全排列
给定一个字符串S,通过将字符串S中的每个字母转变大小写,我们可以获得一个新的字符串。返回所有可能得到的字符串集合。

输入: S = “a1b2”
输出: [“a1b2”, “a1B2”, “A1b2”, “A1B2”]

class Solution:
    def letterpermute(self,S):
        
        res=[]
        size=len(S)
        
        def backtrack(combination,S):
            
            if len(combination)==size:
                res.append(''.join(combination))
                return 
            
            for i in range(len(S)):
                if "a"<=S[i]<= "z" or "A"<=S[i]<= "Z":
                    for j in range(2):
                        if j==0:
                            backtrack(combination+[S[i].lower()],S[i+1:])
                        if j==1:
                            backtrack(combination+[S[i].upper()],S[i+1:])
                        
                else:
                    backtrack(combination+[S[i]],S[i+1:])
                    
                    
        backtrack([],S)
        return res         
 
if __name__=='__main__':
    S=[i for i in "1B2"]
    solution=Solution()
    print(solution.letterpermute(S))  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值