算法日记day 10(反转字符串)

一、反转字符串

题目1:

编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。

不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。

示例 1:

输入:s = ["h","e","l","l","o"]
输出:["o","l","l","e","h"]

示例 2:

输入:s = ["H","a","n","n","a","h"]
输出:["h","a","n","n","a","H"]

思路:

采用双指针的方法,一头指针一尾指针,让两指针分别交换位置

代码:

public void reverseString(char[] s) {
    int left = 0;
    int right = s.length - 1;
    while (left < right) {   //判断是否可以交换的条件
        char temp = s[left];  //交换操作
        s[left] = s[right];
        s[right] = temp;
        left++;   //左指针右移到下一元素
        right--;  //右指针左移到上一元素
    }
}

 

二、反转字符串2

题目:

给定一个字符串 s 和一个整数 k,从字符串开头算起,每计数至 2k 个字符,就反转这 2k 字符中的前 k 个字符。

  • 如果剩余字符少于 k 个,则将剩余字符全部反转。
  • 如果剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符,其余字符保持原样。

示例 1:

输入:s = "abcdefg", k = 2
输出:"bacdfeg"

示例 2:

输入:s = "abcd", k = 2
输出:"bacd"

大致意思就是每隔2k个字符串时就需要将前k个字符进行反转操作 ,如果最后不够2k个字符了,但是够k个字符,也需要进行反转

思路:

难点在于如何将每一段的字符串单独的区分开来,其实只需要让for循环中的遍历条件稍加修改,使得增加的值每次都是按2k个来增加,再对里面的值进行判断

代码:

首先给出交换的方法

public void reverse(char[] ch, int i, int j) {
    for (; i < j; i++, j--) {
        char temp = ch[i];
        ch[i] = ch[j];
        ch[j] = temp;
    }
}

反转字符串代码

public String reverseStr(String s, int k) {
    char[] ch = s.toCharArray();    //将输入的字符串 s 转换为字符数组 ch,这样可以方便地对字符串中的每个字符进行操作。
    for (int i = 0; i < ch.length; i = i + 2 * k) { //这个循环每次迭代增加 2k,即每次跳过 2k 个字符。i 是当前要反转子串的起始位置。
        if (i + k <= ch.length) {
            reverse(ch, i, i + k - 1);    //如果 i + k 小于等于字符数组 ch 的长度,说明从 i 开始的 k 长度的子串可以完整反转。调用 reverse(ch, i, i + k - 1) 实现反转。
            continue;
        }
        reverse(ch, i, ch.length - 1);   //如果 i + k 超过了数组长度,说明剩余的字符不足 k 个,直接反转从 i 到数组末尾的部分,即调用 reverse(ch, i, ch.length - 1)。
    }
    return new String(ch);  //将修改后的字符数组 ch 转换回字符串,并返回作为函数的结果。
}

 toCharArray()方法介绍

用途:将字符串转换为字符数组通常用于需要按字符访问和操作字符串内容的场景。相比直接使用字符串,字符数组更加灵活,可以通过索引直接访问每个字符,也可以方便地进行各种字符操作和算法实现。

示例:假设有字符串 s = "Hello",执行 char[] ch = s.toCharArray(); 后,ch 的内容将是 ['H', 'e', 'l', 'l', 'o']。现在可以通过 ch 数组的索引来访问和修改字符串中的每个字符,比如 ch[0] 是 'H'ch[1] 是 'e',以此类推。

总结来说,char[] ch = s.toCharArray(); 这行代码的作用是将字符串 s 转换为一个字符数组 ch,方便对字符串进行字符级别的操作和处理。

三、反转字符串中的单词

题目:

给你一个字符串 s ,请你反转字符串中 单词 的顺序。

单词 是由非空格字符组成的字符串。s 中使用至少一个空格将字符串中的 单词 分隔开。

返回 单词 顺序颠倒且 单词 之间用单个空格连接的结果字符串。

注意:输入字符串 s中可能会存在前导空格、尾随空格或者单词间的多个空格。返回的结果字符串中,单词间应当仅用单个空格分隔,且不包含任何额外的空格。

示例 1:

输入:s = "the sky is blue"
输出:"blue is sky the"

示例 2:

输入:s = "  hello world  "
输出:"world hello"
解释:反转后的字符串中不能存在前导空格和尾随空格。

示例 3:

输入:s = "a good   example"
输出:"example good a"
解释:如果两个单词间有多余的空格,反转后的字符串需要将单词间的空格减少到仅有一个。

 思路:

