list转字符串_剑指offer 38——字符串的排列

21ddf0e2c0744d77381bf71facdf61a8.png

本题主要在于对回溯的理解,优化时可以结合 java 特性,以及排列的一些知识。

原题

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

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

示例:

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

限制:

1 <= s 的长度 <= 8

原题url:https://leetcode-cn.com/problems/zi-fu-chuan-de-pai-lie-lcof

解题

回溯

回溯算法的基本思想是:从一条路往前走,能进则进,不能进则退回来,换一条路再试。

大家在解决经典的八皇后问题时,大多都会采用回溯进行解决。

本问题其实就是求所有字符的排列组合,针对这种问题,也可以利用回溯进行解决,但要求不能重复,因此需要进行剪枝

比如字符串 abc ,如果让我们求所有排列,肯定是:

  1. 先固定第 1 位,从 a 、b 、 c 中选一个,比如 a。
  2. 在以 a 为第 1 位的前提下,固定第 2 位,从 b 、 c 中选一个,比如 b。
  3. 此时第 3 位也没有可以选择的余地了,只剩下 c,这一步就走完了。
  4. 退回第 2 步,依旧在第 2 位,这次选择 c 。
  5. 此时第 3 位也没有可以选择的余地了,只剩下 b,这一步也走完了。
  6. 退回第 1 步。

从上面,你可以总结出,正常的回溯,就是先走一条路,当结束后,退回上一步继续走,反复执行,直至退无可退,结束流程。

我们可以发现,最终是没有可以选择的余地,这在程序里可以理解为,运行到下一位时,不能使用之前使用过的数据,因此会涉及到字符交换。

但因为会进行回溯,所以数字可以在回溯后再换回去,从而不影响下一次的回溯。

那什么叫剪枝呢?就是要排除一些情况,针对本题,就是要排除重复的情况。

也就是在同一位置,不能出现两次相同的字符,因为第 2 次出现时,之前肯定已经针对这种情况,所有路线都已经走过了。

因此可以联想到使用集合,存储当前位置出现过的字符,如果重复,就可以直接跳过。

接下来我们看看代码:

class Solution {char[] array;List<String> result = new LinkedList<>();public String[] permutation(String s) {array = s.toCharArray();// 回溯backtrack(0);// 赋值给数组String[] resultArray = new String[result.size()];int index = 0;for (String str : result) {resultArray[index] = str;index++;}return resultArray;}private void backtrack(int index) {// 如果是最后一个位置,就可以添加进result中if (index == array.length - 1) {StringBuilder sb = new StringBuilder();for (char temp : array) {sb.append(temp);}result.add(sb.toString());return;}Set<Character> set = new HashSet<>();for (int i = index; i < array.length; i++) {// 保证不会重复if (set.contains(array[i])) {continue;}set.add(array[i]);// 交换两者的位置swap(index, i);// 固定下一个位置,继续寻找backtrack(index + 1);// 还原两者的位置swap(i, index);}}private void swap(int index, int newIndex) {char temp = array[index];array[index] = array[newIndex];array[newIndex] = temp;}
}

提交OK。

分析一下复杂度:

  • 时间复杂度 O(N!) : 这个比较好理解,长度为 N 的字符串,需要计算的次数是: N * (N - 1) * (N - 2) * ... * 2 * 1,结果也就是 N! 。
  • 空间复杂度 O(N^2) : 需要借助的额外空间,也就是那个保证不会重复所使用到的set,它所存储的总量,最差情况下,长度为 N 的字符串中,所有字符各不相同,也就需要 N + (N - 1) + (N - 2) * ... * 2 * 1,结果也就是 N^2。

java 优化

针对上面代码中出现的 char[]String,可以使用String.valueOf(char[])方法进行优化,因为该方法,最终会使用System.arrayCopy方法,该方法属于native方法,更加高效。

至于最终,将 list 转 array 的过程,可以用list.toArray(String[])做写法上的简化,性能上倒并没有什么提升。

优化后的代码为:

class Solution {char[] array;List<String> result = new LinkedList<>();public String[] permutation(String s) {array = s.toCharArray();// 回溯backtrack(0);// 赋值给数组return result.toArray(new String[result.size()]);}private void backtrack(int index) {// 如果是最后一个位置,就可以添加进result中if (index == array.length - 1) {result.add(String.valueOf(array));return;}Set<Character> set = new HashSet<>();for (int i = index; i < array.length; i++) {// 保证不会重复if (set.contains(array[i])) {continue;}set.add(array[i]);// 交换两者的位置swap(index, i);// 固定下一个位置,继续寻找backtrack(index + 1);// 还原两者的位置swap(i, index);}}private void swap(int index, int newIndex) {char temp = array[index];array[index] = array[newIndex];array[newIndex] = temp;}
}

继续优化

其实到了,如果想进一步优化的话,可以针对 list 转 array 这里。

因为我们使用的是 LinkedList,内部存储的 String 对象在物理上是不连续的,在最后遍历时会相对比较耗时。

如果我们一开始就可以求出所有该字符串所能获得的所有不重复字符串的总个数的话,就可以提前构造一个 array,不需要在最后又遍历一次 list 了。

那么如何求出有重复字符的所有排列呢?假设是字符串aabbc,其求法为:

