首页 > 其他分享 > 代码随想录Day8

代码随想录Day8

时间:2022-10-23 20:59:01浏览次数:45  
标签:String Day8 int 代码 随想录 initialArr 空格 length chars

剑指offer05.替换空格

请实现一个函数,把字符串 s 中的每个空格替换成"%20"。

示例 1: 输入:s = "We are happy."
输出:"We%20are%20happy."

思路:

1.首先需要考虑扩容后数组的大小。因为空格填充不是一对一填充,首先需要考虑的问题就是数组长度如何定义。

   两种方法定义:1.边扩容边开辟新的空间   2.初始化数组的时候定义好大小。

初始化时开辟大小是比较优的选择,避免多次开辟空间造成的系统资源占用。

2.需要考虑如何进行替换操作,原来的数据怎么办。

  两种方法:笨方法就是新开一个数组,从头开始遍历,遇到空格就填充对应的填充符,这种方法无脑但是效率低。

  最聪明的方法是使用双指针。那么是从前往后填充还是从后往前填充呢?

如果是从前往后进行填充,比如我们移动到第一个空格,我们需要把后面的所有元素后移三个,填充,然后再开始继续遍历,这效率可能还不如用笨方法。
如果是从后往前填充,我们把左指针的值复制到新开辟的空格中,进行替换操作,遇到空格填充空白部分,相当于双指针一次遍历完成多躺赋值操作。

 

代码:

package com.dwj.LeetCode.String;

/**
 * 替换空格。剑指offer05
 * 请实现一个函数,把字符串 s 中的每个空格替换成"%20"。
 *
 * 示例 1: 输入:s = "We are happy."
 * 输出:"We%20are%20happy."
 */
public class replaceBlank {
    //使用一个新的对象,复制 str,复制的过程对其判断,是空格则替换,否则直接复制,类似于数组复制
    public static String replaceSpace(String s) {
        if (s == null) {
            return null;
        }
        //选用 StringBuilder 单线程使用,比较快,选不选都行
        StringBuilder sb = new StringBuilder();
        //使用 sb 逐个复制 s ,碰到空格则替换,否则直接复制
        for (int i = 0; i < s.length(); i++) {
            //s.charAt(i) 为 char 类型,为了比较需要将其转为和 " " 相同的字符串类型
            //if (" ".equals(String.valueOf(s.charAt(i)))){}
            if (s.charAt(i) == ' ') {
                sb.append("%20");
            } else {
                sb.append(s.charAt(i));
            }
        }
        return sb.toString();
    }

    //方式二:双指针法
    public String replaceSpace2(String s) {
        if(s == null || s.length() == 0){
            return s;
        }
        //扩充空间,空格数量2倍
        StringBuilder str = new StringBuilder();
        for (int i = 0; i < s.length(); i++) {
            if(s.charAt(i) == ' '){
                str.append("  ");
            }
        }
        //若是没有空格直接返回
        if(str.length() == 0){
            return s;
        }
        //有空格情况 定义两个指针
        int left = s.length() - 1;//左指针:指向原始字符串最后一个位置
        s += str.toString();
        int right = s.length()-1;//右指针:指向扩展字符串的最后一个位置
        char[] chars = s.toCharArray();
        while(left>=0){
            if(chars[left] == ' '){
                chars[right--] = '0';
                chars[right--] = '2';
                chars[right] = '%';
            }else{
                chars[right] = chars[left];
            }
            left--;
            right--;
        }
        return new String(chars);
    }
}

 

LeetCode151.翻转字符串里的单词

给定一个字符串,逐个翻转字符串中的每个单词。

示例 1:
输入: "the sky is blue"
输出: "blue is sky the"

示例 2:
输入: "  hello world!  "
输出: "world! hello"
解释: 输入字符串可以在前面或者后面包含多余的空格,但是反转后的字符不能包括。

