算法的学习笔记—字符串的排列(牛客JZ38)

img

😀前言
在编程面试中,字符串排列问题是一类常见但又具有挑战性的题目。本文将通过一道经典的字符串排列题目,带领你一步步深入分析并最终解决这一问题。

🏠个人主页:尘觉主页

🥰字符串的排列

NowCoder

😋问题描述

给定一个字符串,要求输出该字符串中字符的所有排列组合。字符串中字符的排列顺序可以是任意的,但每个排列都必须是字符串中的字符的唯一组合。

例如,输入字符串 "ABC",则输出的所有排列组合应包括 "ABC", "ACB", "BAC", "BCA", "CAB", 和 "CBA"

注: 字符串的长度不超过10,字符只包括大小写字母。

img

数据范围:n<10
要求:空间复杂度 O(n!),时间复杂度 O(n!)

😊示例解析

  • 示例1:

    输入:“ab”

    返回值:[“ab”,“ba”]

  • 示例2:

    输入:“aab”

    返回值:[“aab”,“aba”,“baa”]

  • 示例3:

    输入:“abc”

    返回值:[“abc”,“acb”,“bac”,“bca”,“cab”,“cba”]

  • 示例4:

    输入:“”

    返回值:[“”]

在以上示例中,字符串的排列组合均满足唯一性要求,并且结果顺序可以是任意的。

❤️‍🔥解题思路

这个问题可以归类为典型的回溯算法问题。回溯算法是一种通过探索所有可能的解决方案并回溯以避免无效路径的策略。在这里,我们需要生成所有字符的排列组合,并在生成过程中避免重复排列。

步骤概述:

  1. 排序字符串: 为了在生成排列时去除重复,我们首先对字符串中的字符进行排序。
  2. 回溯法生成排列: 利用回溯法逐一生成所有可能的字符排列,并在每个字符位置选择后进行递归调用。当遍历到字符串末尾时,将该排列加入结果集中。
  3. 去重: 在生成排列的过程中,如果当前字符与前一个字符相同且前一个字符未使用过,则跳过该字符,避免生成重复排列。

😊代码实现

下面是一段基于上述思路的Java代码实现:

import java.util.ArrayList;
import java.util.Arrays;

public class StringPermutation {
    // 定义一个全局变量 ret 来存储所有的排列结果
    private ArrayList<String> ret = new ArrayList<>();

    public ArrayList<String> Permutation(String str) {
        // 如果输入字符串为空,直接返回空结果
        if (str.length() == 0) {
            return ret;
        }
        
        // 将字符串转换为字符数组,并对其排序
        char[] chars = str.toCharArray();
        Arrays.sort(chars);
        
        // 调用回溯算法,生成所有排列
        backtracking(chars, new boolean[chars.length], new StringBuilder());
        
        // 返回所有的排列结果
        return ret;
    }

    private void backtracking(char[] chars, boolean[] hasUsed, StringBuilder s) {
        // 递归基准条件:如果当前排列的长度与字符数组长度相同,说明找到一个完整排列
        if (s.length() == chars.length) {
            // 将排列结果添加到全局列表 ret 中
            ret.add(s.toString());
            return;
        }
        
        // 遍历字符数组,尝试每一个字符
        for (int i = 0; i < chars.length; i++) {
            // 如果当前字符已经被使用过,跳过该字符
            if (hasUsed[i]) {
                continue;
            }
            
            // 如果当前字符与前一个字符相同,且前一个字符没有被使用过,跳过该字符
            // 这是为了避免生成重复的排列
            if (i != 0 && chars[i] == chars[i - 1] && !hasUsed[i - 1]) {
                continue;
            }
            
            // 标记当前字符为已使用
            hasUsed[i] = true;
            // 将当前字符添加到排列中
            s.append(chars[i]);
            
            // 递归调用,继续生成剩余字符的排列
            backtracking(chars, hasUsed, s);
            
            // 回溯过程:移除最后添加的字符,并将其标记为未使用
            s.deleteCharAt(s.length() - 1);
            hasUsed[i] = false;
        }
    }
}

😊代码详解

  1. 递归基准条件:在 backtracking 方法中,当构造的字符串 s 的长度与原字符串长度相同时,说明找到了一个有效的排列,将其加入 ret 列表。
  2. 去重处理:在排列过程中,为避免重复排列的出现,首先对字符数组进行排序,然后在递归时跳过重复的字符(if (i != 0 && chars[i] == chars[i - 1] && !hasUsed[i - 1]))。
  3. 回溯过程:通过使用一个 boolean 数组 hasUsed 来记录每个字符是否已被使用。在递归调用后,撤销当前选择并继续尝试其他可能性(s.deleteCharAt(s.length() - 1); hasUsed[i] = false;)。

😄总结

回溯算法是解决排列问题的有效工具。通过合理的剪枝策略,我们能够在生成所有排列的同时避免不必要的重复计算,从而提高算法的效率。这种方法不仅适用于字符串排列问题,还可以扩展到其他类似的组合问题中。在实际应用中,理解回溯算法的基本思想并灵活运用,将极大地提升你的算法设计能力。

😁热门专栏推荐
想学习vue的可以看看这个

java基础合集

数据库合集

redis合集

nginx合集

linux合集

手写机制

微服务组件

spring_尘觉

springMVC

mybits

等等等还有许多优秀的合集在主页等着大家的光顾感谢大家的支持

🤔欢迎大家加入我的社区 尘觉社区

文章到这里就结束了,如果有什么疑问的地方请指出,诸佬们一起来评论区一起讨论😁
希望能和诸佬们一起努力,今后我们一起观看感谢您的阅读🍻
如果帮助到您不妨3连支持一下,创造不易您们的支持是我的动力🤞

img

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

尘觉

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值