力扣第四十六题——全排列

内容介绍

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

完整代码

 class Solution {
    public List<List<Integer>> permute(int[] nums) {
        List<List<Integer>> res = new ArrayList<List<Integer>>();

        List<Integer> output = new ArrayList<Integer>();
        for (int num : nums) {
            output.add(num);
        }

        int n = nums.length;
        backtrack(n, output, res, 0);
        return res;
    }

    public void backtrack(int n, List<Integer> output, List<List<Integer>> res, int first) {
        // 所有数都填完了
        if (first == n) {
            res.add(new ArrayList<Integer>(output));
        }
        for (int i = first; i < n; i++) {
            // 动态维护数组
            Collections.swap(output, first, i);
            // 继续递归填下一个数
            backtrack(n, output, res, first + 1);
            // 撤销操作
            Collections.swap(output, first, i);
        }
    }
}

思路详解

思路详解:

  1. 算法设计

    • 回溯法是一种递归算法,通过尝试所有可能的解决方案,并在必要时回溯到之前的步骤来找到所有可能的解。
    • 在本例中,回溯法用于生成数组 nums 的所有排列组合。
  2. 函数定义

    • permute 函数是算法的主入口点,它负责创建一个结果列表 res 来存储所有可能的排列组合,并初始化一个输出列表 output 来存储当前的排列。
    • backtrack 函数是递归函数,用于生成排列组合。它接受当前排列的长度 n、当前排列 output、结果列表 res 和当前处理的起始位置 first
  3. 初始化

    • 在 permute 函数中,首先创建一个空的结果列表 res 和一个初始化的输出列表 output
    • 将 nums 数组中的每个元素添加到 output 中。
  4. 回溯算法

    • backtrack 函数的核心是一个 if 语句,它检查是否所有数都已经被填入 output。如果是,说明当前的排列是一个有效的排列,将其添加到结果列表 res 中。
    • 接着,使用一个 for 循环遍历 output 中的每个元素,从当前处理的起始位置 first 开始。
    • 对于每个元素,执行以下操作:
      • 使用 Collections.swap 方法将当前元素与 first 位置的元素交换,以生成一个新的排列。
      • 递归调用 backtrack 函数,处理下一个位置。
      • 递归调用结束后,使用 Collections.swap 方法撤销之前的交换操作,以恢复原始排列。
  5. 撤销操作

    • 在递归调用 backtrack 函数之前,使用 Collections.swap 方法将当前元素与 first 位置的元素交换。
    • 在递归调用 backtrack 函数之后,使用 Collections.swap 方法撤销之前的交换操作,以恢复原始排列。
  6. 返回结果

    • 遍历 output 中的每个元素并执行上述操作后,返回结果列表 res

这个算法的时间复杂度为 O(n!),其中 n 是数组 nums 的长度。这是因为算法需要生成所有 n! 个排列组合,每个排列组合都需要 O(n) 的时间来生成。空间复杂度为 O(n),因为需要一个长度为 n 的输出列表 output 来存储中间结果。

知识点精炼

  1. 回溯法

    • 回溯法是一种递归算法,通过尝试所有可能的解决方案,并在必要时回溯到之前的步骤来找到所有可能的解。
    • 在本例中,回溯法用于生成数组 nums 的所有排列组合。
  2. 递归函数

    • 递归函数是一种函数,它调用自身来解决问题。
    • 在本例中,backtrack 函数是递归函数,用于生成排列组合。
  3. 列表操作

    • 使用 ArrayList 类来存储数组 nums 中的元素。
    • 使用 add 方法将元素添加到列表中。
    • 使用 swap 方法交换列表中的元素。
  4. 集合操作

    • 使用 Collections 类来提供集合操作的静态方法。
    • 在本例中,使用 Collections.swap 方法交换列表中的元素。
  5. 函数定义

    • permute 函数是算法的主入口点,它负责创建一个结果列表 res 来存储所有可能的排列组合,并初始化一个输出列表 output 来存储当前的排列。
    • backtrack 函数是递归函数,用于生成排列组合。它接受当前排列的长度 n、当前排列 output、结果列表 res 和当前处理的起始位置 first
  6. 变量初始化

    • 在 permute 函数中,首先创建一个空的结果列表 res 和一个初始化的输出列表 output
    • 将 nums 数组中的每个元素添加到 output 中。
  7. 循环结构

    • 使用 for 循环遍历 output 中的每个元素,从当前处理的起始位置 first 开始。
  8. 逻辑判断

    • 使用条件语句 if 来判断是否所有数都已经被填入 output
  9. 返回结果

    • 遍历 output 中的每个元素并执行上述操作后,返回结果列表 res

 题型扩展

题目描述

给定一个字符串 s,请实现一个函数 countSubstrings,该函数返回字符串 s 中所有子串的出现次数。

子串是指字符串中任意连续的若干字符组成的序列。例如,对于字符串 "abc",其子串包括 "a""b""c""ab""bc""abc"

输入输出格式

输入:一个字符串 s

输出:一个整数数组,表示 s 中所有子串的出现次数。

示例

输入:"abc"

输出:[1, 1, 1, 2, 2, 3]

解释:子串 "a" 出现 1 次,子串 "b" 出现 1 次,子串 "c" 出现 1 次,子串 "ab" 出现 2 次,子串 "bc" 出现 2 次,子串 "abc" 出现 3 次。

这个题目可以扩展为更复杂的问题,例如:

  1. 给定一个字符串 s 和一个整数 k,请实现一个函数 countSubstringsWithK,该函数返回字符串 s 中所有长度为 k 的子串的出现次数。

  2. 给定一个字符串 s 和一个字符 c,请实现一个函数 countSubstringsWithChar,该函数返回字符串 s 中所有包含字符 c 的子串的出现次数。

  3. 给定一个字符串 s 和两个整数 m 和 n,请实现一个函数 countSubstringsWithRange,该函数返回字符串 s 中所有长度在 m 到 n 之间的子串的出现次数。

这些问题都可以通过扩展原有的 countSubstrings 函数来实现。例如,对于 countSubstringsWithK 问题,可以在 countSubstrings 函数的基础上添加一个条件来判断子串的长度是否等于 k。对于 countSubstringsWithChar 问题,可以在 countSubstrings 函数的基础上添加一个条件来判断子串中是否包含字符 c。对于 countSubstringsWithRange 问题,可以在 countSubstrings 函数的基础上添加两个条件来判断子串的长度是否在 m 到 n 之间。

  • 24
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值