算法通关村-----字符串反转问题解析

反转字符串

问题描述

编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。详见leetcode344

问题分析

输入的是字符数组,不能引入额外的空间,则我们可以直接操作字符数组,使用双指针方式进行遍历交换数组元素,达到字符串反转的效果。

代码实现

public void reverseString(char[] s) {
    int n = s.length;
    int left = 0; 
    int right = n - 1;
    while(left<right){
        char temp = s[right];
        s[right] = s[left];
        s[left] = temp;
        left++;
        right--; 
    }
}

反转字符串 II

问题描述

给定一个字符串 s 和一个整数 k,从字符串开头算起,每计数至 2k 个字符,就反转这 2k 字符中的前 k 个字符。如果剩余字符少于 k 个,则将剩余字符全部反转。如果剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符,其余字符保持原样。详见leetcode541

问题分析

假设字符串的长度为n,题目中的要求,n2k时,反转前k个字符,迭代变量每次增加2k。通过分析,我们可以通过循环+双指针的方式来完成,当题目中给出的输入是字符串类型,而不是字符数组时,我们可以通过toCharArray方法将字符串转化成字符数组。

代码实现

public String reverseStr(String s, int k) {
    int n = s.length();
    int count = 0;
    while(n >= k){
        int left = count * k; 
        int right = (count+1) * k - 1;
        s = reverseString(s,left,right);
        count+=2;
        n = n-2*k;
    }
    if(n>0&&n<k){
        int len = s.length();
        s = reverseString(s,len-1-(n-1),len-1);
    }
    return s;
}
public String reverseString(String str, int left, int right) {
    char[] s = str.toCharArray();
    while(left<right){
        char temp = s[right];
        s[right] = s[left];
        s[left] = temp;
        left++;
        right--; 
    }
    return new String(s);
}

仅反转字母

题目描述

给你一个字符串 s ,根据下述规则反转字符串:

所有非英文字母保留在原有位置。

所有英文字母(小写或大写)位置反转。

返回反转后的 s 。

详见leetcode917

问题分析

同样的,我们可以将字符串转化为字符数组,通过双指针遍历的方式,因为题目中要求只反转字母,所以,在left指针和right指针遍历过程中,只有当前指向为字母时才进行交换。

代码实现

public String reverseOnlyLetters(String s) {
    int left = 0;
    int right = s.length()-1;
    char[] chars = s.toCharArray();
    while(left<right){
        while(!isLetter(chars[left])){
            left++;
            if(left>right){
                return new String(chars);
            }
        }
        while(!isLetter(chars[right])){
            right--;
            if(right<left){
                return new String(chars);
            }
        }
        if(left<right){
            char temp = chars[left];
            chars[left] = chars[right];
            chars[right] = temp;
            left++;
            right--;
        }
    }
    return new String(chars);
}

public boolean isLetter(char c){
    boolean lower = c>='a' && c<='z';
    boolean upper = c>='A' && c<='Z';
    return lower || upper;
}

反转字符串里的单词

问题描述

给你一个字符串 s ,请你反转字符串中 单词 的顺序。

单词是由非空格字符组成的字符串。s 中使用至少一个空格将字符串中的单词分隔开。返回单词顺序颠倒且单词 之间用单个空格连接的结果字符串。

注意:输入字符串 s中可能会存在前导空格、尾随空格或者单词间的多个空格。返回的结果字符串中,单词间应当仅用单个空格分隔,且不包含任何额外的空格。

问题分析

按照题目中的要求直接实现是比较复杂的,我们可以逐渐递进简化问题的难度,首先,给定的字符串中存在前导空格、尾随空格或者单词间的多个空格,这是不便于我们的反转操作的,我们可以先去掉前导空格和尾随空格,将单词之间的空格保留至一个。我们可以通过双指针遍历两端,忽略空格,只保留其他字符的方式实现去掉前后空格,对于单词之间的多个空格,我们在遍历时判断,如果当前字符不是空格,直接保留,如果当前字符是空格,判断当前字符的前一个字符是否是空格,如果是空格,则去掉,否则保留。

public String removeMoreKong(String s){
   char[] chars =  s.toCharArray();
   int left = 0;
   int right = s.length()-1;
   while(left<right && chars[left]==' '){
       left++;
   }
   while(left<right && chars[right]==' '){
       right--;
   }
   StringBuilder sb = new StringBuilder();
   for(int i=left;i<=right;i++){
       if(chars[i]!=' '){
           sb.append(chars[i]);
       }else if(sb.charAt(sb.length()-1) != ' '){
           sb.append(chars[i]);
       }
   }
   return sb.toString();
}

去掉多余空格之后,我们的到如s = “the sky is blue"格式的字符串,最终我们希望得到"blue is sky the”,这里,我们可以通过将字符串分割,然后在变换顺序的方式,但是,我们也可以依旧操作字符数组,先将字符数组整体反转,得到"eht yks si eulb",然后再将单词内部反转得到最终结果。

public String reverseLetter(String s){
    char[] chars =  s.toCharArray();
    int n = chars.length;
    for(int i=0;i<n/2;i++){
       char temp = chars[i];
       chars[i] = chars[n-1-i];
       chars[n-1-i] = temp;
    }
    return new String(chars);
}

public String reverseWord(String s){
    int start = 0;
    int end = 0;
    char[] chars =  s.toCharArray();
    while(start < s.length()){
        while(end < s.length() && chars[end] != ' '){
            end++;
        }
        int left = start;
        int right = end-1;
        while(left<right){
            char c = chars[left];
            chars[left] = chars[right];
            chars[right] = c;
            left++;
            right--;
        }
        start = end + 1;
        end = end + 1;
    }
    return new String(chars);
}

最终的代码实现,调用我们上面已经实现的方法即可

代码实现

public String reverseWords(String s) {
    s = removeMoreKong(s);
    s = reverseLetter(s);
    s = reverseWord(s);
    return s;
}

API实现

字符串中有很多现成方法可以供我们调用实现上述题目要求,简化我们平时的开发,但是在算法问题中一般我们不能直接调用,这里可以提供一份参考:

public String reverseWords(String s) {
    if(s==null || s.length()==0){
        return s;
    }
    // 去掉前后空格
    s = s.trim();
    // 去掉单词之间的多余空格,并按照单词划分为字符串数组
    String[] splits = s.split("\\s+");
    // 将字符串数组转化为列表
    List<String> stringList = Arrays.asList(splits);
    // 反转单词
    Collections.reverse(stringList);
    //单词之间添加空格并转化为字符串
    return String.join(" ", stringList);
}

总结

反转字符串可以通过改变底层字符数组的字符顺序实现,可以通过双指针进行遍历交换,再结合体具体要求,判断双指针的指向交换条件即可。

你可能感兴趣的:(算法,数据结构,java)