反转字符串

去哪儿面试的最后一道题,开始我写了全数遍历的实现代码,然后面试官要求使用递归来实现,但是我一个算法战五渣早就忘记递归是个什么鬼了,然后面试就GG了。
对于这道题目的解答方法有多种,先把它们全部列出来吧:

  • 全数遍历;
  • 半数遍历;
  • 递归实现;
  • 使用栈来实现;

全数遍历(我的答案)

刚开始,我写的是最近的从尾到头的遍历的形式:

public class StringReverseExample {

    public static void main(String[] args) {
        String str = "abcdefg";
        StringBuilder sb = new StringBuilder(str.length());
        for (int i = str.length() - 1; i >= 0; i--) {
            sb.append(str.charAt(i));
        }
        System.out.println(sb.toString());
    }
}

这个毫无技术含量,当过一天程序员的人应该都是可以写出来的。。。然后面试官就说用递归写一下吧,然后我思考了三分钟之后就交白卷投降了。。。那么,用递归究竟怎么实现呢?以下内容来自网络,我只是做了下个人整理。

递归实现

递归的思想就是把反转长度为 n 的字符串 str 的任务划分为反转长度为 n - 1 的字符串 subStr1(其中 subStr1 = str.substring(1);),然后再加上有 str 第一个字符构成的字符串 subStr2(即 subStr2 = str.charAt(0);),具体实现如下:

public class StringReverseRecursive {

    public static void main(String[] args) {
        String str = "abcdefg";
        String reverseStr = recursiveReverse(str);
        System.out.println(reverseStr);
    }

    public static String recursiveReverse(String str) {
        if (str.length() <= 1) {
            return str;
        }
        return recursiveReverse(str.substring(1)) + str.charAt(0);
    }
}

那么,使用递归这种算法究竟有什么好处呢?这个问题我还尚未想到,网上搜索的话,基本上都是说算法的实现,然而并没有进行时间复杂度以及空间复杂度的分析。我个人也没想出递归算法跟上面的从尾到头遍历相比有啥好处。。。

半数遍历

半数遍历-JDK类库的实现

JDK 类库里面 StringBuilder 类有一个 reverse 方法,里面有反转的实现,其实也就是类似于半数遍历而已,下面直接贴代码:

public class StringReverseByInBuild {

    public static void main(String[] args) {
        String str = "abcdefg";
        StringBuilder sb = new StringBuilder(str);
        sb.reverse();
        String destStr = sb.toString();
        System.out.println(destStr);
    }
}

然后是 StringBuilder.reverse 的内部实现:

public AbstractStringBuilder reverse() {
        boolean hasSurrogates = false;
        int n = count - 1;
        for (int j = (n-1) >> 1; j >= 0; j--) {
            int k = n - j;
            char cj = value[j];
            char ck = value[k];
            value[j] = ck;
            value[k] = cj;
            if (Character.isSurrogate(cj) ||
                Character.isSurrogate(ck)) {
                hasSurrogates = true;
            }
        }
        if (hasSurrogates) {
            reverseAllValidSurrogatePairs();
        }
        return this;
    }

半数遍历-直白的实现

这种遍历算法也是容易理解,下面直接贴实现代码:

public class StringHalfTraverseReverseExample {

    public static void main(String[] args) {
        String str = "abcdefgh";
        char[] chars = new char[str.length()];
        for (int i = 0; i < Math.ceil(str.length() / 2f); i++) {
            chars[i] = str.charAt(str.length() -1 - i);
            chars[str.length() - 1 - i] = str.charAt(i);
        }
        String destStr = new String(chars);
        System.out.println(destStr);
    }
}

半数遍历-使用异或

这种方法就真的是涨姿势了,数学没学好玩算法就是蛋疼啊,没办法只能以后多补补了。下面先说说异或的基本性质:

1. 交换律:A^B = B^A
2. 结合律:A^(B^C) = (A^B)^C
3. 恒等率:A^0 = A
4. 归零率:A^A = 0
5. 自反:A^B^B = A^0 = A

然后是代码实现:

public class StringReverseByXor {

    public static void main(String[] args) {
        String str = "abcdefgh";
        char[] chars = str.toCharArray();
        int halfCeil =  (int) Math.floor(str.length() / 2f);
        for (int i = 0, j = str.length() - 1; i < halfCeil; i++, j--) {
            chars[i] ^= chars[j];
            chars[j] ^= chars[i];
            chars[i] ^= chars[j];
        }
        String destStr = new String(chars);
        System.out.println(destStr);
    }
}

半数遍历-使用指针

Java 里面并没有指针,不过使用指针的思路就是:进行半数遍历,然后交换对称位置上的值,当然了交换过程需要一个临时变量,在 Java 中不用指针的实现大概如下:

public class StringReverseByExchange {

    public static void main(String[] args) {
        String str = "abcdefg";
        char[] chars = str.toCharArray();
        for (int i = 0; i < Math.ceil(chars.length / 2f); i++) {
            char temp = chars[i];
            chars[i] = chars[chars.length - 1 - i];
            chars[chars.length - 1 - i] = temp;
        }
        String destStr = new String(chars);
        System.out.println(destStr);
    }
}

使用栈

栈这种数据接口有先进后出的特性,所以可以用它来轻松的实现字符串的反转,不过这么使用栈真的好吗?只是徒增了空间复杂度和时间复杂度吧!

public class StringReverseByStack {

    public static void main(String[] args) {
        String str = "abcdefg";
        Stack<Character> stack = new Stack<>();
        for (int i = 0; i < str.length(); i++) {
            stack.push(str.charAt(i));
        }
        char[] chars = new char[str.length()];
        for (int i = 0; i < str.length(); i++) {
            chars[i] = stack.pop();
        }
        String destStr = new String(chars);
        System.out.println(destStr);
    }
}

参考

  1. http://blog.sina.com.cn/s/blog_6997f0150100tpse.html
  2. https://www.cnblogs.com/JohnTsai/p/5606719.html
原文地址:https://www.cnblogs.com/optor/p/8758816.html