Java数据结构和算法总结-字符串相关高频面试题算法

前言:周末闲来无事,在七月在线上看了看字符串相关算法的讲解视频,收货颇丰,跟着视频讲解简单做了一下笔记,方便以后翻阅复习同时也很乐意分享给大家。什么字符串在算法中有多重要之类的大路边上的客套话就不多说了,直接上笔记吧。

一、字符串

  • java:String内置类型,不可更改。(如需更改可考虑:StringBuffer, StringBuilder,char[]等)

二、归类

 字符串涉及到的相关题型通常会是以下几个方面:

  • 概念理解:字典序
  • 简单操作:插入删除字符、旋转
  • 规则判断(罗马数字转换 是否是合法的整数、浮点数)
  • 数字运算(大数加法,二进制加法)
  • 排序、交换
  • 字符计数:变位词
  • 匹配(正则表达式、全串匹配、KMP、周期判断)
  • 动态规划(LCS、编辑距离、最长回文子串)
  • 搜索(单词变换、排列组合)

三、例题

1、交换:把一个只包含01的串排序,可交换任意两个数的位置,最少需要多少次交换?

  思路:从两头往中间扫荡,扫荡过程中在左边遇到1就和右边遇到的0交换位置,直接到左有下标相遇时结束。 具体代码如下:

 1 public static void main(String[] strs) {
 2         int count = 0;
 3         int[] arrays = new int[] {0, 0, 1, 1, 1, 0, 1, 0, 0, 1};
 4         int left = 0;
 5         int right = arrays.length - 1;
 6         while (true) {
 7             while (arrays[left] == 0) {
 8                 left++;
 9             }
10             while (arrays[right] == 1) {
11                 right--;
12             }
13             if (left >= right) {
14                 break;
15             } else {
16                 int temp = arrays[left];
17                 arrays[left] = arrays[right];
18                 arrays[right] = temp;
19                 count++;
20             }
21         }
22         Logger.println("交换次数:" + count);
23         for (int array : arrays) {
24             Logger.print(array + ", ");
25         }
26 }

清晰起见,交换次数和排序后的的字符串输出如下:

交换次数:3
0, 0, 0, 0, 0, 1, 1, 1, 1, 1,

2、字符串替换和复制:删除一个字符串所有的a,并且复制所有的b(字符数组足够大)

  思路:详细思路见代码注释

 1 public static void main(String[] strs) {
 2         char[] input = new char[]{'a', 'b', 'c', 'd', 'a', 'f', 'a', 'b', 'c', 'd', 'b', 'b', 'a', 'b'};
 3         char[] chars = new char[50];
 4         for (int j = 0; j < input.length; j++) {
 5             chars[j] = input[j];
 6         }
 7         Logger.println("操作前:");
 8         for (char c:chars
 9                 ) {
10             Logger.print(c + ", ");
11         }
12         int n = 0;
13         int countB = 0;
14         // 1、删除a,用n当做新下标,循环遍历数组,凡是不是a的元素都放到新下标的位置,由于新n增长慢,老下标i增长快,所以元素不会被覆盖。
15         // 并且在删除a时顺便记录b的数量,以便下一步复制b时可以提前确定数组最终的最大的下标。
16         for (int i = 0; chars[i] != '\u0000' && i < chars.length; i++) {
17             if (chars[i] != 'a') {
18                 chars[n++] = chars[i];
19             }
20             if (chars[i] == 'b') {
21                 countB++;
22             }
23         }
24 
25         // 2、复制b,由于在第一步中就已经知道了字符串中b的个数,这里就能确定最终字符串的最大下标,从最打下表开始倒着复制原字符串,碰到b时复制即可。
26         int newMaxIndex = n + countB - 1;
27         for (int k = n - 1; k >= 0; k--) {
28             chars[newMaxIndex--] = chars[k];
29             if (chars[k] == 'b') {
30                 chars[newMaxIndex--] = chars[k];
31             }
32         }
33 
34         Logger.println("\n操作后:");
35         for (char c:chars
36                 ) {
37             Logger.print(c + ", ");
38         }
39 }

3、交换星号:一个字符串只包含 * 和数字,请把它的 * 都放在开头。

  如:1 * 2 * 4 * 3 => * * * 1 2 4 3

  • 方案一:倒着操作,从最大下标开始向前遍历,遇到非 * 号的元素则加入"新"下标中,遍历完毕后,j即代表 * 号的个数,然后将0-j赋值为 * 即可。(操作后,数字的相对位置不变) 代码如下:
 1 public static void main(String[] strs) {
 2         char[] chars = new char[]{'1', '*', '4', '3', '*', '5', '*'};
 3         // 方案一(操作后,数字的相对位置不变)
 4         // 倒着操作:从最大下标开始向前遍历,遇到非*号的元素则加入"新"下标中,遍历完毕后,j即代表*号的个数,然后将0-j赋值为*即可。
 5         int j = chars.length - 1;
 6         for (int i = j; i >= 0; i--) {
 7             if (chars[i] != '*') {
 8                 chars[j--] = chars[i];
 9             }
10         }
11         while (j >= 0) {
12             chars[j--] = '*';
13         }
14         for (char c:chars
15                 ) {
16             Logger.print(c + ", ");
17         }
18 }

输出结果如下:

