全排列算法.md 4.6 KB
Newer Older
K
KEQI HUANG 已提交
1
### 全排列算法
K
KEQI HUANG 已提交
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88


#### 46. Permutations


Given a collection of distinct numbers, return all possible permutations.

For example,
[1,2,3] have the following permutations:

	[
	  [1,2,3],
	  [1,3,2],
	  [2,1,3],
	  [2,3,1],
	  [3,1,2],
	  [3,2,1]
	]
	
	
#####从空开始加

先跳离开这道题,来看类似的'ABC',我们要求它的全排列


```
def recPermute(sofar, rest):
	if rest == '':
		print sofar
	else:
		for i in range(len(rest)):
			nxt = sofar + rest[i]
			remaining = rest[:i] + rest[i+1:]
			recPermute(nxt, remaining)

// "wrapper" function
def listPermute(s):
	recPermute('',s)
```

会正确输出`ABC ACB BAC BCA CAB CBA`,题目依靠的是每次我们从余下的字母中选一个,如果画图则会是这样:


```
		A				B				C
	B		C		A		C		A		B
	C		B		C		A		B		A
```

时间复杂度应该是O(n!)

- n choose 1
- n-1 choose 1
- ...



#####另一种市面上常见思路是交换:

思路是这样的,同样看上面的图:

- n个元素的全排列 = (n-1)个元素的全排列 + 另一个元素作为前缀
- 如果只有一个元素,那么这个元素本身就是它的全排列
- 不断将每个元素放作第一个元素,然后将这个元素作为前缀,并将其余元素继续全排列,等到出口,出口出去后还需要还原数组


这个用数组来测试更容易写代码和直观:


```
def recPermute(nums,begin):
    n = len(nums)
    if begin == n:
    	print nums,
    
    for i in range(begin,n):
        nums[begin], nums[i] = nums[i],nums[begin]
        recPermute(nums,begin+1)
        nums[begin],nums[i] = nums[i],nums[begin]

recPermute(['A','B','C'],0)

```

这样的写法更容易理解:


K
KEQI HUANG 已提交
89
```python
K
KEQI HUANG 已提交
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193
class Solution:
    # @param num, a list of integer
    # @return a list of lists of integers
    def permute(self, num):
        if len(num) == 0: return []
        if len(num) == 1: return [num]
        res = []
        for i in range(len(num)):
            x  = num[i]
            xs = num[:i] + num[i+1:]             
            for j in self.permute(xs):
                res.append([x] + j)
        return res
```

每次用一个没有用过的头元素,然后加上全排列产生的结果.

如果分析复杂度,应该也是O(n!)


#### 47. Permutations II


最简单的想法:

- 排序
- 如果碰到重复的就继续处理下一个

```
class Solution(object):
    def permuteUnique(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        if len(nums) == 0: return []
        if len(nums) == 1: return [nums]
        res = []
        nums.sort()
        for i in range(len(nums)):
            if i > 0 and nums[i] == nums[i-1]: continue
            for j in self.permuteUnique(nums[:i] + nums[i+1:]):
                res.append([nums[i]] + j)
        return res

```




#### 31. Next Permutation

实际上这个题目也就是Generation in lexicographic order,

wikipedia 和 [这里](https://www.nayuki.io/page/next-lexicographical-permutation-algorithm) 有很好,很精妙的算法,也有点two pointer的意思


```
1. Find the highest index i such that s[i] < s[i+1]. If no such index exists, the permutation is the last permutation.
2. Find the highest index j > i such that s[j] > s[i]. Such a j must exist, since i+1 is such an index.
3. Swap s[i] with s[j].
4. Reverse the order of all of the elements after index i till the last element.
```


看例子:

125430


- 从末尾开始,找到decreasing subsequence,5430,因为来调5330无论怎么调,都不可能有比它更小的,数也被自然的分成两部分(1,2) 和 (5,4,3,0)
- 下一步是找这个sequence里面第一个比前面部分,比2大的,3,也很容易理解,因为下一个必定是(1,3)打头
- 交换 3和2 ,变成 (1,3,5,4,2,0),再把后面的部分reverse,得到后面部分可得到的最小的

这个时候,得到下一个sequence 130245



```
class Solution(object):
    def nextPermutation(self, nums):
        """
        :type nums: List[int]
        :rtype: void Do not return anything, modify nums in-place instead.
        """
        m, n = 0, 0
        for i in range(len(nums) - 2, 0 , -1):
        	if nums[i] < nums[i+1]:
        		m = i 
        		break

        for i in range(len(nums) - 1, 0 , -1):
        	if nums[i] > nums[m]:
        		n = i
        		break

        if m < n :
        	nums[m], nums[n] = nums[n], nums[m]
        	nums[m+1:] = nums[len(nums):m:-1]
        else:
        	nums = nums.reverse()
```


K
KEQI HUANG 已提交
194
所以可以用这个next permutation来解46/47也可以,然后我兴奋了一下,这个算法很快的!然后我又冷静了,因为permutation的个数是O(n!)个啊|||,所以也不可能有啥大的提升吧