leetcode题544 java解反转字符串2 beat100%

48 篇文章 0 订阅

一、问题描述:

反转字符串

CategoryDifficultyLikesDislikes
algorithmsEasy (78.10%)551-
Tags

two-pointers | string

Companies

Unknown

编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。

不要给另外的数组分配额外的空间,你必须**原地修改输入数组**、使用 O(1) 的额外空间解决这一问题。

示例 1:

输入:s = ["h","e","l","l","o"]
输出:["o","l","l","e","h"]

示例 2:

输入:s = ["H","a","n","n","a","h"]
输出:["h","a","n","n","a","H"]

提示:

  • 1 <= s.length <= 105
  • s[i] 都是 ASCII 码表中的可打印字符

Discussion | Solution

二、思路:

xin麒的题解1仅beat60%~70%,但是题解2可以beat100%,而两者思路完全一致,只是代码结构略有差异,题解2的是因为交换元素时,调用成员方法,所以才beat100%,这两者的结构为什么差异如此大,麒麒后面会细说。思路是根据题解1来写的,相信明白题解1的思路就可以明白题解2了。

前提引入:

  • 字符数组chars用来保存字符串s的所有元素;
  • 变量len保存chars的数组长度了;
  • 从左往右将chars分段,每2k个元素为一段,用part变量表示有多少2k个元素的段的数目;
  • 不满足元素数目为2k的最后一段的起始元素索引使用变量left表示;
  • 最后一段的末尾元素索引使用end变量表示;

分析过程:

  • 看到这个题目时,xin麒有一种折叠的感觉,而且需要找规律,对边界的确定尤为重要!
  • xin麒打算分两部分走,首先处理连续元素个数满足2k的那些段落,part = len / (2 * k);,最后一段剩下的元素个数要么是在k到2k之间,要么是不足k个;
  • 当处理完part段后,再处理最后一段的元素,处理的时候,对于交换元素的细节一模一样;

首先将s转换为字符数组chars,再定义好数组长度的len以及计算出满足连续元素个数为2k的段落的数目part;

使用普通for循环,外层是使用变量i来遍历part;

对于每一段数目为2k的连续的元素,因为需要反转的是前k个元素,所以先确定这2k个元素里要反转的最后一个元素的索引end 以及 需要 反转的元素中的第一个元素的索引j

    • 如何确认才是重点:
      • ①当逻辑很清晰时,可以分析:
        • 每一段的首元素的索引应是(i * 2 * k),而再次基础上加上(k - 1)便得到该数目为2k的元素里的第k个元素的索引,因此end = i * 2 * k + k - 1; j = (i * 2 * k);
      • ②可以先别填end和j的值,先把整体的架构写好,后面再用一些特定的字符串带进去,一一确定;

接着就是交换了,使用一个while循环即可,这里的判断条件的选取为重点,end大于j时,便需要交换两索引指向的两个元素,每次交换完,都需要j++和end–;

当这part个2k的元素都已经处理完毕后,就需要处理最后一段元素了;

最后一段元素的第一个元素索引left如何选取呢?根据part的计算方式,left应为(part * 2 * k);

对于最后一个需要被反转的元素的索引end,我们不能直接使用len来赋值,因为这里有2种情况:1、剩下的元素个数小于k;2、剩下的元素个数在[k,2k)之间;

  • 对于情况1,end直接赋值为len即可;
  • 对于情况2,因为长度超过k,那么找到最后一段第k个元素的索引即可,那么end值为(left + k - 1);

剩下的便是对需要反转的元素进行反转即可,实现细节同上述for循环里的while循环一样。

三、题解:

题解1(无法beat100%,但是题解2可以,并且两题解思路一致):

class Solution {
    public String reverseStr(String s, int k) {
        char[] chars = s.toCharArray();
        int len = chars.length;
        int part = len / (2 * k);
        for (int i = 0; i < part; i++) {
            int end = i * 2 * k + k - 1;
            int j = i * 2 * k;
            while(j < end){
                char c = chars[j];
                chars[j++] = chars[end];
                chars[end--] = c;
            }
        }
        int left = part * 2 * k;
        int end = len;
        if (len - left >= k){
            end = left + k - 1;
        }else{
            end = len - 1;
        }
        while (end > left){
            char c = chars[left];
            chars[left++] = chars[end];
            chars[end--] = c;

        }
        return String.valueOf(chars);
    }
} 

运行:

在这里插入图片描述

疑惑与思考:

​ 这样子分析,时间复杂度应该是O(1)啊,都是为什么题解1运行效果却不好呢?

​ xin麒百思不得其解,晚上躺在床上也在思考,睡不着很难受脑海里挂着这个问题,xin麒看到其他评论的都是调用了成员方法,难道他们会有更好的方法吗?难道自己实现的题解的时间复杂度比别人的多很多吗?xin麒看了很久都没有看出来自己的题解哪里存在走多了几步的嫌疑,xin麒始终觉得自己的题解时间复杂度是最优的。突然xin麒灵光一闪,xin麒来一个大胆的假设,于是xin麒在床上躺了近半小时偷偷打开电脑,实在按捺不住想论证自己的猜想:

​ xin麒按照题解1的思路写出了可以beat100%的题解2,应该是论证了xin麒的假设。

​ 题解2和题解1的区别:将反转的实现过程封装到类Solution的成员方法rever里面,private void rever(int end,int left);其中end表示需要传入的末尾索引;left表示需要传入的首元素的索引;保存s字符串的字符数组使用成员变量chars表示。

这个思考的内容很多,为什么调用成员方法就效率提高很多,xin麒想到了很多底层的内容,不过xin麒最近没有足够时间来写文章了,xin麒后面会继续完善的!

题解2:

class Solution {
    char[] chars;
    public String reverseStr(String s, int k) {
        chars = s.toCharArray();
        int len = chars.length;
        int part = len / (2 * k);
        for (int i = 0; i < part; i++) {
            int end = i * 2 * k + k - 1;
            int j = i * 2 * k;
            rever(end,j);

        }
        int left = part * 2 * k;
        int end = len;
        if (len - left >= k){
            end = left + k - 1;
        }else{
            end = len - 1;
        }
        rever(end,left);
        return String.valueOf(chars);
    }
    private void rever(int end,int left){
        while (end > left){
            char c = chars[left];
            chars[left++] = chars[end];
            chars[end--] = c;
        }
    }
}

运行:

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值