高频算法面试题——翻转字符串(字符串Java版)

195 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第8天,点击查看活动详情

一、【题目:翻转字符串】

给定一英文句子,在单词间做逆序调整,单词内的字符不变。

【举例】

eg1. you love code. ——> code. love you

【思路】

  • (1) 整体逆序:you love code. ——> .edoc evol uoy

  • (2) 遍历锁定每个单词 :以空格分隔单词,llrr,分别为记录每个单词位置的左右游标,其中(下面的赋值请画图理解)
    l={起始位置0遍历过程中左侧为空格的位置l=\left\{ \begin{array}{lcl} 起始位置0\\ 或\,遍历过程中左侧为空格的位置 \end{array}\right.

    r={句子末尾位置遍历过程中右侧为空格的位置r=\left\{ \begin{array}{lcl} 句子末尾位置\\或\,遍历过程中右侧为空格的位置 \end{array}\right.

  • (3) 每个单词逆序:.edoc evol uoy ——> code. love you
    其中,逆序函数,reverse 的思路:
    首尾互换,次首尾互换...所以,该函数的参数中需要增加左右游标,begin,end,记录当前互换的位置信息。

【代码】

//逆转字符串
public void reverse( char[] chas, int begin, int end )
{
    char temp = 0;//char型变量0表示空字符
    while( begin < end )
    {
        //字符互换
        temp = chas[begin];
        chas[begin] = chas[end];
        chas[end] = temp;
        //移动左右游标
        begin++;
        end--;
    }
}
//逆转句子,单词间逆序
public void rotateSentence( char[] chas )
{
    if( chas == null || chas.length == 0 )
    {
        return;
    }
    reverse( chas, 0, chas.length-1 );
    int l = -1;
    int r = -1;
    for( int i = 0; i < chas.length; i++ )
    {
        //尚未遇到空格,则确定当前单词的左右游标
        if( chas[i] != ' ' )
        {
            l = ( ( i == 0 ) || ( chas[i-1] == ' ' ) ? i : l );
            r = ( ( i == chas.length-1 ) || ( chas[i+1] == ' ') ? i : r );
        }
        //当前位置字符为空格
        //如果有单词,将之前找到的单词逆置
        if( l != -1 && r != -1 )
        {
            reverse( chas, l, r );
            l = -1;
            r = -1;
        }
    }
}

【时间复杂度】

O(n)

【空间复杂度】

额外的空间复杂度为O(1)

【检查】

考虑代码中是否有越界情况 考虑输入为空的情况,针对字符串、字符数组,特殊考虑null和空字符串""的情况,具体说明见下面的代码,所以才会有上面的判断代码: chas == null || chas.length == 0。

char[] chas1 = null;//null不能判断长度,chas1.length会抛出NullPointerException异常
String str = "";
char[] chas2 = str.toCharArray();//""空字符的字符数组长度为0
System.out.println(chas1==null?1:2);
System.out.println(chas2.length);

输出结果分别为:1 和 0

【测试用例】

功能测试:多个字符句子,单个单词的句子
特殊字符测试:null,空字符"",只有一个空格的字符" "

二、【变形一:左旋转字符串】:

给定一个整数n,将大小为n的左半边字符转移到字符串的尾部。

【举例】

eg1. 字符串:you love code. 整数:2 ——> u love code.yo

【思路】

一个比较巧妙的方法
(1) 将原字符串按照整数值,分为左右两部分: yo和u love code.
(2) 分别将左右两部分字符串逆序操作:oy 和.edoc evol u
(3) 再对整体字符串进行逆序操作:u love code.yo 其中,逆序操作reverse函数同上一道题。

【代码】

//左旋字符串
public void rotate1( char[] chas, int n )
{
    if( chas == null || n <= 0 || n >= chas.length )
    {
        return;
    }
    reverse( chas, 0, n-1 );
    reverse( chas, n,chas.length-1 );
    reverse( chas, 0, chas.length-1 );
}

【时间复杂度】

O(n)

【空间复杂度】

额外的空间复杂度为O(1)

【检查】

考虑代码中是否有越界情况
考虑输入为空的情况

【测试用例】

功能测试:将字符串左旋0个,1个,n-1个,n个,n+1个字符 特殊字符测试:null,空字符""

三、【变形二】:

翻转中间由各种符号隔开的字符串。

【举例】

eg1. 字符串:you;love;code; 分隔符:; ——> uoy;evol;edoc;

【思路】

(一) 思路一:与上面例题思路基本相同

(1) 遍历锁定每个单词 :以指定分隔符分隔单词,llrr,分别为记录每个单词位置的左右游标
(2) 对每个单词进行逆序操作

(二) 思路二:采用Java内特有的处理字符串的函数

(1) 指定分隔符为delim:StringTokenizer( String str, String delim )
(2) 判断字符串中是否还有分隔符:boolean hasMoreToken( String delim)
(3) 只要还有分隔符,就取从当前位置到下一个分隔符之前的字符串:String nextToken( String delim )
(4) 逆置上一步取得的字符串,输出,同时输出分隔符
(5) 重复(3)(4)的操作,直至没有分隔符

【代码】

(一)

//逆转句子,单词内逆序
public void rotateSentence2( char[] chas, char delim )
{
    if( chas == null || chas.length == 0 )
    {
        return;
    }
    int l = -1;
    int r = -1;
    for( int i = 0; i < chas.length; i++ )
    {
        //尚未遇到分隔符delim,则确定当前单词的左右游标
        if( chas[i] != delim )
        {
            l = ( ( i == 0 ) || ( chas[i-1] == delim ) ? i : l );
            r = ( ( i == chas.length-1 ) || ( chas[i+1] == delim) ? i : r );
        }
        //当前位置字符为delim
        //如果有单词,将之前找到的单词逆置
        else
        {
            reverse(chas, l, r);
        }
    }
}

(二)

import java.util.Scanner;
import java.util.StringTokenizer;

public class Rotate
{
    public static void main( String[] args )
    {
        Scanner sc = new Scanner( System.in );
        System.out.println("请输入字符串:");
        String str = sc.nextLine();
        StringTokenizer st = new StringTokenizer( str, ";");
        while( st.hasMoreTokens() )
        {
            String streverse = new StringBuffer( st.nextToken() ).reverse().toString();
            System.out.print( streverse );
            System.out.print( ";" );
        }
    }
}

思路(二),给出了完整的可运行代码,大家可以自行体会一下字符串的读取操作Scanner的用法;理解一下String,StingBuffer的区别,String类是字符串常量,StringBuffer是字符串变量,可扩充,可修改,而Java中自带的reverse函数是将一个输入流倒序输出,不可以用于字符串上。变形二的其他细节本文就不再赘述了。

(文中题源摘自剑指offer,程序员代码面试指南,牛客网面经,思路均为作者原创总结,文中如有错误,烦请各位读者指出,让我们共同进步,一起成长!)