算法训练day23 | php | 491.递增子序列, 46.全排列, 47.全排列 II

一、力扣题491. 递增子序列

给你一个整数数组 nums ,找出并返回所有该数组中不同的递增子序列,递增子序列中 至少有两个元素 。你可以按 任意顺序 返回答案。

数组中可能含有重复元素,如出现两个整数相等,也可以视作递增序列的一种特殊情况。

示例 1:

输入:nums = [4,6,7,7]
输出:[[4,6],[4,6,7],[4,6,7,7],[4,7],[4,7,7],[6,7],[6,7,7],[7,7]]

示例 2:

输入:nums = [4,4,3,2,1]
输出:[[4,4]]

提示:

  • 1 <= nums.length <= 15
  • -100 <= nums[i] <= 100

         这道题的主要难点也是去重,同一树层中的相同元素不能重复取用(指的是同一父节点下的同一树层),但和之前去重的题目的不同之处在于,这道题给的数组是不能打乱顺序重新排序的,所以要用别的方法判断重复元素。

        这里用到了uset数组,uset数组在每一个树层开始遍历时都会置空,该数组相当于一个map数组,当遍历到一个元素 i 时,定义 uset[ nums[ i ] ]  为 1,所以遍历到第二个重复元素时,判断以该元素值为键名的值存在,则跳过该元素。

        实际上就是对同一树层非顺序排列的数组的去重。

class Solution {
    private $result = [];
    /**
     * @param Integer[] $nums
     * @return Integer[][]
     */
    function findSubsequences($nums) {
        if(count($nums) < 2) return [];
        $this->backtracking($nums, [], 0, []);
        return $this->result;
    }

    function backtracking($nums, $path, $cur, $uset) {
        for($i = $cur; $i < count($nums); $i++) {
            if(($path != [] && end($path) > $nums[$i]) || $uset[$nums[$i]] == 1) {
                continue;
            }

            $path[] = $nums[$i];
            if(count($path) > 1) {
                $this->result[] = $path;
            }
            $uset[$nums[$i]] = 1;
            $this->backtracking($nums, $path, $i + 1,[]);
            array_pop($path);
        }
    }
}

二、力扣题46. 全排列

给定一个不含重复数字的数组 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 中的所有整数 互不相同

         排列与组合的不同之处在于,组合中两个集合只要含有的元素相同就是重复,但在排列的定义中,即使两个集合含有的元素相同,但只要两个集合中元素排列的顺序不同,都算是不同集合。

        总的来说本题就是求出一个集合中元素的全部排列方式。所以解集中所有集合的元素个数都相等。

        所以和以前不同的是,每次循环遍历都要从0的位置开始,这里就涉及到一个问题,遍历时遇到已经取用过的元素怎么办?

        使用used数组用于标记已取用过的元素。

class Solution {
    private $result = [];
    /**
     * @param Integer[] $nums
     * @return Integer[][]
     */
    function permute($nums) {
        $this->backtracking($nums, [], []);
        return $this->result;
    }

    function backtracking($nums, $path, $used) {
        if(count($path) == count($nums)) {
            $this->result[] = $path;
            return;
        }

        for($i = 0; $i < count($nums); $i++) {
            if($used[$i]) {
                continue;
            }
            $used[$i] = true;
            $path[] = $nums[$i];
            $this->backtracking($nums, $path, $used);
            $used[$i] = false;
            array_pop($path);
        }
    }
}

三、力扣题47. 全排列 II 

给定一个可包含重复数字的序列 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

        本题就是在上一题的基础上加了去重要求,现在不仅要标记已经取用过的元素了,还要同一树层不能取用重复的元素。

        同一树层去重可以直接使用map数组,也可以将数组重新排序后用之前的used数组(这里说的used数组作用不是用于标记用过的元素,而是为了去重,同名不同意思)的方法。

        这里就直接使用map数组的方法了。

class Solution {
    private $result = [];
    /**
     * @param Integer[] $nums
     * @return Integer[][]
     */
    function permuteUnique($nums) {
        $this->backtracking($nums, [], [], []);
        return $this->result;
    }

    function backtracking($nums, $path, $used, $uset) {
        if(count($path) == count($nums)) {
            $this->result[] = $path;
            return;
        }
        for($i = 0; $i < count($nums); $i++) {
            if($used[$i] || $uset[$nums[$i]]) {
                continue;
            }
            $used[$i] = true;
            $uset[$nums[$i]] = true;
            $path[] = $nums[$i];
            $this->backtracking($nums, $path, $used, []);
            $used[$i] = false;
            array_pop($path);
        }
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值