  1. 假设先排 a ,一共 5 个位置,选 2 个位置,C(5, 2) = (5 * 4) / (2 * 1) = 10
  2. 再排 b ,剩下 3 个位置里,选 2 个位置,C(3, 2) = (3 * 2) / (2 * 1) = 3
  3. 最后排 c ,剩下 1 个位置里,选 1 个位置,C(1, 1) = 1
  4. 综上,一共有10 * 3 * 1 = 30种排列。

接下来看看代码:

class Solution {char[] array;String[] result;int resultIndex = 0;public String[] permutation(String s) {array = s.toCharArray();// 求出一共有多少种可能int totalCount = calculate();result = new String[totalCount];// 回溯backtrack(0);// 赋值给数组return result;}private int calculate() {// 各字符出现的次数,默认只会出现26个英文字母int[] countArray = new int[26];for (char temp : array) {countArray[temp - 'a'] += 1;}// 统计总次数int length = array.length;int totalCount = 1;for (int count : countArray) {if (count == 0) {continue;}// 求排列totalCount *= cc(length, count);length -= count;}return totalCount;}private int cc(int total, int count) {// 如果count超过total的一半,则换成 (total - count),因为在排列中,C(5, 4) = C(5, 1)if (count > total / 2) {count = total - count;}// 分别求分子、分母int result = 1;int result1 = 1;for (int i = 0; i < count; i++) {result *= (total - i);result1 *= (count - i);}return result / result1;}private void backtrack(int index) {// 如果是最后一个位置,就可以添加进result中if (index == array.length - 1) {result[resultIndex++] = String.valueOf(array);return;}// 默认只会出现26个英文字母boolean[] exists = new boolean[26];for (int i = index; i < array.length; i++) {// 保证不会重复if (exists[array[i] - 'a']) {continue;}exists[array[i] - 'a'] = true;// 交换两者的位置swap(index, i);// 固定下一个位置,继续寻找backtrack(index + 1);// 还原两者的位置swap(i, index);}}private void swap(int index, int newIndex) {char temp = array[index];array[index] = array[newIndex];array[newIndex] = temp;}
}

提交OK,其执行时间最短,因此认为优化是有效的。

总结

以上就是这道题目我的解答过程了,不知道大家是否理解了。本题主要在于对回溯的理解,优化时可以结合 java 特性,以及排列的一些知识。

有兴趣的话可以访问我的博客或者关注我的公众号、头条号,说不定会有意外的惊喜。

https://death00.github.io/

公众号:健程之道

0457f8850ac3b9d133cfde0e4bee7336.png

2cd133b81ce270bdce04d479f8fa5816.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/504941.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

v5系列服务器后面板不存在以下哪款指示,群晖RS10613xs+ NAS服务器后面板简介

群晖RS10613xs NAS服务器后面板简介群晖RS10613xs NAS服务器后面板简介:NAS服务器的后面板往往承担着数据的输入、输出&#xff0c;电影的输入&#xff0c;网络的传输&#xff0c;容量的扩展&#xff0c;电能的支持以及产品的散热等重要功能&#xff0c;看似简单的后面板往往是…

怎么判断冠词用a还是an_【语法微课堂】英语冠词的用法,学会这4点,轻松玩转a、an、the...

点击上方??蓝色字&#xff0c;轻松关注&#xff01;Well begun is half done.良好的开端是成功的一半。准备了一下午&#xff0c;终于可以给大家更新了&#xff0c;给大家分享了冠词讲解的视频、音频和文字版&#xff0c;自行取用吧&#xff01;?冠词讲解视频版(小提示&…

pb自定义控件 事件_Android WebView与下拉刷新控件滑动冲突的解决方法

使用WebView时一般会在外层使用下拉刷新控件如(SwipeRefreshLayout)。但是测试时会发现网页无法上拉&#xff0c;往上滑动就会触发下拉刷新控件的refresh事件。所以这里记录一下解决该问题的办法。1、通过webView.getScrollY() 的值来判断是否滚动到顶部private SwipeRefreshLa…

双路服务器cpu必须型号相同,双路主板存在使用不同型号的cpu之说吗?还是必须使用一模一样相同的cpu型号?...

双路主板不存在使用不同型号的cpu一说&#xff0c; 可以使用不同型号的cpu&#xff0c; 不过参数差别不能过大(例如处理器的架构差别)多路主板就是一种主从结构&#xff0c; 处理器之间是协同工作&#xff0c;由中间的高速总线实现两个处理器的配合&#xff0c;不存在处理器必须…

打开多个界面_如何创建用户界面

CANBusKit&#xff0c;是一款集成汽车总线开发、测试、分析的专业软件工具。本章内容主要介绍如何使用CBK_OpenPanel工具为CANBUSKIT 工程创建用户界面&#xff0c;本工具目前支持Vector的xvp格式的面板文件导入。首先是启动软件(试用版软件只能从CANBusKit软件界面中启动该软件…

汤姆克兰西全境封锁服务器维护时间,汤姆克兰西全境封锁无法登录怎么解决 无法登录解决方法攻略...

《汤姆克兰西&#xff1a;全境封锁》是款大型射击游戏&#xff0c;这款游戏的画面十分的精致&#xff0c;在这款游戏中会有各种不同的任务&#xff0c;玩家要带着武器来进行射击。在游戏的时候很多玩家们都反映无法登录怎么解决&#xff1f;那么下面小编就为玩家们详细解说下关…

需要的依赖_三十而已:夫妻关系中需要的是坦诚和依赖

最近三十而已大热播&#xff0c;开始时很多人都看好顾佳和许幻山这一对&#xff0c;顾佳有才有颜&#xff0c;上得厅堂下得厨房&#xff0c;处理事情干净利索&#xff0c;是难得的贤内助。许幻山温柔帅气还有才&#xff0c;关键是还对老婆好&#xff0c;他们的组合可以说是很让…

语言建立一个学生籍贯管理簿_编写一个Excel自定义函数,身份证信息提取如探囊取物...

观看视频更直观我们建立信息表时不仅要输入性别、生日和年龄等信息&#xff0c;往往也需要输入身份证号码&#xff0c;而身份证号码中包含有籍贯、性别、生日和年龄等信息&#xff0c;从身份证号码中提取上述信息可以减少输入工作量&#xff0c;提高工作效率。利用Excel中的内置…

原码一位乘法器设计_数字IC校招基础知识点复习(七)——超前进位加法器、Wallace树、Booth乘法器...

1.超前进位加法器看了一些面经&#xff0c;提到会让你用基础的门搭加法器&#xff0c;因此首先得熟悉半加器&#xff0c;全加器等最基础的加法器才能理解之后的超前进位加法器&#xff0c;树型加法器等复杂的加法器。半加器的输入为a&#xff0c;b&#xff0c;输出为结果s和进位…

保存点云数据_3D点云配准(二多幅点云配准)

本文首发于微信公众号「3D视觉工坊」&#xff1a;3&#xff24;点云配准&#xff08;二多幅点云配准&#xff09; 在上一篇文章 点云配准&#xff08;一 两两配准&#xff09;中我们介绍了两两点云之间的配准原理。本篇文章&#xff0c;我们主要介绍一下PCL中对于多幅点云连续配…

url存在宽字节跨站漏洞_5分钟速览丨常见的Web安全漏洞及测试方法

中秋小长假“余额”就剩半天了尽管心里有太多不舍也要调整自己毕竟假期都是短暂的工作才是职场人生的常态为了尽快消除“假日综合症”e小安贴心送上小文一篇小伙伴们赶紧“脉动”回来吧各类web应用充斥在我们的网络生活中&#xff0c;但是因为开发者安全意识不强而导致的安全问…

9切换中文mac_超详细的Mac重装系统教程!让重装系统变得简单起来!

mac电脑该怎么重装系统&#xff1f;苹果电脑在长时间使用后&#xff0c;系统可能会变得比较慢&#xff0c;另外各种缓存垃圾也会越堆越多&#xff0c;影响电脑的反应速度。mac OS系统是苹果电脑独有的操作系统,重装mac系统过程和Win系统完全不同,所以第一次使用苹果电脑的用户都…

delphi datasnap断线后再次连接_电脑连接WiFi后经常出现断线断开连接问题的解决方法...

电脑连接WiFi总是断线怎么办&#xff1f;有用户的笔记本在连接无线网络后&#xff0c;一直出现断线问题&#xff0c;并且网速也很慢&#xff0c;这是怎么回事&#xff1f;电脑连接WiFi后经常断开连接该如何解决&#xff1f;下面给大家分享具体操作步骤。操作步骤&#xff1a;1、…

添加javascript代码:_JavaScript(1)

一、JavaScript组成1.ECMAScript&#xff1a;是ECMA制定的脚本语言的语法标准&#xff0c;基础语法的规范&#xff0c;为了让不同的浏览器都可以运行通过标准运行出来的代码。2.文档对象模型(DOM)&#xff1a;JavaScript操作网页上的元素(标签)的API。3.浏览器对象模型(BOM)&am…

win 8 共享连接数是多少_局域网IP限速怎么配置,限速多少比较合适

由于视频和下载可以轻易的占用大量带宽&#xff0c;为了网络的稳定运行&#xff0c;大部分局域网都会对客户端进行一定的限速。本文中&#xff0c;我将介绍如何根据带宽来做限速&#xff0c;限速设置多少比较合理&#xff1f;1. 限速多少比较合理&#xff1f;正常的办公上网&am…

xp 4g内存补丁_32位操作系统导致电脑可用内存不足4G

现象描述今天给大家分享一个关于电脑使用的小知识&#xff1a;电脑安装32位操作系统&#xff0c;导致电脑可用内存低于4G。详细解释一下&#xff1a;如果你的电脑安装的操作系统是32位操作系统&#xff0c;无论你电脑安装了多大的内存&#xff0c;可用内存都不会超过4G&#xf…

maven 镜像_Maven(一)

1. 掌握M2Elipse插件安装及Maven仓库的配置2. 掌握Maven工程目录结构和创建工程3. 掌握Maven工程的关系4. 掌握Maven常见的插件5. 掌握Maven命令壹、什么是 Maven在Maven中&#xff0c;可以理解为所有的项目都是一个个的对象。贰、Maven 和 ANT 的区别叁、 Maven 的下载与 IDE …

文本编辑器查看 cprintf颜色_实战PyQt5: 028-纯文本编辑控件QPlainTextEdit

QPlainTextEdit简介QPlainTextEdit小部件是一个用于编辑和显示纯文本控件&#xff0c;QPlainTextEdit控件与QTextEdit控件使用了相同的技术和概念&#xff0c;但是它为纯文本处理提供了优化。QPlainTextEdit是一个支持纯文本的高级查看器/编辑器&#xff0c;可以处理大型文档并…

使用通用mapper实现条件查询_【微服务】152:Stream流和通用mapper批量查询的使用...

今天是刘小爱自学Java的第152天。感谢你的观看&#xff0c;谢谢你。学习计划安排如下&#xff1a;补充完昨天商品查询中关于分类和品牌的部分&#xff0c;其中牵扯到了两个非常重要的知识点&#xff1a;Stream流的使用&#xff0c;这个学过后基本就没怎么使用过&#xff0c;这次…

java递归实现多级菜单栏_vue+ java 实现多级菜单递归效果

效果如图&#xff1a;大概思路&#xff1a;树形视图使用的是vue官方事例代码&#xff0c;java负责封装数据&#xff0c;按照vue官方事例的数据结构封装数据即可。有两个需要关注的点&#xff1a;1.官方事例的数据结构是一个对象里面包含着集合&#xff0c;而不是一个集合对象 2…