示例 3:
输入: "a good   example"
输出: "example good a"
解释: 如果两个单词间有多余的空格,将反转后单词间的空格减少到只含一个。

 

思路:

整体思路,去掉多余空格,整体翻转,每个单词翻转。

1.首先需要考虑,给出的字符串存在多余的前后空格,需要先清除掉。

可以考虑使用快慢指针,因为:

快指针遍历所有元素,找出符合的元素,赋值给慢指针位置。慢指针只在条件符合的时候进行移动操作
如果慢指针的下标不为起始位置,需要给慢指针的位置多留一个空格,每个单词之前那都有一个空格。然后把快指针位置的单词赋给慢指针即可。

四种不同的写法:

package com.dwj.LeetCode.String;

/**
 * LeetCode151 给定一个字符串,逐个翻转字符串中的每个单词。
 *
 * 示例 1:
 * 输入: "the sky is blue"
 * 输出: "blue is sky the"
 *
 * 示例 2:
 * 输入: "  hello world!  "
 * 输出: "world! hello"
 * 解释: 输入字符串可以在前面或者后面包含多余的空格,但是反转后的字符不能包括。
 *
 * 示例 3:
 * 输入: "a good   example"
 * 输出: "example good a"
 * 解释: 如果两个单词间有多余的空格,将反转后单词间的空格减少到只含一个。
 *
 */
public class reverseWrold {
    class Solution {
        /**
         * 不使用Java内置方法实现
         * <p>
         * 1.去除首尾以及中间多余空格
         * 2.反转整个字符串
         * 3.反转各个单词
         */
        public String reverseWords(String s) {
            // System.out.println("ReverseWords.reverseWords2() called with: s = [" + s + "]");
            // 1.去除首尾以及中间多余空格
            StringBuilder sb = removeSpace(s);
            // 2.反转整个字符串
            reverseString(sb, 0, sb.length() - 1);
            // 3.反转各个单词
            reverseEachWord(sb);
            return sb.toString();
        }

        private StringBuilder removeSpace(String s) {
            // System.out.println("ReverseWords.removeSpace() called with: s = [" + s + "]");
            int start = 0;
            int end = s.length() - 1;
            while (s.charAt(start) == ' ') start++;
            while (s.charAt(end) == ' ') end--;
            StringBuilder sb = new StringBuilder();
            while (start <= end) {
                char c = s.charAt(start);
                if (c != ' ' || sb.charAt(sb.length() - 1) != ' ') {
                    sb.append(c);
                }
                start++;
            }
            // System.out.println("ReverseWords.removeSpace returned: sb = [" + sb + "]");
            return sb;
        }

        /**
         * 反转字符串指定区间[start, end]的字符
         */
        public void reverseString(StringBuilder sb, int start, int end) {
            // System.out.println("ReverseWords.reverseString() called with: sb = [" + sb + "], start = [" + start + "], end = [" + end + "]");
            while (start < end) {
                char temp = sb.charAt(start);
                sb.setCharAt(start, sb.charAt(end));
                sb.setCharAt(end, temp);
                start++;
                end--;
            }
            // System.out.println("ReverseWords.reverseString returned: sb = [" + sb + "]");
        }

        private void reverseEachWord(StringBuilder sb) {
            int start = 0;
            int end = 1;
            int n = sb.length();
            while (start < n) {
                while (end < n && sb.charAt(end) != ' ') {
                    end++;
                }
                reverseString(sb, start, end - 1);
                start = end + 1;
                end = start + 1;
            }
        }
    }


