给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。
示例 1:
输入:nums = [1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
示例 2:
输入:nums = [0,1]
输出:[[0,1],[1,0]]
示例 3:
输入:nums = [1]
输出:[[1]]
提示:
1 <= nums.length <= 6
-10 <= nums[i] <= 10
nums 中的所有整数 互不相同
leetcode 46 全排列
回溯核心
for xx in xx:
选择-->递归-->撤销选择
python 解法:
class Solution:
used = []
def permute(self, nums: List[int]) -> List[List[int]]:
if len(nums)<=1:
return [nums]
self.res = []
self.backtrace(nums, [])
return self.res
def backtrace(self, nums , path=[]):
if len(nums) == len(path):
self.res.append(path.copy()) # path.copy
return
for i in range(len(nums)):
if nums[i] in self.used:
continue
path.append(nums[i])
self.used.append(nums[i])
self.backtrace(nums, path)
self.used.pop()
path.pop()
PHP解法:
class Solution {
public $res = [];
public $used = [];
public function permute($nums) {
// if (count($nums) == 0) {
// return [];
// }
if (count($nums) <= 1) {
return [$nums];
}
$this->backtrack($nums);
return $this->res;
}
public function backtrack($nums){
// 递归终点
if(count($this->used) == count($nums)) {
$this->res[] = $this->used;
return;
}
// 所有可能进行递归
for ($i=0;$i<count($nums);$i++){
if(in_array($nums[$i], $this->used)) {
continue;
}
array_push($this->used, $nums[$i]);
$this->backtrack($nums);
array_pop($this->used);
}
}
}
全排列2
给定一个可包含重复数字的序列 nums ,按任意顺序 返回所有不重复的全排列。
示例 1:
输入:nums = [1,1,2]
输出:
[[1,1,2],
[1,2,1],
[2,1,1]]
示例 2:
输入:nums = [1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
提示:
1 <= nums.length <= 8
-10 <= nums[i] <= 10
leetcode 47
排序 方便去重 所有可能 { 剪枝 选择--递归--撤销选择}
class Solution {
public $ret = [];
public $used = [];
/**
* @param Integer[] $nums
* @return Integer[][]
*/
function permuteUnique($nums) {
// 回溯的核心 对所有可能 选择--> 递归--> 撤销选择
// corner case
if (count($nums)<= 1) {
return [$nums];
}
// 递归之前对nums 原地排序
sort($nums);
$this->backtrack($nums,[],0);
return $this->ret;
}
public function backtrack($nums, $path, $start){
if (count($nums) == count($path)) {
$this->ret[] = $path;
return ;
}
for ($i=0; $i<count($nums);$i++) {
if ($this->used[$i]) {
continue;
}
// 重复值,从左到右的第一个未被使用的
if ($i>0 && $nums[$i]==$nums[$i-1] && !$this->used[$i-1]) continue;
$path[] = $nums[$i];
$this->used[$i] = true;
$this->backtrack($nums,$path,$i+1);
$this->used[$i] = false;
array_pop($path);
}
}
}
class Solution:
def permuteUnique(self, nums: List[int]) -> List[List[int]]:
if len(nums) <=1:
return [nums]
nums.sort()
ret = []
path = []
used = [False for i in range(len(nums))]
self.backtrack(nums, ret, path, used,0)
return ret
def backtrack(self, nums, ret ,path , used , start):
if len(path) == len(nums):
ret.append(path.copy())
return
# for i=0;i<len(nums);i++:
for i in range(len(nums)):
if used[i]:
continue
if i>0 and nums[i] == nums[i-1] and used[i-1]==False:
continue
used[i] = True
path.append(nums[i])
self.backtrack(nums,ret,path,used, i+1)
path.pop()
used[i] = False
下一个排列:
整数数组的一个 排列 就是将其所有成员以序列或线性顺序排列。
例如,arr = [1,2,3] ,以下这些都可以视作 arr 的排列:[1,2,3]、[1,3,2]、[3,1,2]、[2,3,1] 。
整数数组的 下一个排列 是指其整数的下一个字典序更大的排列。更正式地,如果数组的所有排列根据其字典顺序从小到大排列在一个容器中,那么数组的 下一个排列 就是在这个有序容器中排在它后面的那个排列。如果不存在下一个更大的排列,那么这个数组必须重排为字典序最小的排列(即,其元素按升序排列)。
例如,arr = [1,2,3] 的下一个排列是 [1,3,2] 。
类似地,arr = [2,3,1] 的下一个排列是 [3,1,2] 。
而 arr = [3,2,1] 的下一个排列是 [1,2,3] ,因为 [3,2,1] 不存在一个字典序更大的排列。
给你一个整数数组 nums ,找出 nums 的下一个排列。
必须 原地 修改,只允许使用额外常数空间。
示例 1:
输入:nums = [1,2,3]
输出:[1,3,2]
示例 2:
输入:nums = [3,2,1]
输出:[1,2,3]
示例 3:
输入:nums = [1,1,5]
输出:[1,5,1]
提示:
1 <= nums.length <= 100
0 <= nums[i] <= 100
leetcode 31
两遍扫描
第一遍 倒序扫到第一个非倒序的位置 i
第二遍 倒序扫到第一个大于 nums[i] 的 j
交换 nums[i],nums[j]
逆转 i 之后的所有数据。
import sys
class Solution:
def nextPermutation(self, nums: List[int]) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
# corner case
if len(nums) <= 1:
return
# 从右向左查找第一个非正序的位置
i = len(nums) - 1
while nums[i] <= nums[i-1] and i-1>=0:
i -= 1
left = i-1
if i == 0:
# i == 0 降序数列
self.reverse(left, nums)
return
# 从右向左找到第一个大于 num[i]的值, 做交换
j = len(nums) -1
while j>=0 and nums[j] <= nums[left] :
j-=1
nums[left],nums[j] = nums[j],nums[left]
self.reverse(left,nums)
return
def reverse(self, left, nums: List[int])->List[int]:
# left 之后的数组倒序
start = left + 1
end = len(nums) -1
while start < end:
nums[start],nums[end] = nums[end],nums[start]
start += 1
end -= 1
class Solution {
/**
* @param Integer[] $nums
* @return NULL
*/
function nextPermutation(&$nums) {
if (count($nums) <=1){
return ;
}
// 从右向左遍历查找第一个非降序的值 确定为 i 值
$left = -1; $right = count($nums)-1;
for ($i = $right; $i>=1; $i--){
if($nums[$i]>$nums[$i-1]){
$left = $i-1;
break;
}
}
if ($left == -1) { // 此时left == 0 与 天生的全降序冲突了,需要区分
$this->reverse($nums,$left+1,$right);
return;
}
// 从右向左遍历查找第一个大于 a[i] 值a[j]
$tmpIdx = 0;
for ($i = $right; $i>=0; $i--){
if($nums[$i] > $nums[$left]) {
$tmpIdx = $i;
// 交换
$tmp = $nums[$i];
$nums[$i] = $nums[$left];
$nums[$left] = $tmp;
break;
}
}
// var_dump($nums);exit;
// 交换a[i] a[j]
// 将 i 以后的值做变为升序 , 不含i
$this->reverse($nums, $left+1, $right);
}
function reverse(&$nums, $i, $j){
while($i<$j){
$tmp = $nums[$i];
$nums[$i] = $nums[$j];
$nums[$j] = $tmp;
$i++;
$j--;
}
}
}
==============
全子集也用这个思想, 区别全排列结果剪枝, 全子集不用剪枝
class Solution {
/**
* @param Integer[] $nums
* @return Integer[][]
*/
function subsets($nums) {
# corner case
if (empty($nums)) return $nums;
$this->ret = [];
# 回溯
$path = [];
$this->backtrack($nums, $path, 0);
return $this->ret;
}
function backtrack($nums, &$path, $start){
# 退出条件
// if ($start == count($nums)){
$this->ret[] = $path; # 每次都是遍历都是一个结果
// return ;
// }
# 回溯核心 选择->递归->撤销选择
for ($i=$start;$i<count($nums);$i++){
$path[] = $nums[$i];
$this->backtrack($nums,$path,$i+1); // start + 1 有重复的
array_pop($path);
}
}
}
全子集2 , 有重复元素,需要排序, 不能有重复子集.
class Solution {
/**
* @param Integer[] $nums
* @return Integer[][]
*/
function subsetsWithDup($nums) {
$this->ret = [];
if(empty($nums)) return $nums;
sort($nums); # 排序方便去重
$path = [];
$used = array_fill(0,count($nums),false);
$this->backtrack($nums,0,$path,$used);
return $this->ret;
}
function backtrack($nums,$start, &$path, &$used){
# 每次调用有可能是结果
# 如果用过了就不要
$this->ret[] = $path;
# 选 -> 递归 -> 不选
for($i=$start; $i<count($nums); $i++){
# 如果现在的元素跟前一个
if($nums[$i] == $nums[$i-1] && !$used[$i-1] && $i>$start) continue; # 这里!$used[$i-1] 才可以去重
$path[] = $nums[$i];
$used[$i] = True;
$this->backtrack($nums,$i+1, $path, $used);
array_pop($path);
$used[$i] = false;
}
}
}