已经有很多书籍和博客讲过这两个问题了,也有很多的解法,但是有的解法不够好,或者写的不太清楚,因此我再重新把这两个问题解释下,并用Java代码实现。
之所以将这两个问题放在一起去讨论,一方面是因为这两个问题都需要去反转字符串,另一方面是在反转完字符串后,各自的处理方法不太一样,是两个比较常见的面试笔试题目。
翻转单词顺序(曾经的微软面试题目):
问题描述:给定一个英文句子,将这个句子中每个单词作为整体进行反转,但是单词内字符顺序不变。
例如:输入字符串“I am a student.”,输出"student. a am I"。
问题分析:这个题目是一个比较经典的字符串处理题目,很多公司面试时都会考类似的题目。其实这个问题比较简单,编程前需要先分析清楚,再去写程序。两步就可以完成:
第一步:可以先反转句子中所有的字符。样例中"I am a student." 经过第一步后变成".tneduts a ma I",可以定义一个方法或函数,专门用来反转字符串。
第二步:再反转每个单词中字符的顺序。".tneduts a ma I" 经过第二步变成“student. a am I”,这一步中可以把空格作为单词之间的分隔符,但是要特别注意最后一个单词的处理,
因为最后一个单词没有空格结尾,可以将最后一个单词特别处理。
下面使用Java代码实现,方法reverse()专门用来反转字符串,方法fanzhuan()实现了第二步的功能。
左旋转字符串:
问题描述:给定一个字符串,要求把字符串前面的若干个字符移动到字符串的尾部。要求时间复杂度为 O(n),空间复杂度为 O(1)。
例如:输入字符串“abcdef”,把前面的2个字符'a'和'b'移动到字符串的尾部,使得原字符串变成字符串“cdefab”。
问题分析:使用暴力法可以实现要求的功能,但是暴力法效率比较低,可以满足空间复杂度O(1),但是不满足题目中要求的时间复杂度O(n),因此需要寻找新的解法。
这里提供了一种方法,叫做三步反转法。具体解释如下:
将一个字符串分成X和Y两个部分,在每部分字符串上定义反转操作,如X^T,即把X的所有字符反转(如,X="abc",那么X^T="cba"),那么可以得到这个结论:(X^T Y^T)^T=YX,这样就解决了字符串的反转问题。
例如,字符串 abcdef ,若要让abc反转到def的后面,只要三步即可:
第一步:首先将原字符串分为两个部分,即X:abc,Y:def;
第二步:将X反转,X->X^T,即得:abc->cba;将Y反转,Y->Y^T,即得:def->fed。
第三步:反转上述步骤得到的结果字符串X^TY^T,即反转字符串cbafed的两部分(cba和fed)给予反转,cbafed得到defabc,形式化表示为(X^TY^T)^T=YX,这就实现了整个反转。
下面的Java代码中,方法zuofanzhuan()实现了这个操作,当然也需要反转每个每个部分的方法reverse()。
注意:Java语言中字符串包已经提供了很多方法可以直接实现反转操作,程序员直接调用即可,但是本文所讲的是更通用的方法,在任何编程语言中都可以使用的方法,并且讲出了问题的实质,提供了一种处理字符串的思想,
这也是面试和笔试考官希望看到的答案,因此,这里的方法需要牢牢掌握,方便以后的字符串编程。
下面是具体的Java代码实现,读者可以很容易的改写成其他语言实现:
个
import java.util.*; class Test { public static String reverse(String str){ //将一个字符串进行反转 StringBuilder strb=new StringBuilder(str); //将其转换为可变字符串 int low=0,high=strb.length()-1; while(low<high){ //将左边位和右边位依次进行交换 char ch=strb.charAt(low); strb.setCharAt(low,strb.charAt(high)); strb.setCharAt(high,ch); low++; high--; } return strb.toString(); //将可变字符串变成不可变字符串,并返回 } public static String fanzhuan(String str){ //翻转每个单词的顺序 str=Test.reverse(str); //先整体反转 String strfinal=new String(""); String str1=new String(""); for(int i=0;i<str.length();i++) //再反转每个单词的顺序 if(str.charAt(i)==' ') { strfinal=strfinal+Test.reverse(str1)+str.charAt(i); str1=""; } else { str1+=str.charAt(i); if(i==str.length()-1) //对最后一个单词进行处理,因为最后一个单词没有空格了 strfinal=strfinal+Test.reverse(str1); } return strfinal; } public static String zuoxuanzhuan(String str,int k){ int n=str.length(); if(n==0 || k<=0|| k>n ) //如果字符串长度为0,或者k 不在字符串长度的范围内,则返回原来的字符串 return str; String str1=new String(""); //存储第一部分,前k个字符 String str2=new String(""); //存储第二部分,后面n-k个字符 for(int i=0;i<k;i++) str1+=str.charAt(i); for(int i=k;i<n;i++) str2+=str.charAt(i); return reverse(reverse(str1)+reverse(str2)); //采用三步反转法 } } public class Main { public static void main(String[] args) { String str=new String("I am a student."); System.out.println("翻转句子前为:"+str); System.out.println("翻转句子后为:"+Test.fanzhuan(str)); int k=3; str="abcdef"; System.out.println("左旋转字符串前为:"+str); System.out.println("左旋转字符个数为:"+k); System.out.println("左旋转字符串后为:"+Test.zuoxuanzhuan(str,k)); } }
输出结果为:
翻转句子前为:I am a student.
翻转句子后为:student. a am I
左旋转字符串前为:abcdef
左旋转字符个数为:3
左旋转字符串后为:defabc