    //解法二:创建新字符数组填充。时间复杂度O(n)
    class Solution2 {
        public String reverseWords(String s) {
            //源字符数组
            char[] initialArr = s.toCharArray();
            //新字符数组
            char[] newArr = new char[initialArr.length+1];//下面循环添加"单词 ",最终末尾的空格不会返回
            int newArrPos = 0;
            //i来进行整体对源字符数组从后往前遍历
            int i = initialArr.length-1;
            while(i>=0){
                while(i>=0 && initialArr[i] == ' '){i--;}  //跳过空格
                //此时i位置是边界或!=空格,先记录当前索引,之后的while用来确定单词的首字母的位置
                int right = i;
                while(i>=0 && initialArr[i] != ' '){i--;}
                //指定区间单词取出(由于i为首字母的前一位,所以这里+1,),取出的每组末尾都带有一个空格
                for (int j = i+1; j <= right; j++) {
                    newArr[newArrPos++] = initialArr[j];
                    if(j == right){
                        newArr[newArrPos++] = ' ';//空格
                    }
                }
            }
            //若是原始字符串没有单词,直接返回空字符串;若是有单词,返回0-末尾空格索引前范围的字符数组(转成String返回)
            if(newArrPos == 0){
                return "";
            }else{
                return new String(newArr,0,newArrPos-1);
            }
        }
    }

    //解法三:双反转+移位,在原始数组上进行反转。空间复杂度O(1)
    class Solution3 {
        /**
         * 思路:
         *    ①反转字符串  "the sky is blue " => " eulb si yks eht"
         *    ②遍历 " eulb si yks eht",每次先对某个单词进行反转再移位
         *       这里以第一个单词进行为演示:" eulb si yks eht" ==反转=> " blue si yks eht" ==移位=> "blue si yks eht"
         */
        public String reverseWords(String s) {
            //步骤1:字符串整体反转(此时其中的单词也都反转了)
            char[] initialArr = s.toCharArray();
            reverse(initialArr, 0, s.length() - 1);
            int k = 0;
            for (int i = 0; i < initialArr.length; i++) {
                if (initialArr[i] == ' ') {
                    continue;
                }
                int tempCur = i;
                while (i < initialArr.length && initialArr[i] != ' ') {
                    i++;
                }
                for (int j = tempCur; j < i; j++) {
                    if (j == tempCur) { //步骤二:二次反转
                        reverse(initialArr, tempCur, i - 1);//对指定范围字符串进行反转,不反转从后往前遍历一个个填充有问题
                    }
                    //步骤三:移动操作
                    initialArr[k++] = initialArr[j];
                    if (j == i - 1) { //遍历结束
                        //避免越界情况,例如=> "asdasd df f",不加判断最后就会数组越界
                        if (k < initialArr.length) {
                            initialArr[k++] = ' ';
                        }
                    }
                }
            }
            if (k == 0) {
                return "";
            } else {
                //参数三:以防出现如"asdasd df f"=>"f df asdasd"正好凑满不需要省略空格情况
                return new String(initialArr, 0, (k == initialArr.length) && (initialArr[k - 1] != ' ') ? k : k - 1);
            }
        }

        public void reverse(char[] chars, int begin, int end) {
            for (int i = begin, j = end; i < j; i++, j--) {
                chars[i] ^= chars[j];
                chars[j] ^= chars[i];
                chars[i] ^= chars[j];
            }
        }
    }

