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


    前言

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

    一、字符串

    • 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 }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

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

    交换次数:3
    0, 0, 0, 0, 0, 1, 1, 1, 1, 1,
    
    • 1
    • 2
    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 }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    输出结果如下:

    *, *, *, 1, 4, 3, 5,
    
    • 1
    • 方案二(操作后,数组的相对位置会变)快排划分,根据循环不变式(每一步循环之后条件都成立):如本题[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 } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    输出结果如下:

    *, *, *, 3, 1, 5, 4,
    
    • 1
    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 }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    输出结果如下:

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

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

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

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

    最后再分享一份面试题答案解析,

    由于内容比较多,篇幅有限,资料已经被整理成了PDF文档,有需要2023年Android中高级最全面试真题答案 完整文档的可扫码领取!!!

    目录

    img

    第一章 Java方面

    • Java基础部分
    • Java集合
    • Java多线程
    • Java虚拟机

    img

    第二章 Android方面

    • Android四大组件相关
    • Android异步任务和消息机制
    • Android UI绘制相关
    • Android性能调优相关
    • Android中的IPC
    • Android系统SDK相关
    • 第三方框架分析
    • 综合技术
    • 数据结构方面
    • 设计模式
    • 计算机网络方面
    • Kotlin方面

    img

    第三章 音视频开发高频面试题

    • 为什么巨大的原始视频可以编码成很小的视频呢?这其中的技术是什么呢?
    • 怎么做到直播秒开优化?
    • 直方图在图像处理里面最重要的作用是什么?
    • 数字图像滤波有哪些方法?
    • 图像可以提取的特征有哪些?
    • 衡量图像重建好坏的标准有哪些?怎样计算?

    img

    第四章 Flutter高频面试题

    • Dart部分
    • Flutter部分

    img

    第五章 算法高频面试题

    • 如何高效寻找素数
    • 如何运用二分查找算法
    • 如何高效解决雨水问题
    • 如何去除有序数组的重复元素
    • 如何高效进行模幂运算
    • 如何寻找最长回文子串

    img

    第六章 Andrio Framework方面

    • 系统启动流程面试题解析
    • Binder面试题解析
    • Handler面试题解析
    • AMS面试题解析

    img

  • 相关阅读:
    上周热点回顾(2.6-2.12)
    Dapr在Java中的实践 之 状态管理
    【复杂网络】网络科学导论学习笔记-第五章节点重要性与相似性
    简单理解prototype原型对象、原型链
    【刷题】代码随想录算法训练营第二十二天|235、二叉搜索树的最近公共祖先,701、二叉搜索树中的插入操作,450、删除二叉搜索树中的节点
    制造行业数字化运维破局之道
    Netty10-WebSocket长连接开发
    vue的生命周期及各个生命周期函数中适合做什么事
    基于现代深度学习的目标检测方法综述
    C++不同类型转换
  • 原文地址:https://blog.csdn.net/huahaiyi/article/details/132662599