全排列全子集

给定一个不含重复数字的数组 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;
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值