重难点在于如何去处理空格的问题,既要保证单词的反转,也要保证每个单词之间只能保留有一个空格,其余的空格均需删除,因此需要有专门的删除空格的方法,采用双指针的方法去删除多余的空格,专门的反转单词的方法,需要先将整个字符串s整体反转,再分别对各个单词进行反转,因此需要完成两个反转方法

以代码为例具体说明:

reverseString方法

用于反转 StringBuilder 对象 sb 中从索引 start 到 end 的字符序列

public void reverseString(StringBuilder sb, int start, int end) {
    while (start < end) {
        // 1. 交换字符串中索引为 start 和 end 的字符
        char temp = sb.charAt(start); // 保存起始位置的字符
        sb.setCharAt(start, sb.charAt(end)); // 将末尾位置的字符放到起始位置
        sb.setCharAt(end, temp); // 将保存的起始位置的字符放到末尾位置

        // 2. 更新索引,继续向中间移动
        start++;
        end--;
    }
}
  1. 参数说明

    • StringBuilder sb:表示要操作的可变字符序列对象。
    • int start:表示要反转的起始位置的索引。
    • int end:表示要反转的结束位置的索引。
  2. 功能说明

    • while (start < end):使用 while 循环来判断是否还有需要交换的字符对。这个条件确保在反转过程中,每个字符只被交换一次,从而实现反转效果。
  3. 反转过程

    • char temp = sb.charAt(start); 首先将起始位置 start 处的字符保存到临时变量 temp 中,以便后续使用。 
    • sb.setCharAt(start, sb.charAt(end)); 将末尾位置 end 处的字符放到起始位置 start 处,实现了字符的交换。
    • sb.setCharAt(end, temp); 将之前保存在 temp 中的起始位置字符放到末尾位置 end 处,完成字符的交换过程。
  4. 索引更新

    • start++:向后移动 start 索引,以便下一次循环处理下一个字符。
    • end--:向前移动 end 索引,以便下一次循环处理前一个字符。
  5. 循环结束

    • 当 start 不再小于 end 时,循环结束,此时字符序列从 start 到 end 范围内的字符已经完成了反转操作。

removeSpace方法 

该方法主要实现删除开头和结尾的空格,以及两个单词之间多余的空格,且只保留一个空格

public StringBuilder removeSpace(String s) {
    // 初始化起始和结束索引,去除开头和结尾的空格
    int start = 0;
    int end = s.length() - 1;
    
    // 去除开头的空格
    while (s.charAt(start) == ' ')
        start++;
    
    // 去除结尾的空格
    while (s.charAt(end) == ' ')
        end--;
    
    // 创建一个 StringBuilder 对象来存储处理后的字符串
    StringBuilder sb = new StringBuilder();
    
    // 遍历去除空格后的字符序列
    while (start <= end) {
        char c = s.charAt(start);
        
        // 如果当前字符不是空格,或者是第一个字符,或者前一个字符不是空格,则添加到 StringBuilder 中
        if (c != ' ' || sb.length() == 0 || sb.charAt(sb.length() - 1) != ' ') {
            sb.append(c);
        }
        
        // 移动到下一个字符
        start++;
    }
    
    // 返回处理后的字符串
    return sb;
}
  1. 去除开头和结尾的空格

    • int start = 0; 和 int end = s.length() - 1; 初始化字符串的起始和结束索引。
    • while (s.charAt(start) == ' ') start++; 循环直到找到第一个不是空格的字符,确定新的起始索引。
    • while (s.charAt(end) == ' ') end--; 循环直到找到第一个不是空格的字符,确定新的结束索引。
  2. 创建 StringBuilder 对象

    • StringBuilder sb = new StringBuilder(); 创建一个用于构建最终结果的 StringBuilder 对象。
  3. 遍历字符序列

    • while (start <= end) 通过循环遍历从去除空格后的起始到结束索引的字符。
  4. 条件判断和添加字符

    • char c = s.charAt(start); 获取当前位置的字符。
    • if (c != ' ' || sb.length() == 0 || sb.charAt(sb.length() - 1) != ' ')  如果当前字符不是空格,或者是第一个字符,或者前一个字符不是空格,则将该字符添加到 StringBuilder 中。
    • sb.append(c)  将字符 c 添加到 StringBuilder 中。
  5. 移动索引

    • start++; 将索引向后移动,处理下一个字符。
  6. 返回结果

    • return sb; 返回处理后的字符串结果。

reverseEachWord方法 