*, *, *, 1, 4, 3, 5,
  • 方案二(操作后,数组的相对位置会变)快排划分,根据循环不变式(每一步循环之后条件都成立):如本题[0..i-1]是*,[i..j-1]是数字,[j...n-1]未探测,循环时,随着i和j增加,维护此条件依然不变,代码如下:
 1 public static void main(String[] strs) {
 2         char[] chars = new char[]{'1', '*', '4', '3', '*', '5', '*'};
 3         // 方案二(操作后,数组的相对位置会变)
 4         // 快排划分,根据循环不变式(每一步循环之后条件都成立):如本题[0..i-1]是*,[i..j-1]是数字,[j...n-1]未探测,循环时,随着i和j增加,维护此条件依然不变
 5         for (int i = 0, j = 0; j < chars.length; ++j) {
 6             if (chars[j] == '*') {
 7                 char temp = chars[i];
 8                 chars[i] = chars[j];
 9                 chars[j] = temp;
10                 i++;
11             }
12         }
13         for (char c:chars
14                 ) {
15             Logger.print(c + ", ");
16         }
17 } 

输出结果如下:

*, *, *, 3, 1, 5, 4,

4、单词翻转

  例如:I am a student =》 student a am I

  思路:

  1、先将整个字符串翻转:如:I am a student =》 tneduts a ma I

  2、通过空格判断出每个单词,然后对每个单词进行翻转

  代码如下:

 1 public static void main(String[] strs) {
 2         String input = "I am a student";
 3         char[] chars = input.toCharArray();
 4         int i = 0;
 5         int j = chars.length - 1;
 6         while (i < j) {
 7             swap(chars, i++, j--);
 8         }
 9         int front = 1;
10         int tail = 0;
11         while (front < chars.length) {
12             if (chars[front] == ' ') {
13                 int frontTemp = front - 1;
14                 while (tail < frontTemp) {
15                     swap(chars, tail++, frontTemp--);
16                 }
17                 tail = front + 1;
18             }
19             front++;
20         }
21         for (char c:chars
22                 ) {
23             Logger.print(c);
24         }
25 }
26 
27 public static void swap(char[] chars, int index1, int index2) {
28         char temp = chars[index1];
29         chars[index1] = chars[index2];
30         chars[index2] = temp;
31 }

输出结果如下:

student a am I

5、子串变位词:给定两个串a和b,问b是否a的子串变位词。

  例如:a=hello。b=lel,lle,ello都是true;b=elo是false

  思路:

    •   一、首先需要了解对两个串是否是变位词的判断:
    1.   对两个串按统一规则排序,排序后若相等则是变位词。
    2.   对两个串中出现的字母统计,两串中相同的字母出现的次数一致则为变位词。
    •   二、然后从母串的第一个元素遍历,每往后遍历一个元素就把从当前元素开始到加上子串的长度的位置作为一个区间和子串比较是否是变位词。

最后一题综合前几个题用到的一些技巧,还是比较有趣的,这里就不贴出代码了,也激发一下大家的动手能力,感兴趣的童鞋不妨试着写一写。

以上问题如有不同思路欢迎交流。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏北京马哥教育

深入 Python 字典的内部实现

字典是通过键(key)索引的,因此,字典也可视作彼此关联的两个数组。下面我们尝试向字典中添加3个键/值(key/value)对: 这些值可通过如下方法访问: 由...

33815
来自专栏Albert陈凯

Spark详解02Job 逻辑执行图Job 逻辑执行图

Job 逻辑执行图 General logical plan ? GeneralLogicalPlan.png 典型的 Job 逻辑执行图如上所示,经过下面四个...

33811
来自专栏angularejs学习篇

angularjs学习第一天笔记

    您好,我是一名后端开发工程师,由于工作需要,现在系统的从0开始学习前端js框架之angular,每天把学习的一些心得分享出来,如果有什么说的不对的地方,...

1031
来自专栏我是攻城师

Apache Pig学习笔记之内置函数(三)

3674
来自专栏angularejs学习篇

angularjs学习第一天笔记

    您好,我是一名后端开发工程师,由于工作需要,现在系统的从0开始学习前端js框架之angular,每天把学习的一些心得分享出来,如果有什么说的不对的地方,...

703
来自专栏程序员维他命

《Effective Objective-C》干货三部曲(一):概念篇

本书是iOS程序员入门的必读书籍,它讲述了在iOS开发中(Objective-C语言)可以遵循的规范和一些开发技巧。

1082
来自专栏java一日一条

面试中单例模式有几种写法

纠结单例模式有几种写法有用吗?有点用,面试中经常选择其中一种或几种写法作为话头,考查设计模式和coding style的同时,还很容易扩展到其他问题。这里讲解几...

826
来自专栏一个会写诗的程序员的博客

《Kotlin 程序设计》第十二章 Kotlin的多线程

Kotlin 1.1 introduced coroutines, a new way of writing asynchronous, non-blockin...

701
来自专栏一个会写诗的程序员的博客

第10章 使用 Kotlin 创建 DSL第10章 使用 Kotlin 创建 DSL

使用DSL的编程风格,可以让程序更加简单干净、直观简洁。当然,我们也可以创建自己的 DSL。相对于传统的API, DSL 更加富有表现力、更符合人类语言习惯。

532
来自专栏一个会写诗的程序员的博客

Java8 Lambda表达式.md什么是λ表达式λ表达式的类型λ表达式的使用其它相关概念

为了支持函数式编程,Java 8引入了Lambda表达式. 在Java 8中采用的是内部类来实现Lambda表达式.具体实现代码,可以通过debug看, 同时...

813

扫码关注云+社区