目录

  • 题目:
    • [剑指 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. 字符串的排列

难度中等257

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

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

示例:

输入: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 允许有 null 值。

HashSet 是无序的,即不会记录插入的顺序。

HashSet 不是线程安全的, 如果多个线程尝试同时修改 HashSet,则最终结果是不确定的。 您必须在多线程访问时显式同步对 HashSet 的并发访问。

HashSet 实现了 Set 接口。

七、快捷键

代码生成常用的快捷键

(1)使用psvm快捷键生成main方法

(2)使用sout快捷键生成输出语句

(3)使用fori快捷键生成普通for循环

(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区别

StringBuffer、StringBuilder和String一样,也用来代表字符串。String类是不可变类,任何对String的改变都 会引发新的String对象的生成;StringBuffer则是可变类,任何对它所指代的字符串的改变都不会产生新的对象。既然可变和不可变都有了,为何还有一个StringBuilder呢?相信初期的你,在进行append时,一般都会选择StringBuffer吧!

HashTable是线程安全的,很多方法都是synchronized方法,而HashMap不是线程安全的,但其在单线程程序中的性能比HashTable要高。StringBuffer和StringBuilder类的区别也是如此,他们的原理和操作基本相同,区别在于StringBufferd支持并发操作,线性安全的,适 合多线程中使用。StringBuilder不支持并发操作,线性不安全的,不适合多线程中使用。新引入的StringBuilder类不是线程安全的,但其在单线程中的性能比StringBuffer高。

从上面的结果可以看出,不考虑多线程,采用String对象时(我把Count/100),执行时间比其他两个都要高,而采用StringBuffer对象和采用StringBuilder对象的差别也比较明显。由此可见,如果我们的程序是在单线程下运行,或者是不必考虑到线程同步问题,我们应该优先使用StringBuilder类;如果要保证线程安全,自然是StringBuffer。

从后面List的测试结果可以看出,除了对多线程的支持不一样外,这两个类的使用方式和结果几乎没有任何差别。

总结

以上就是本题的内容和学习过程了,这道题还是比较难的,还涉及到很多其他变量的转换,最近因为生病加补习数据结构相关的课程,刷题进度放慢了。

本来昨天又有华为的机试,最后胆怯了没上,还是好好踏实学习吧,把基础的分类排序二分,表图数链栈都弄清楚再上场。

欢迎讨论,共同进步。

【剑指Offer】个人学习笔记_38_字符串的排列相关推荐

  1. 剑指offer——面试题28:字符串的排列

    剑指offer--面试题28:字符串的排列 Solution1: 2018年9月2日重做 典型的DFS套路,LeetCode 46 && 47 [46]https://blog.csd ...

  2. 剑指offer:面试题38. 字符串的排列

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

  3. [剑指offer]面试题28:字符串的排列

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

  4. 剑指Offer的学习笔记(C#篇)-- 数组中重复的数字

    题目描述 在一个长度为n的数组里的所有数字都在0到n-1的范围内. 数组中某些数字是重复的,但不知道有几个数字是重复的.也不知道每个数字重复几次.请找出数组中任意一个重复的数字. 例如,如果输入长度为 ...

  5. 剑指Offer的学习笔记(C#篇)-- 用两个栈实现队列

    题目描述 用两个栈来实现一个队列,完成队列的Push和Pop操作. 队列中的元素为int类型. 一 . 概念! 首先要理解栈和队列的概念. 1. 栈:咱可以简单的把栈理解成装羽毛球的球桶.或者我们吃的 ...

  6. 【LeetCode】剑指 Offer 20. 表示数值的字符串

    [LeetCode]剑指 Offer 20. 表示数值的字符串 文章目录 [LeetCode]剑指 Offer 20. 表示数值的字符串 package offer;import java.util. ...

  7. 【LeetCode】剑指 Offer 58 - II. 左旋转字符串

    [LeetCode]剑指 Offer 58 - II. 左旋转字符串 文章目录 [LeetCode]剑指 Offer 58 - II. 左旋转字符串 一.字符串切片 二.列表遍历拼接 三.字符串遍历拼 ...

  8. 剑指 Offer II 117. 相似的字符串

    链接:剑指 Offer II 117. 相似的字符串 题解: 1.建设图 2.bfs遍历 class Solution { public:int numSimilarGroups(vector< ...

  9. 算法Day8|字符串专题二 剑指 Offer 58 - II. 左旋转字符串,28. 找出字符串中第一个匹配项的下标,459. 重复的子字符串

    剑指 Offer 58 - II. 左旋转字符串 解题思路: 反转区间为前n的子串 反转区间为n到末尾的子串 反转整个字符串 class Solution {public String reverse ...

最新文章

  1. 基于i.MX RT1060的可编程视觉模块
  2. git删除远程分支文件
  3. git rebase -i 修改提交
  4. Tensorflow:tf.contrib.rnn.DropoutWrapper函数(谷歌已经为Dropout申请了专利!)、MultiRNNCell函数的解读与理解
  5. java基础之java中的基本数据类型
  6. 微软Team Foundation Service 的Scrum模板中的Feature和Backlog Items 的区别【转载】
  7. 生产Docker应用重启排查经历
  8. modbus_tk与Modubs Slave结合使用
  9. php正则表达式中的字符是,PHP_PHP正则表达式中的特殊字符,字符/意义:对于字符,通常表 - phpStudy...
  10. 最新中科院分区2020_最新中科院分区电气领域SCI期刊
  11. VS中Release模式下生成去掉生成pdb文件
  12. goflyway安装
  13. android布局详解
  14. 天正电气lisp是什么文件_天正电气——那些隐蔽却好用的功能
  15. python的key函数_由 sort 中 key 的用法浅谈 python
  16. Badboy录制提示脚本错误解决方案
  17. 嵌入式Linux之我行——C+CGI+Ajax在S3C244
  18. Java 常用正则表达式与测试
  19. AT24C02驱动程序,【I2C串行总线】的组成及工作原理
  20. JAVA EE JSP collection

热门文章

  1. 特色工作流引擎:助推企业实现端到端全流程打通
  2. FPGA中DDR3 mig核的时钟以及带宽说明
  3. 一般网站有哪些类型的
  4. 用python抢票犯法吗_火车票抢票python代码公开揭秘!
  5. 2022年3月时事政治每日一练
  6. msk调制 vhdl_MSK调制解调技术的原理及应用分析
  7. 【征文】Hadoop十周年特别策划——我与Hadoop不得不说的故事
  8. 网易两份凉拌面「面经」
  9. 如何制作gif图片?如何制作你项目的动态效果图到你的csdn?
  10. Sams Teach Yourself SQL in 10 Minutes, Third Edition