题⽬描述

输⼊⼀个字符串,按字典序打印出该字符串中字符的所有排列。例如输⼊字符串 abc ,则按字典序打印出由字符 a , b , c 所能排列出来的所有字符串 abc , acb , bac , bca , cab 和 cba 。

输⼊描述:输⼊⼀个字符串,⻓度不超过9(可能有字符重复),字符只包括⼤⼩写字⺟

思路及解答

递归回溯(使用Set去重)

看到题目,应该就知道要使⽤回溯了。

通过 ​回溯算法​ 生成所有排列,配合 ​剪枝条件​ 实现去重和字典序输出。关键步骤包括:

  1. 固定位置法​:逐个固定每个位置的字符
  2. 交换策略​:通过交换字符位置生成不同排列
  3. 去重处理​:使用集合(Set)或排序后跳过重复字符来避免重复排列
  4. 字典序排序​:最后对结果进行排序
import java.util.ArrayList;
import java.util.Collections; public class StringPermutation {
public ArrayList<String> permutation(String str) {
ArrayList<String> result = new ArrayList<>();
if (str == null || str.length() == 0) {
return result;
} char[] chars = str.toCharArray();
permute(chars, 0, result);
Collections.sort(result); // 按字典序排序
return result;
} private void permute(char[] chars, int begin, ArrayList<String> result) {
if (begin == chars.length - 1) {
result.add(new String(chars));
return;
} for (int i = begin; i < chars.length; i++) {
// 跳过重复字符,避免重复排列
if (i != begin && chars[i] == chars[begin]) {
continue;
} swap(chars, begin, i);
permute(chars, begin + 1, result);
swap(chars, i, begin); // 回溯,恢复原始状态
}
} private void swap(char[] chars, int i, int j) {
char temp = chars[i];
chars[i] = chars[j];
chars[j] = temp;
}
}
  • 时间复杂度​:O(n*n!),n为字符串长度,n!是排列总数,每次排列需要O(n)时间
  • 空间复杂度​:O(n!),需要存储所有排列结果

回溯+剪枝法(优化去重)

上面方法主要是通过Set进行去重,也可以将字符数组排序来跳过重复字符:

  1. 先排序​:将字符数组排序,使相同字符相邻
  2. 剪枝策略​:在递归过程中跳过相同字符的重复分支
  3. 标记数组​:使用boolean数组记录已使用字符
public class StringPermutation {
public ArrayList<String> permutation(String str) {
ArrayList<String> result = new ArrayList<>();
if (str == null || str.length() == 0) {
return result;
} char[] chars = str.toCharArray();
Arrays.sort(chars); // 先排序便于去重
boolean[] used = new boolean[chars.length];
backtrack(chars, used, new StringBuilder(), result);
return result;
} private void backtrack(char[] chars, boolean[] used,
StringBuilder path, ArrayList<String> result) {
if (path.length() == chars.length) {
result.add(path.toString());
return;
} for (int i = 0; i < chars.length; i++) {
// 剪枝条件:跳过已使用字符或相同字符的重复分支
if (used[i] || (i > 0 && chars[i] == chars[i-1] && !used[i-1])) {
continue;
} used[i] = true;
path.append(chars[i]);
backtrack(chars, used, path, result);
path.deleteCharAt(path.length() - 1); // 回溯
used[i] = false;
}
}
}
  • 时间复杂度​:O(n*n!),但剪枝减少了不必要的递归调用
  • 空间复杂度​:O(n!),结果存储空间

非递归

此方法算法理解难度较大,非标准解法

用字典序生成下一个排列的算法:

  1. 初始排序​:将字符数组按字典序排序
  2. 找下一个排列​:
    • 从后向前找到第一个升序对
    • 交换适当元素
    • 反转后缀
  3. 循环生成​:直到无法生成下一个排列
import java.util.ArrayList;
import java.util.Arrays; public class StringPermutation {
public ArrayList<String> permutation(String str) {
ArrayList<String> result = new ArrayList<>();
if (str == null || str.length() == 0) {
return result;
} char[] chars = str.toCharArray();
Arrays.sort(chars); // 初始排序
result.add(new String(chars)); while (true) {
int i = chars.length - 2;
// 从后向前找第一个升序对
while (i >= 0 && chars[i] >= chars[i + 1]) {
i--;
}
if (i < 0) break; // 已是最大排列 int j = chars.length - 1;
// 找到第一个大于chars[i]的字符
while (chars[j] <= chars[i]) {
j--;
} swap(chars, i, j);
reverse(chars, i + 1, chars.length - 1);
result.add(new String(chars));
} return result;
} private void swap(char[] chars, int i, int j) {
char temp = chars[i];
chars[i] = chars[j];
chars[j] = temp;
} private void reverse(char[] chars, int start, int end) {
while (start < end) {
swap(chars, start++, end--);
}
}
}
  • 时间复杂度​:O(n*n!),每次生成下一个排列需要O(n)时间
  • 空间复杂度​:O(n!),结果存储空间

