【剑指Offer】个人学习笔记_38_字符串的排列
目录
- 题目:
- [剑指 Offer 38. 字符串的排列](https://leetcode-cn.com/problems/zi-fu-chuan-de-pai-lie-lcof/)
- 题目分析
- 初始解答:
- 学习他人:
- 方法一:
- 方法二:
- 方法三:
- 方法四:
- [Java HashSet](https://blog.csdn.net/weixin_43314519/article/details/107473290)
- IntelliJ IDEA必备快捷键
- String、StringBuffer、StringBuilder区别
- 总结
刷题日期:19:0919 星期四2021年4月29日
个人刷题记录,代码收集,来源皆为leetcode
经过多方讨论和请教,现在打算往Java方向发力
主要答题语言为Java
题目:
剑指 Offer 38. 字符串的排列
输入:s = "abc"
输出:["abc","acb","bac","bca","cab","cba"]
1 <= s 的长度 <= 8
题目分析
感觉这个更像是数学问题,返回的是字符串数组对象,因此首先得定义一个res,然后返回为null或者一个元素的结果。限制规定了s长度小于等于8,也就限制了返回的最大值。
怀疑最终的排列种类是n的阶乘。关键在于如何穷举里面的所有可能,而且还要考虑把输入的每个元素分开,作为单个字符串再加起来。
初始解答:
这类问题还是适合采用递归的思想。
在每一次都把字符串分为两部分,第一个和后面所有,用第一个和后面所有字符排列。
接着求后面的排列。
对字符串string的操作还不是很熟练,分解问题的思路也有待提高。
返回的是 字符串数组。
看了下评论,解答都很长,带dfs,还是基础不牢,学习方法一。
class Solution {public String[] permutation(String s) {Set<String> list = new HashSet<>(); //用哈希表来存放结果char[] arr = s.toCharArray(); //字符串转换为字符数组StringBuilder sb = new StringBuilder(); //字符串boolean[] visited = new boolean[arr.length]; //是否重复//进入子程序dfs(arr, "", visited, list); //深度优先搜索return list.toArray(new String[0]); //转换为数组再返回,将list直接转为Object[] 数组;}public void dfs(char[] arr, String s, boolean[] visited, Set<String> list) {if(s.length() == arr.length) {list.add(s);return;}for(int i = 0; i < arr.length; i++) {if(visited[i]) continue;visited[i] = true;//将右边括号内的变量 arr[i] 转换成字符串 dfs(arr, s + String.valueOf(arr[i]), visited, list);visited[i] = false;}}
}
好多看不懂的操作,百度才知道。执行结果:通过
显示详情
执行用时:56 ms, 在所有 Java 提交中击败了15.89%的用户
内存消耗:44.4 MB, 在所有 Java 提交中击败了13.25%的用户
学习K神
class Solution {//外部变量List<String> res = new LinkedList<>();char[] c;public String[] permutation(String s) {c = s. toCharArray();dfs(0);return res.toArray(new String[res.size()]);}void dfs(int x) {if(x == c.length - 1) {res.add(String.valueOf(c)); // 添加可能方案return;}HashSet<Character> set = new HashSet<>();//Character 类用于对单个字符进行操作,Character 类在对象中包装一个基本类型 char 的值for(int i = x; i < c.length; i++) {if(set.contains(c[i])) continue; //重复了,所以剪枝set.add(c[i]);swap(i, x); //交换,将c[i] 固定在x位dfs(x + 1); //考虑第x+1位swap(i, x); //恢复交换}} void swap(int a, int b) { //交换子程序char tmp = c[a];c[a] = c[b];c[b] = tmp;}
}
效率更高了些 执行结果:通过
显示详情
执行用时:14 ms, 在所有 Java 提交中击败了51.57%的用户
内存消耗:43.6 MB, 在所有 Java 提交中击败了40.37%的用户
学习他人:
方法一:
女马了个b 2021-01-30
//我就不明白了,一帮傻吊公众号把排名第一的答案抄一遍加点comment再发上来有什么意思。其实很简单,不需要用到swap,直接用visited记录一下每次已经访问过的char,然后每次for loop 都从头开始,遇到visited直接跳过就可以。
class Solution {public String[] permutation(String s) {Set<String> list = new HashSet<>();char[] arr = s.toCharArray();StringBuilder sb = new StringBuilder();boolean[] visited = new boolean[arr.length];dfs(arr, "", visited, list);return list.toArray(new String[0]);}public void dfs(char[] arr, String s, boolean[] visited, Set<String> list){if(s.length() == arr.length){list.add(s);return;}for(int i=0; i<arr.length; i++){if(visited[i]) continue;visited[i] = true;dfs(arr, s+String.valueOf(arr[i]), visited, list);visited[i] = false;}}
}
方法二:
临扬 2021-03-06
javaDFS+剪枝,这题我按照47.全排列2的方法来做的 ps:我发现大佬们在写dfs函数的时候,喜欢把变量定义为全局变量而不是放进函数参数里欸
class Solution {public String[] permutation(String s) {//对s进行排序String [] kk = s.split("");Arrays.sort(kk);StringBuilder sb = new StringBuilder();for(String x:kk) sb.append(x);s = sb.toString();sb.delete(0,sb.length());List<String> list = new LinkedList<>();boolean [] used = new boolean[s.length()];dfs(sb,s,used,list);return list.toArray(new String[list.size()]);}public void dfs(StringBuilder sb,String s,boolean[] used,List<String> list){if(sb.length() == s.length()){list.add(sb.toString());return;}for(int i=0;i<s.length();i++){//与前一个字母相等,且前一个字母标记为false,说明前一个字母已经被回溯过,同级,不能再使用当前字母,否则重复if(i>0 && !used[i] && s.charAt(i)==s.charAt(i-1) && !used[i-1]) continue;if(!used[i]){sb.append(s.charAt(i));used[i] = true;dfs(sb,s,used,list);sb.delete(sb.length()-1,sb.length());used[i] = false;}}}
}
方法三:
NekoCharmsL3
2021-03-17
class Solution {void dfs(char[] chars, boolean[] visited, StringBuilder buffer, List<String> ans){if(buffer.length() == chars.length){ans.add(new String(buffer));return;}boolean[] set = new boolean[26];for(int i = 0; i < chars.length; i++){if(!visited[i] && !set[chars[i] - 'a']){set[chars[i] - 'a'] = true; // 表示值为chars[i]的字符已经选择过visited[i] = true; // 表示下标i的字符已经选择过buffer.append(chars[i]);dfs(chars, visited, buffer, ans);visited[i] = false;// 回溯状态 重新可选buffer.deleteCharAt(buffer.length() - 1);}}}public String[] permutation(String s) {List<String> ans = new ArrayList<>();dfs(s.toCharArray(), new boolean[s.length()], new StringBuilder(), ans);return ans.toArray(new String[ans.size()]);}
}
class Solution{void Swap(char[] chars, int i, int j){char tem = chars[i];chars[i] = chars[j];chars[j] = tem;}void recursion(char[] chars, int fromIndex, List<String> ans){if(fromIndex == chars.length){ans.add(new String(chars));return;}boolean[] isAdd = new boolean[26];for(int i = fromIndex; i < chars.length; i++){if(!isAdd[chars[i] - 'a']){ // 保证交换上来的数是没有出现过的isAdd[chars[i] - 'a'] = true;Swap(chars, i, fromIndex);recursion(chars, fromIndex + 1, ans);Swap(chars, i, fromIndex);}}}public String[] permutation(String s) {List<String> ans = new ArrayList<>();recursion(s.toCharArray(), 0, ans);return ans.toArray(new String[ans.size()]);}
}
方法四:
K神登场
解题思路:
对于一个长度为 nn 的字符串(假设字符互不重复),其排列方案数共有:
n \times (n-1) \times (n-2) … \times 2 \times 1n×(n−1)×(n−2)…×2×1
排列方案的生成:
根据字符串排列的特点,考虑深度优先搜索所有排列方案。即通过字符交换,先固定第 11 位字符( nn 种情况)、再固定第 22 位字符( n-1n−1 种情况)、… 、最后固定第 nn 位字符( 11 种情况)。
重复排列方案与剪枝:
当字符串存在重复字符时,排列方案中也存在重复的排列方案。为排除重复方案,需在固定某位字符时,保证 “每种字符只在此位固定一次” ,即遇到重复字符时不交换,直接跳过。从 DFS 角度看,此操作称为 “剪枝” 。
作者:jyd
链接:https://leetcode-cn.com/problems/zi-fu-chuan-de-pai-lie-lcof/solution/mian-shi-ti-38-zi-fu-chuan-de-pai-lie-hui-su-fa-by/
来源:力扣(LeetCode)
class Solution {List<String> res = new LinkedList<>();char[] c;public String[] permutation(String s) {c = s.toCharArray();dfs(0);return res.toArray(new String[res.size()]);}void dfs(int x) {if(x == c.length - 1) {res.add(String.valueOf(c)); // 添加排列方案return;}HashSet<Character> set = new HashSet<>();for(int i = x; i < c.length; i++) {if(set.contains(c[i])) continue; // 重复,因此剪枝set.add(c[i]);swap(i, x); // 交换,将 c[i] 固定在第 x 位dfs(x + 1); // 开启固定第 x + 1 位字符swap(i, x); // 恢复交换}}void swap(int a, int b) {char tmp = c[a];c[a] = c[b];c[b] = tmp;}
}
//饭胡啦 (编辑过)2020-04-22
//大佬那个图画的太好了,加上些我的理解,应该是这样子的吧,有错误欢迎指正class Solution {//为了让递归函数添加结果方便,定义到函数之外,这样无需带到递归函数的参数列表中List<String> list = new ArrayList<>();//同;但是其赋值依赖c,定义声明分开char[] c;public String[] permutation(String s) {c = s.toCharArray();//从第一层开始递归dfs(0);//将字符串数组ArrayList转化为String类型数组return list.toArray(new String[list.size()]);}private void dfs(int x) {//当递归函数到达第三层,就返回,因为此时第二第三个位置已经发生了交换if (x == c.length - 1) {//将字符数组转换为字符串list.add(String.valueOf(c));return;}//为了防止同一层递归出现重复元素HashSet<Character> set = new HashSet<>();//这里就很巧妙了,第一层可以是a,b,c那么就有三种情况,这里i = x,正巧dfs(0),正好i = 0开始// 当第二层只有两种情况,dfs(1)i = 1开始for (int i = x; i < c.length; i++){//发生剪枝,当包含这个元素的时候,直接跳过if (set.contains(c[i])){continue;}set.add(c[i]);//交换元素,这里很是巧妙,当在第二层dfs(1),x = 1,那么i = 1或者 2, 不是交换1和1,要就是交换1和2swap(i,x);//进入下一层递归dfs(x + 1);//返回时交换回来,这样保证到达第1层的时候,一直都是abc。这里捋顺一下,开始一直都是abc,那么第一位置总共就3个交换//分别是a与a交换,这个就相当于 x = 0, i = 0;// a与b交换 x = 0, i = 1;// a与c交换 x = 0, i = 2;//就相当于上图中开始的三条路径//第一个元素固定后,每个引出两条路径,// b与b交换 x = 1, i = 1;// b与c交换 x = 1, i = 2;//所以,结合上图,在每条路径上标注上i的值,就会非常容易好理解了swap(i,x);}}private void swap(int i, int x) {char temp = c[i];c[i] = c[x];c[x] = temp;}
}
Java HashSet
HashSet 基于 HashMap 来实现的,是一个不允许有重复元素的集合。
HashSet 不是线程安全的, 如果多个线程尝试同时修改 HashSet,则最终结果是不确定的。 您必须在多线程访问时显式同步对 HashSet 的并发访问。
(4)增强for循环的快捷键生成,使用 集合.for的快捷键方式生成
(5)使用ctrl + alt + t快捷键,可以生成流程控制语句。
(6)使用alt+insert快捷键,可以生成构造方法、get和set方法、重写toString()的方法、重写父类的方法等。
IntelliJ IDEA必备快捷键
Ctrl + D
复制光标所在行或复制选择内容,并把复制内容插入光标位置下面
Ctrl + F
在当前文件进行文本查找
Ctrl + H
查看类的继承结构
Ctrl + N
通过类名定位文件
Ctrl + O
快速重写父类方法
Ctrl + X
剪切所选中行
Ctrl + Y
删除光标所在行或删除选中的行
Ctrl + Z
撤销
Ctrl + F12
弹出当前文件结构层,可以在弹出的层上直接输入进行筛选
Ctrl + Space
基础代码补全默认在Windows系统上被输入法占用,需要进行修改,
建议修改为Ctrl + 分号
Ctrl + /
注释光标所在行代码,会根据当前不同文件类型使用不同的注释符号
Ctrl+Shift+/
多行注释,如果按第二次就是反注释
Alt + Enter
根据光标所在问题,提供快速修复选择,用的最多的是生成变量
Ctrl + Alt + B
在某个调用的方法名上使用会跳到具体的实现处
Ctrl + Alt + L
格式化代码 可以对当前文件和整个包目录使用
Ctrl + Alt + T
对选中的代码弹出环绕选项弹出层
Ctrl + Alt + 左方向键
退回到上一个操作的地方,查看源码的时候很方便
Ctrl + Alt + 右方向键
前进到上一个操作的地方,查看源码的时候很方便
Ctrl + Shift + R
根据输入内容替换对应内容,
范围为整个项目或指定目录内文件
Ctrl + Shift + U
对选中的代码进行大/小写轮流转换
Ctrl + Shift + Z
取消撤销,习惯使用ctrl+y,可以修改快捷键
需要先把ctrl+y删除一行的快捷键解绑
Ctrl + Shift+ /
String、StringBuffer、StringBuilder区别
从后面List的测试结果可以看出,除了对多线程的支持不一样外,这两个类的使用方式和结果几乎没有任何差别。
总结
以上就是本题的内容和学习过程了,这道题还是比较难的,还涉及到很多其他变量的转换,最近因为生病加补习数据结构相关的课程,刷题进度放慢了。
本来昨天又有华为的机试,最后胆怯了没上,还是好好踏实学习吧,把基础的分类排序二分,表图数链栈都弄清楚再上场。
欢迎讨论,共同进步。
【剑指Offer】个人学习笔记_38_字符串的排列相关推荐
- 剑指offer——面试题28:字符串的排列
剑指offer--面试题28:字符串的排列 Solution1: 2018年9月2日重做 典型的DFS套路,LeetCode 46 && 47 [46]https://blog.csd ...
- 剑指offer:面试题38. 字符串的排列
题目:面试题38. 字符串的排列 输入一个字符串,打印出该字符串中字符的所有排列. 你可以以任意顺序返回这个字符串数组,但里面不能有重复元素. 示例: 输入:s = "abc" 输 ...
- [剑指offer]面试题28:字符串的排列
面试题28:字符串的排列 题目:输入一个字符串,打印出该字符串中字符的所有排列.例如输入字符串abc,则打印出由字符a.b.c所能排列出来的所有字符串abc.acb.bac.bca.cab和cba. ...
- 剑指Offer的学习笔记(C#篇)-- 数组中重复的数字
题目描述 在一个长度为n的数组里的所有数字都在0到n-1的范围内. 数组中某些数字是重复的,但不知道有几个数字是重复的.也不知道每个数字重复几次.请找出数组中任意一个重复的数字. 例如,如果输入长度为 ...
- 剑指Offer的学习笔记(C#篇)-- 用两个栈实现队列
题目描述 用两个栈来实现一个队列,完成队列的Push和Pop操作. 队列中的元素为int类型. 一 . 概念! 首先要理解栈和队列的概念. 1. 栈:咱可以简单的把栈理解成装羽毛球的球桶.或者我们吃的 ...
- 【LeetCode】剑指 Offer 20. 表示数值的字符串
[LeetCode]剑指 Offer 20. 表示数值的字符串 文章目录 [LeetCode]剑指 Offer 20. 表示数值的字符串 package offer;import java.util. ...
- 【LeetCode】剑指 Offer 58 - II. 左旋转字符串
[LeetCode]剑指 Offer 58 - II. 左旋转字符串 文章目录 [LeetCode]剑指 Offer 58 - II. 左旋转字符串 一.字符串切片 二.列表遍历拼接 三.字符串遍历拼 ...
- 剑指 Offer II 117. 相似的字符串
链接:剑指 Offer II 117. 相似的字符串 题解: 1.建设图 2.bfs遍历 class Solution { public:int numSimilarGroups(vector< ...
- 算法Day8|字符串专题二 剑指 Offer 58 - II. 左旋转字符串,28. 找出字符串中第一个匹配项的下标,459. 重复的子字符串
剑指 Offer 58 - II. 左旋转字符串 解题思路: 反转区间为前n的子串 反转区间为n到末尾的子串 反转整个字符串 class Solution {public String reverse ...
最新文章
- 基于i.MX RT1060的可编程视觉模块
- git删除远程分支文件
- git rebase -i 修改提交
- Tensorflow:tf.contrib.rnn.DropoutWrapper函数(谷歌已经为Dropout申请了专利!)、MultiRNNCell函数的解读与理解
- java基础之java中的基本数据类型
- 微软Team Foundation Service 的Scrum模板中的Feature和Backlog Items 的区别【转载】
- 生产Docker应用重启排查经历
- modbus_tk与Modubs Slave结合使用
- php正则表达式中的字符是,PHP_PHP正则表达式中的特殊字符,字符/意义:对于字符,通常表 - phpStudy...
- 最新中科院分区2020_最新中科院分区电气领域SCI期刊
- VS中Release模式下生成去掉生成pdb文件
- goflyway安装
- android布局详解
- 天正电气lisp是什么文件_天正电气——那些隐蔽却好用的功能
- python的key函数_由 sort 中 key 的用法浅谈 python
- Badboy录制提示脚本错误解决方案
- 嵌入式Linux之我行——C+CGI+Ajax在S3C244
- Java 常用正则表达式与测试
- AT24C02驱动程序,【I2C串行总线】的组成及工作原理
- JAVA EE JSP collection