句子反转与手摇算法

给定一个句子,比如,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的算法。


 
 
 
 
 
 
 
 
 
 
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值