字符串系列③ -- 翻转字符串里的单词

题目概述

此题对应力扣里的151.翻转字符串里的单词,中等难度,解题过程些许麻烦,但把问题分解,再解决就会轻松很多。

给你一个字符串 s ,逐个翻转字符串中的所有 单词 。
单词 是由非空格字符组成的字符串。s 中使用至少一个空格将字符串中的 单词 分隔开。
请你返回一个翻转 s 中单词顺序并用单个空格相连的字符串。
说明:
①输入字符串 s 可以在前面、后面或者单词间包含多余的空格。
②翻转后单词间应当仅用一个空格分隔。
③翻转后的字符串中不应包含额外的空格。

示例:
示例

解题思路

首先拿到这个题目,分析之后发现这个题是对字符串操作的综合考察,可以把问题进行分解,如下:

  • 第一步:去除前后的空格。
  • 第二步:把整个字符串翻转过来。
  • 第三步:把其中的每一个单词翻转过来。
  • 第四步:把单词中间的空格缩减到一个。

第一步以及第二步涉及的思想方法在字符串系列的①,②中已经讲解过,这里不再赘述。
第三步思路:只要是字符串翻转那么首先想到的就应该是双指针法,但是这里的翻转却并不像第二部那般简单,因为在第二部中指针的初始位置是很容易确定的,一头一尾。而在第三部中因为要对每个单词进行翻转,那么指针的初始位置要一次次发生改变。如何控制这个变化呢?我想到的还是双指针法!
过程图1
流程图二
开始的时候先让两个指针与数组的头部对其,让指针2去摸索第一个字符段的结尾(我们规定指针形成的区域为闭区间),在此处新创建两个指针进行翻转(先前的两个指针是用来寻找翻转区域的,此处创建的两个指针是用来翻转的,可能有人会问不能就用先前的两个指针来翻转吗?当然不行,因为翻转用的那对指针会不停的往内收缩,如果共用就会导致划定区间的指针丢失位置!同时注意:这两对指针不能共用,但是能复用),翻转的过程完成后,指针2继续往后摸索到下一个字符段的头部,然后让指针1与此时的指针2对齐。

其实此时我们不难发现一个规律:
关系图
每个字符段加上其后面的空格形成一个轮回(空格数不限,只是图中都画得是一个),在每一个轮回中两个指针的行为是一样的。最后一个绿圈也算一个不完整的轮回因为它处于数组的尾部没有空格部分,在程序中要使用条件语句进行控制,否则在进行到最后一个轮回的时候,极有可能会出现角标越界的情况。

第四步的思路
把字符段之间的空格缩减到一,我想到的是使用一个单指针,新建一个StringBuilder往里面加东西,在加的过程中控制空格的数量。此处我也运用了步骤三的思想。同样也是把一个字符段以及其后面的若干空格当作一个轮回,不过指针的行为不一样,此次单指针要摸索到字符段尾部的后面一格,将这一部分添加到StringBuffer中之后,单指针停止录入,继续往后摸索到下一个字符段的开头,完成一个完整的轮回。同样,最后一个轮回要用条件语句进行控制,否则会出现角标越界的情况。

代码实现

实现一:

public String solution(String s){
        //首先转化为char型数组
        char[] ss = s.toCharArray();
        //第二步:去除前后的空格(双指针法)
        int head = 0;
        int end = ss.length - 1;
        while(ss[head]==' '){ head++; }
        while(ss[end]==' '){ end--; }
        //此时的head和end划定的区域就是到时候要返回的区域
        //第三步:将整个字符串反转(同样双指针法,重新定义两个指针)
        int left = head;
        int right = end;
        // 第三者暂存处
        char temp;
        while(left<right){
            temp = ss[left];
            ss[left] = ss[right];
            ss[right] = temp;
            left++;
            right--;
        }
        //第三步:调转每一个单词(初始化之后,继续使用上述的两个指针)
        left = head;
        right = left;
        int left1;
        int right1;
        while(right < end){
            while(ss[right+1] != ' '){
                right++;
                if (right == end){ break;}
            }
            left1 = left;
            right1 = right;
            while(left1<right1){
                temp = ss[left1];
                ss[left1] = ss[right1];
                ss[right1] = temp;
                left1++;
                right1--;
            }
            if(right == end){ break; }
            left = right+1;
            while(ss[left]==' '){ left++; }
            right = left;
        }
        char[] ans = new String(ss).substring(head,end+1).toCharArray();
        StringBuilder box = new StringBuilder();
        //建立添加指针;
        int add = 0;
        while(add<ans.length){
            while(ans[add]!=' '){
                box.append(ans[add]);
                add++;
                if(add>=ans.length){break;}
            }
            if(add>=ans.length){break;}
            box.append(ans[add]);
            while(ans[add]==' '){ add++; }
        }
        return new String(box);
    }

做完之后我当时觉得我的思路是不是有点混乱,再加上程序有点冗长,所以以为在力扣上会超时通过不了,但结果不算太坏。
力扣上的运行结果

做题反思

这道题我出思路,打代码,测试,纠错前前后后大概花了一个小时,毕竟也是第一次接触这种题。在做题的时候发现了自己有些知识点的遗忘,如下:

  • while循环,for循环均可以加标签,在break时可以退出标签所在的相应循环。
  • 如果有两层以上的多重循环,如果停止最里面的循环外面的循环依旧会进行;而如果停止最外层的循环,则所有的循环都会停止。

总结方法

在使用while完成复杂的动态变化时,要记住一个要点,就是每次进入while循环的状态要保持一样(如此才具有一种连续性),这个状态可以在while循环代码的后半段建立。也就是说要把每个轮回划分清楚,执行清楚,并且对最后一个不完整的轮回进行处理,防止角标越界等错误的发生!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

十八岁讨厌编程

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值