剑指 Offer(第2版)面试题 38:字符串的排列

剑指 Offer(第2版)面试题 38:字符串的排列

  • 剑指 Offer(第2版)面试题 38:字符串的排列
    • 解法1:回溯
    • 扩展题一:LeetCode 46. 全排列
    • 扩展题二:LeetCode 47. 全排列 II

剑指 Offer(第2版)面试题 38:字符串的排列

题目:输入一个字符串,打印出该字符串中字符的所有排列。

解法1:回溯

全排列问题,且各个字符都不相同。

代码:

void Permutation(char *pStr)
{
	if (pStr == nullptr)
		return;
	Permutation(pStr, pStr);
}

void Permutation(char *pStr, char *pBegin)
{
	if (*pBegin == '\0')
		printf("%s\n", pStr);
	for (char *pCh = pBegin; *pCh != '\0'; pCh++)
	{
		swap(*pCh, *pBegin);
		Permutation(pStr, pBegin + 1);
		swap(*pCh, *pBegin);
	}
}

复杂度分析:

时间复杂度:O(n × n!),其中 n 是序列的长度。回溯的调用次数是 O(n!),对于每个回溯的叶子节点,我们需要将当前答案使用 O(n) 的时间复制到答案数组中,相乘得时间复杂度为 O(n × n!)。

空间复杂度:O(n),其中 n 是序列的长度。除答案数组以外,递归函数在递归过程中需要为每一层递归函数分配栈空间,所以这里需要额外的空间且该空间取决于递归的深度,这里可知递归调用深度为 O(n)。

扩展题一:LeetCode 46. 全排列

题目来源:LeetCode 46. 全排列

给定一个没有重复数字的序列,返回其所有可能的全排列。

代码:

/*
 * @lc app=leetcode.cn id=46 lang=cpp
 *
 * [46] 全排列
 */

// @lc code=start
class Solution
{
public:
    // 主函数
    vector<vector<int>> permute(vector<int> &nums)
    {
        vector<vector<int>> ans;
        backtrack(nums, 0, ans);
        return ans;
    }
    // 辅函数
    void backtrack(vector<int> &nums, int level, vector<vector<int>> &ans)
    {
        if (level == nums.size() - 1)
        {
            ans.push_back(nums);
            return;
        }
        for (int i = level; i < nums.size(); i++)
        {
            swap(nums[i], nums[level]);      // 修改当前节点状态
            backtrack(nums, level + 1, ans); // 递归子节点
            swap(nums[i], nums[level]);      // 回改当前节点状态
        }
    }
};
// @lc code=end

复杂度分析:

时间复杂度:O(n × n!),其中 n 是数组 nums 的长度。回溯的调用次数是 O(n!),对于每个回溯的叶子节点,我们需要将当前答案使用 O(n) 的时间复制到答案数组中,相乘得时间复杂度为 O(n × n!)。

空间复杂度:O(n),其中 n 是数组 nums 的长度。除答案数组以外,递归函数在递归过程中需要为每一层递归函数分配栈空间,所以这里需要额外的空间且该空间取决于递归的深度,这里可知递归调用深度为 O(n)。

扩展题二:LeetCode 47. 全排列 II

题目来源:LeetCode 47. 全排列 II

序列中有重复的数字,要考虑重复,需要剪枝。

代码:

/*
 * @lc app=leetcode.cn id=47 lang=cpp
 *
 * [47] 全排列 II
 */

// @lc code=start
class Solution
{
private:
    vector<bool> visited;

public:
    // 主函数
    vector<vector<int>> permuteUnique(vector<int> &nums)
    {
        vector<vector<int>> ans;
        vector<int> seq;
        visited = vector<bool>(nums.size(), false);
        sort(nums.begin(), nums.end());
        backtrack(nums, 0, ans, seq);
        return ans;
    }
    // 辅函数
    void backtrack(vector<int> &nums, int level, vector<vector<int>> &ans, vector<int> &seq)
    {
        if (level == nums.size())
        {
            ans.emplace_back(seq);
            return;
        }
        for (size_t i = 0; i < nums.size(); i++)
        {
            if (visited[i] || (i > 0 && nums[i] == nums[i - 1] && !visited[i - 1]))
            {
                continue;
            }
            seq.emplace_back(nums[i]);
            visited[i] = true;
            backtrack(nums, level + 1, ans, seq);
            visited[i] = false;
            seq.pop_back();
        }
    }
};
// @lc code=end

复杂度分析:

时间复杂度:O(n × n!),其中 n 是数组 nums 的长度。回溯的调用次数是 O(n!),对于每个回溯的叶子节点,我们需要将当前答案使用 O(n) 的时间复制到答案数组中,相乘得时间复杂度为 O(n × n!)。

空间复杂度:O(n),其中 n 是数组 nums 的长度。我们需要 O(n) 的标记数组,同时在递归的时候栈深度会达到 O(n),因此总空间复杂度为 O(n+n)=O(2n)=O(n)。

你可能感兴趣的:(剑指,Offer,C++,数据结构与算法,leetcode,全排列,回溯)