剑指offer-27、字符串的排列的更多相关文章

  1. 剑指Offer 27. 字符串的排列 (字符串)

    题目描述 输入一个字符串,按字典序打印出该字符串中字符的所有排列.例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba. 输入描述: 输 ...

  2. [剑指Offer] 27.字符串的排列

    [思路]从第一位开始,判断每一位字符的所有可能性,依此递归. class Solution { public: void PermutationHelp(vector<string> &a ...

  3. 剑指 Offer 38. 字符串的排列 + 无重复元素的全排列

    剑指 Offer 38. 字符串的排列 Offer_38 题目描述 解题思路 可以使用递归实现全排列,每次都确定一个数的位置,当所有位置的数都确定后即表示一个排列. 但是考虑到本题需要排除重复的排列, ...

  4. 剑指 Offer 38. 字符串的排列

    剑指 Offer 38. 字符串的排列 输入一个字符串,打印出该字符串中字符的所有排列. 你可以以任意顺序返回这个字符串数组,但里面不能有重复元素. 示例: 输入:s = "abc" ...

  5. 【剑指Offer】字符串的排列 解题报告(Python)

    [剑指Offer]字符串的排列 解题报告(Python) 标签(空格分隔): LeetCode 题目地址:https://www.nowcoder.com/ta/coding-interviews 题 ...

  6. 【Java】 剑指offer(38) 字符串的排列

    本文参考自<剑指offer>一书,代码采用Java语言. 更多:<剑指Offer>Java实现合集   题目 输入一个字符串,打印出该字符串中字符的所有排列.例如输入字符串ab ...

  7. Go语言实现:【剑指offer】字符串的排列

    该题目来源于牛客网<剑指offer>专题. 输入一个字符串,按字典序打印出该字符串中字符的所有排列.例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,b ...

  8. 《剑指offer》字符串的排列

    本题来自<剑指offer> 反转链表 题目: 思路: C++ Code: Python Code: 总结:

  9. 剑指offer:字符串的排列

    题目描述: 输入一个字符串,按字典序打印出该字符串中字符的所有排列.例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba. 输入描述: ...

  10. 剑指OFFER之字符串的排列(九度OJ1369)

    题目描述: 输入一个字符串,按字典序打印出该字符串中字符的所有排列.例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba. 输入: 每个 ...

随机推荐

  1. vue_模版语法、计算属性和监视、样式绑定

    vue引入 <html lang="en"> <head> <meta charset="UTF-8"> <title ...

  2. java--jdbc基础

    jdbc连接数据库 链接数据库时要先导包,导入数据库链接驱动程序包 public class Demo1 { //连接数据库的URL private String url = "jdbc:m ...

  3. 【6】ST表学习笔记

    前言 学习ST表,主要是倍增思想,可以理解为倍增优化后的DP.写在这里,一方面方便自己以后复习,另一方面给其他人参考. UPD on 2023/3/21 :修改了格式,使格式与其他的学习笔记统一. 倍 ...

  4. 前端开发系列129-进阶篇之Throttle And Debounce

    本文讨论前端开发中 函数防抖 和 函数节流,它们的应用.区别以及简单实现. 在前端开发中我们可能经常需要给(页面)标签绑定一些持续触发的事件,如 resize.scroll.input.mousemo ...

  5. USB3.0 PHY方案(FT601Q)在 FPGA上的速率验证

    一.背景 高通量在体神经信号采集系统,随着通道数增加.增加实时刺激需求等,采用以太网传输面临带宽极限,亟需一种更快的传输介质. 目前以太网的带宽极限:实测800Mbit/s左右,[移植并使用Iperf ...

  6. 多源异构数据源融合怎么做?Join操作篇(2)

    在探讨多源异构数据融合的过程中,除了上篇介绍的通过Union方式实现的数据整合之外,Join操作同样是一种非常重要的手段.如果说Union是从横向角度将不同来源但结构相似的数据集合起来的话,那么Joi ...

  7. 从硬盘爆满到 GitHub 封号,一位前端开发者的开源历险记

    前段时间,我结识了一位前端工程师「1024小神」.他的"战友"是一台 MacBook Air M3(8G+256GB),原本用来开发网页.小程序,绰绰有余. 然而,他的噩梦始于老板 ...

  8. javaWeb发展历史

    https://blog.csdn.net/weixin_42694511/article/details/121599320 https://blog.csdn.net/qq_43421035/ar ...

  9. 用 UI、MinIO Client、API 操作 RustFS 存储桶

    MinIO 是全球知名的对象存储系统,开源免费,但是最近两年 MinIO 在开源版本上删除了一些对用户很重要的功能(比如 UI 操作),引发了社区.用户的不满.为此,国内研发团队基于 Rust 自研了 ...

  10. 2025熵密杯 -- 初始谜题 -- Reproducibility

    2025熵密杯 -- 初始谜题 -- Reproducibility 前言 本文记录2025熵密杯初始谜题赛题复现过程,参考languag3师傅的熵密杯题解博客.膜拜大佬~ https://langu ...