    //解法三:双反转+移位,在原始数组上进行反转。空间复杂度O(1)
    class Solution {
        /**
         * 思路:
         *    ①反转字符串  "the sky is blue " => " eulb si yks eht"
         *    ②遍历 " eulb si yks eht",每次先对某个单词进行反转再移位
         *       这里以第一个单词进行为演示:" eulb si yks eht" ==反转=> " blue si yks eht" ==移位=> "blue si yks eht"
         */
        public String reverseWords(String s) {
            //步骤1:字符串整体反转(此时其中的单词也都反转了)
            char[] initialArr = s.toCharArray();
            reverse(initialArr, 0, s.length() - 1);
            int k = 0;
            for (int i = 0; i < initialArr.length; i++) {
                if (initialArr[i] == ' ') {
                    continue;
                }
                int tempCur = i;
                while (i < initialArr.length && initialArr[i] != ' ') {
                    i++;
                }
                for (int j = tempCur; j < i; j++) {
                    if (j == tempCur) { //步骤二:二次反转
                        reverse(initialArr, tempCur, i - 1);//对指定范围字符串进行反转,不反转从后往前遍历一个个填充有问题
                    }
                    //步骤三:移动操作
                    initialArr[k++] = initialArr[j];
                    if (j == i - 1) { //遍历结束
                        //避免越界情况,例如=> "asdasd df f",不加判断最后就会数组越界
                        if (k < initialArr.length) {
                            initialArr[k++] = ' ';
                        }
                    }
                }
            }
            if (k == 0) {
                return "";
            } else {
                //参数三:以防出现如"asdasd df f"=>"f df asdasd"正好凑满不需要省略空格情况
                return new String(initialArr, 0, (k == initialArr.length) && (initialArr[k - 1] != ' ') ? k : k - 1);
            }
        }

        public void reverse(char[] chars, int begin, int end) {
            for (int i = begin, j = end; i < j; i++, j--) {
                chars[i] ^= chars[j];
                chars[j] ^= chars[i];
                chars[i] ^= chars[j];
            }
        }
    }
    /*
     * 解法四:时间复杂度 O(n)
     * 参考卡哥 c++ 代码的三步骤:先移除多余空格,再将整个字符串反转,最后把单词逐个反转
     * 有别于解法一 :没有用 StringBuilder  实现,而是对 String 的 char[] 数组操作来实现以上三个步骤
     */
    class Solution4 {
        //用 char[] 来实现 String 的 removeExtraSpaces,reverse 操作
        public String reverseWords(String s) {
            char[] chars = s.toCharArray();
            //1.去除首尾以及中间多余空格
            chars = removeExtraSpaces(chars);
            //2.整个字符串反转
            reverse(chars, 0, chars.length - 1);
            //3.单词反转
            reverseEachWord(chars);
            return new String(chars);
        }

        //1.用 快慢指针 去除首尾以及中间多余空格,可参考数组元素移除的题解
        public char[] removeExtraSpaces(char[] chars) {
            int slow = 0;
            for (int fast = 0; fast < chars.length; fast++) {
                //先用 fast 移除所有空格
                if (chars[fast] != ' ') {
                    //在用 slow 加空格。 除第一个单词外,单词末尾要加空格
                    if (slow != 0)
                        chars[slow++] = ' ';
                    //fast 遇到空格或遍历到字符串末尾,就证明遍历完一个单词了
                    while (fast < chars.length && chars[fast] != ' ')
                        chars[slow++] = chars[fast++];
                }
            }
            //相当于 c++ 里的 resize()
            char[] newChars = new char[slow];
            System.arraycopy(chars, 0, newChars, 0, slow);
            return newChars;
        }

        //双指针实现指定范围内字符串反转,可参考字符串反转题解
        public void reverse(char[] chars, int left, int right) {
            if (right >= chars.length) {
                System.out.println("set a wrong right");
                return;
            }
            while (left < right) {
                chars[left] ^= chars[right];
                chars[right] ^= chars[left];
                chars[left] ^= chars[right];
                left++;
                right--;
            }
        }

        //3.单词反转
        public void reverseEachWord(char[] chars) {
            int start = 0;
            //end <= s.length() 这里的 = ,是为了让 end 永远指向单词末尾后一个位置,这样 reverse 的实参更好设置
            for (int end = 0; end <= chars.length; end++) {
                // end 每次到单词末尾后的空格或串尾,开始反转单词
                if (end == chars.length || chars[end] == ' ') {
                    reverse(chars, start, end - 1);
                    start = end + 1;
                }
            }
        }
    }
}

 

标签:String,Day8,int,代码,随想录,initialArr,空格,length,chars
From: https://www.cnblogs.com/dwj-ngu/p/16818882.html

相关文章