还记得之前面试的时候,面试官问过我一个题,就是将一段英文单词逆序输出,类似这样:
hello my name is Jack
输出:
Jack is name my hello
看了一下网上的实现方式,基本都是一致的,取出单词然后进行重新拼接,但我当时却不是这样实现的,当时面试官要求不能生成新的空间(当然也不是绝对了),意思应该就是不能使用取单词后拼接的方式,然后我用了这种方式来实现的,下面来分享一下:(思路重要,代码次要)

思路:

  1. 首先将string转换为字符数组。
  2. 再将字符数组头尾互换,将整个句子翻转,类似变成这样:kcaJ si eman ym olleh
  3. 然后循环遍历获取每个单词的头尾索引值,并嵌套一个循环进行单词翻转,类似这样:kcaJ -> Jack
    思路其实蛮简单的,然后面试官说如果中间有很多空格如何处理呢? 我说没关系,可以过滤的,再来看一下代码实现吧:
public class Test {
    public static void main(String[] args) {
        String test = "hello my name is Jack";
        char[] testChars = test.toCharArray();
        System.out.println("old is:" + test);
        final int len = testChars.length;
        for (int i = 0; i < len / 2; i++) {
            //句子头尾互换,进行len/2次交互
            testChars[len - 1 - i] = (char) (testChars[len - 1 - i] ^ testChars[i]);
            testChars[i] = (char) (testChars[len - 1 - i] ^ testChars[i]);
            testChars[len - 1 - i] = (char) (testChars[len - 1 - i] ^ testChars[i]);
        }
        // 交换后的句子:
        System.out.println("tmp is:" + new String(testChars));

        int start = -1;//每个单词的起始索引
        int end = -1;//每个单词的结束索引
        for (int i = 0; i < len; i++) {
            if (start == -1 && testChars[i] != ' ') {//记住第一个单词的起始索引值
                start = i;
            }
            //当前索引值为空时,单词结束索引为上一个索引值,这里不用考虑上一个是否还是空格
            if (testChars[i] == ' ') {
                end = i - 1;
            } else if (i == len - 1) {//如果是最后一个索引值时,单词结束索引就是结尾字符
                end = len - 1;
            }
            // 判断单词结束索引值符合要求,并且对应的字符不是空字符,则进行单词头尾交换
            if (end >= 0 && testChars[end] != ' ') {
                System.out.println("start:" + start + " end:" + end);
                int wordLen = end - start;
                // 单词头尾互换
                for (int j = 0; j <= wordLen / 2; j++) {
                    int first = start + j;
                    int last = start + wordLen - j;
                    if (testChars[first] == testChars[last]) {
                        //如果字符相同不比交换
                        continue;
                    }
                    testChars[first] = (char) (testChars[first] ^ testChars[last]);
                    testChars[last] = (char) (testChars[first] ^ testChars[last]);
                    testChars[first] = (char) (testChars[first] ^ testChars[last]);
                }
                start = -1;
                end = -1;
            }
        }
        System.out.println("end is:" + new String(testChars));
    }
}

输出结果:

old is:hello my name is Jack
tmp is:kcaJ si eman ym olleh
start:0 end:3
start:5 end:6
start:8 end:11
start:13 end:14
start:16 end:20
end is:Jack is name my hello

很好,完美的解决了此问题~~


不知大家注意上面的^操作没有,这是采用异或的方式进行交换数值,不需要借助临时变量tmp,下面来看一下原理:
现有A、B两个值需要交互,我们可以这样写:

A = A ^ B;
B = A ^ B;
A = A ^ B;

右侧很神奇的一致,为什么就可以成功进行交换呢?这里需要用离散的异或运算就知道了,下面分布解析:
a. A = A ^ B 这个就先记住就行
b. B = A ^ B 将上一步代入可以得到:B = (A ^ B) ^ B
进一步改变: B = A ^ (B ^ B)
大家直到,异或运算,自己异或自己为0,即 A^A = 0
而任何值异或0等于其本身,即 A^0 = A
所以上面的结果就是:
B = A ^ 0 = A
这样就顺利的将A的值赋值给B,一次进行替代,第三步将B的值赋值给了A,完成交换了就~