给定一个句子,比如,I am a programmer
怎么翻转成 programmer a am I?
比较简单的算法是吧句子按照空格分开成一个字符串数组,
然后逆序再组合
Demo1
public static String doReverse(String str) { String [] mStrArray = str.split(" "); String resultString = ""; for(int i = 0;i<mStrArray.length;i++){ resultString = mStrArray[i]+" "+resultString; } return resultString; }
这是比较常规的方法,假设我们把条件要求苛刻些,
逆序算法要求不能开辟新的数组来存储,那么又该怎么做
Demo2
有如下思路
可以看到,句子中的不同单词是用空格分隔开的,那么,可不可以把空格
作为临时存储空间,然后来分别移动句子以达到效果呢。
算法如下:
public static char [] doReverse(char [] mCharSeq){ int pointer = 0; int length = mCharSeq.length; for (int i = 0;i<mCharSeq.length;i++){ if (mCharSeq[i]==' '){ pointer = i; //全体向空格方向移动一个单位,移动i+1次 int temp = i; int tempPointer = i; //移动句子头至句子末尾 while (i>=0){ for (int j = 0;j<length;j++){ if ((i+j+1)<length){ mCharSeq[i+j]= mCharSeq[i+j+1]; } else if (i+j+1==length){ mCharSeq[i+j] = mCharSeq[0]; } else { if(j==0){ return mCharSeq; } else { int index = i+j-(length); mCharSeq[index] = mCharSeq[index+1]; } } } i--; } // 调整最后单词的空格 for (int l = 0;l<temp;l++){ if (length-2-l<0){ return mCharSeq; } mCharSeq[length-1-l]= mCharSeq[length-2-l]; if (l==temp-1){ mCharSeq[length-2-l]=' '; } } length = length-(temp+1); } } return mCharSeq; }public static void main(String args[]){ String mString = "I am a programmer"; System.out.println(doReverse(mString.toCharArray())); }打印结果如下programmer a am I
这个算法的思路是,把整个句子看做一个闭合的环,当遇到空格后,把空格后的所有元素依次
向前移动一个单位,一轮下来后继续移动,一直到原来空格的位置移动到句子的末尾位置,
但是这个时候的要求和我们的句子反转的要求还有一些差别,于是我们把句子的末尾稍作调整,
使句子末尾的单词和前一个单词之间的空格呈现。
这样,一次移动就完成了。
在这样进行移动的过程中,把for循环的i沿着移动小循环置到0。
于是下一轮的寻找空格的过程开始之前,i又为零了。
但是,后面的第二第三一直到最后的移动,后面已经排好序的单词就不再参与移动了。
所以用变量length来记录尚未排序句子的元素长度。
最后,循环退出的条件为,当发现要移动的第一个元素就已经出了length的范围了,这个时候可以
认为,排列已经完成,于是退出for循环。
这种算法虽然能够达到不开辟新的数组内存的目的, 但是算法较为复杂,for循环中套了while,while中又套了for,
并且也不是那么浅显易懂。
那么,有没有既不开辟新的内存,又较为简单,容易理解的算法?
有!于是我们的手摇算法登场了。
Demo3何谓手摇算法所谓手摇算法,顾名思义,关键词在一个"摇"上,手摇算法还有一个名字:三重反转算法
那么,到底是怎么反转的,怎么摇的。
举个例子
假设我有序列
1234589101112
现在我把这个句子分成两个部分12345和89101112
我想把这个序列通过一个算法转换成:8910111212345
要怎么做?
一摇5432189101112
二摇5432121110198
三摇8910111212345
如果是整个句子呢?
I am a programmer
I ma a remmargorp
programmer a am I
为什么这样可以,其实可以这样想,假设把整个字符串看成一个个三三两两自由组合
的字符串的元素所组成的话,
那么整个字符串的反转其实包含了两个部分,
第一个部分是里面的元素的位置都反转了(不管元素是怎么组合的),
第二个部分是元素内部的序列也发生了反转
那么,如果我先把元素内部反转依次,那么整体反转之后,其实是把内部反转这部分给抵消了,
则只剩下元素外部所处位置的反转。
=================================
OK,现在我们尝试使用手摇算法来反转整个句子
public static char [] doReverse(char [] mCharSeq){ int prePoint = 0; int currentPoint ; //调整单词顺序 for (int i = 0;i<mCharSeq.length;i++){ if (mCharSeq[i]==' '||i==mCharSeq.length-1){ currentPoint = i; if (i==mCharSeq.length-1){ currentPoint = i+1; } int length = currentPoint-prePoint; for (int j = 0;j<length/2;j++){ int point1 = j+prePoint; int point2 = currentPoint-j-1; mCharSeq[point1] = (char)(mCharSeq[point1]^mCharSeq[point2]); mCharSeq[point2] = (char)(mCharSeq[point1]^mCharSeq[point2]); mCharSeq[point1] = (char)(mCharSeq[point1]^mCharSeq[point2]); } prePoint = currentPoint+1; } } //调整整体顺序 for (int i = 0;i<mCharSeq.length/2;i++){ int point1 = i; int point2 = mCharSeq.length-i-1; mCharSeq[point1] = (char)(mCharSeq[point1]^mCharSeq[point2]); mCharSeq[point2] = (char)(mCharSeq[point1]^mCharSeq[point2]); mCharSeq[point1] = (char)(mCharSeq[point1]^mCharSeq[point2]); } return mCharSeq; }public static void main(String args[]){ String mString = "I am a programmer"; System.out.println(doReverse(mString.toCharArray())); }打印结果如下programmer a am I其中,第一个for循环是在翻转句子中的单个单词,第二个循环是翻转整个句子可以看到,使用手摇算法之后,整个代码比Demo2容易理解多了,并且循环只嵌套了一层,没有出现Demo2三层循环嵌套的情况。推荐使用Demo3的算法。