用于反转字符串中每个单词的字符顺序,而不改变单词之间的顺序

private void reverseEachWord(StringBuilder sb) {
    int start = 0;  // 初始化单词的起始索引
    int end = 1;    // 初始化单词的结束索引,起始为第一个字符的下一个位置
    int n = sb.length();  // 获取字符串的长度

    while (start < n) {  // 外层循环,遍历整个字符串
        // 内层循环,找到当前单词的结束位置,即遇到空格或者到达字符串末尾
        while (end < n && sb.charAt(end) != ' ') {
            end++;
        }
        
        // 调用 reverseString 方法来反转当前单词的字符顺序
        reverseString(sb, start, end - 1);
        
        // 更新 start 和 end,准备处理下一个单词
        start = end + 1;  // 下一个单词的起始位置是当前单词的下一个字符
        end = start + 1;  // 下一个单词的结束位置初始化为起始位置的下一个字符
    }
}
  1. 变量初始化

    • int start = 0; 初始化单词的起始索引,开始从字符串的开头。
    • int end = 1; 初始化单词的结束索引,起始为第一个字符的下一个位置,以便从第一个单词的第二个字符开始。
    • int n = sb.length(); 获取字符串 sb 的长度,用于循环控制。
  2. 外层循环 (while (start < n)):

    • 这个循环用于遍历整个字符串,直到处理完所有单词。
  3. 内层循环 (while (end < n && sb.charAt(end) != ' ')):

    • 内部循环用于找到当前单词的结束位置。它会持续向后移动 end 索引,直到遇到空格或者到达字符串的末尾位置。
  4. 反转单词

    • 一旦找到了当前单词的结束位置,调用 reverseString(sb, start, end - 1); 方法来反转从 start 到 end-1 之间的字符顺序。这个方法是一个辅助方法,通过交换字符来实现反转。
  5. 更新索引

    • start = end + 1; 更新 start 索引,以便指向下一个单词的起始位置。
    • end = start + 1; 更新 end 索引,以便从下一个单词的第二个字符开始检查。
  6. 循环继续

    • 外层循环继续,直到 start < n 不再成立,即处理完整个字符串中的所有单词。

reverseWords方法

用于反转字符串中每个单词的字符顺序,同时保持单词之间的顺序不变

public String reverseWords(String s) {
    StringBuilder sb = removeSpace(s);  // 去除字符串中多余的空格并转换为 StringBuilder
    reverseString(sb, 0, sb.length() - 1);  // 反转整个字符串的字符顺序
    reverseEachWord(sb);  // 反转每个单词的字符顺序
    return sb.toString();  // 将 StringBuilder 转换为 String 并返回
}
  1. 方法签名

    • public String reverseWords(String s) { ... }
    • 这个方法接收一个字符串 s,并返回一个经过特定处理后的字符串。
  2. 去除空格并构建 StringBuilder

    • StringBuilder sb = removeSpace(s);
    • removeSpace 方法的作用是去除字符串 s 中多余的空格,并将处理后的结果转换为 StringBuilder 对象。这一步确保了后续操作的便利性,避免了在字符串操作过程中频繁创建字符串对象。
  3. 反转整个字符串

    • reverseString(sb, 0, sb.length() - 1);
    • reverseString 方法用于反转 StringBuilder sb 中从索引 0 到 sb.length() - 1 的字符顺序。这一步将整个字符串的字符顺序颠倒过来,使得原来在末尾的字符移动到了开头。
  4. 反转每个单词

    • reverseEachWord(sb);
    • reverseEachWord 方法用于反转 StringBuilder sb 中每个单词的字符顺序,但保持单词之间的顺序不变。这个方法已经在之前的解释中详细说明过,它会遍历 sb 中的每个单词,并将单词内部的字符顺序反转。
  5. 转换为 String 并返回

    • return sb.toString();
    • 最后,将经过处理的 StringBuilder sb 转换为一个普通的字符串,并作为方法的返回值。

具体案例:

以  ("  hello     world  ") 为例,其变化流程为

1、调用reverseSpace方法去除多余的空格  "hello world"

2、调用reverseString方法反转整个字符串  "dlrow olleh"

3、调用reverseEachWord方法反转单个单词    "world hello" 

反转字符串中有很多细节上的问题,从整个字符串反转,再到分别划分区域的反转,这不仅仅需要对代码基本功足够扎实,同时还需要有较为缜密的逻辑,多花点时间去做去想,日积月累,一定可以有所收获!!!

今天的学习就到这里了 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值