[230516 剑指38] 字符串的排列

[230516 剑指38] 字符串的排列

一 题目

剑指 Offer 38. 字符串的排列

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

你可以以任意顺序返回这个字符串数组,但里面不能有重复元素。

示例:

输入:s = "abc"
输出:["abc","acb","bac","bca","cab","cba"]

限制:

1 <= s 的长度 <= 8

二 整体思路

求一个字符串的全排列,本质上就是穷举。我们可以把这个问题看作“有 n 个空位,我们使用给定的 n 个字符从左往右填满这 n 个空位,并且每个字符不能重复使用”。我们可以使用回溯来模拟这个过程。

递归函数的每一次调用,代表着选定了空位;在回溯函数内部,遍历 n 个字符,尝试将某一字符放入该空位,然后进行下一次递归调用,对下一个空位进行相同的操作。

在写代码的过程中描述每一个变量、每一个参数的意思。

三 关键点/重点/难点

关键点:

  • 因为是求全排列,所以递归函数内部中的遍历,每次都要从下标为 0 的字符开始

难点:

  • 如果原字符串中有重复字符,要对结果集中重复的排列进行去重
  • 如果求出所有排列以后再进行去重,时间复杂度会很高,应该会超时
  • 所以我们借助一个元素类型为 bool 的 vector,它标记的是有哪些元素已经被放入了空位。同时,在函数内部的遍历中,我们保证:相同的字符不能第二次填入同一个空位。
if(isUsed[i] || (i > 0 && s[i] == s[i - 1] && !isUsed[i - 1])) {
    continue;
}
  • 举个例子:给定字符串 “aab”,开始时选定第一个空位,第一个 a 可以填入第一个空位,但进行回溯后,第二个 a 不允许再填入第一个空位。

四 代码分析

class Solution {
private:
    vector<string> result;
    string path;
    void traceback(const string& s, vector<bool>& isUsed) {
        if(path.size() == s.size()) {
            result.push_back(path);
            return;
        }
        for(int i = 0; i < s.size(); ++i) {
            if(isUsed[i] || (i > 0 && s[i] == s[i - 1] && !isUsed[i - 1])) {
                continue;
            }
            path.push_back(s[i]);
            isUsed[i] = true;
            traceback(s, isUsed);
            path.pop_back();
            isUsed[i] = false;
        }
    }
public:
    vector<string> permutation(string s) {
        result.clear();
        path.clear();
        sort(s.begin(), s.end());
        vector<bool> isUsed(s.size(), false);
        traceback(s, isUsed);
        return result;
    }
};

(五)一题多解

下一个排列:已知当前的一个排列,快速得到字典序中下一个更大的排列。

31. 下一个排列的官方题解

(六) 知识扩展

C++ 中的 sort 函数

函数实现:

  • C++ 排序函数使用 introsort,这是一种混合算法。不同的实现使用不同的算法。例如,GNU 标准 C++ 库使用三部分混合排序算法:首先执行 introsort,然后对结果进行插入排序。
  • Introsort 是一种混合排序算法,使用三种排序算法来最大限度地减少运行时间,三种排序算法 分别是 Quicksort、Heapsort 和 Insertion Sort。

Introsort 排序算法:

  • Introsort 从快速排序开始,如果递归深度超过特定限制,它会切换到堆排序,以避免快速排序的更坏情况O(N2)时间复杂性。当要排序的元素数量非常少时,它还使用插入排序。
  • 所以执行 Introsort 时首先会将根据数据量的大小进行讨论:
    1. 如果分区大小有可能超过最大深度限制,则Introsort切换到Heapsort。我们将最大深度限制定义为2*log(N)。
    2. 如果分区大小太小,那么Quicksort会衰减到Insertion Sort。我们将此截止值定义为16(由于研究)。因此,如果分区大小小于16,那么我们将进行插入排序。
    3. 如果分区大小低于限制并且不太小(即在16和2*log(N)之间),则它执行简单的快速排序。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值