山间桃花纷飞落,撑伞也是雨中人。|

颜欢兮

园龄:2年1个月粉丝:1关注:0

代码随想录算法训练营Day08 | 344.反转字符串,541. 反转字符串II, 剑指Offer 05.替换空格 ,151.翻转字符串里的单词 ,剑指Offer58-II.左旋转字符串

 344.反转字符串

题目链接:344. 反转字符串 - 力扣(LeetCode)

题目

编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 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 class Solution {
 2     public void reverseString(char[] s) {
 3         int l = 0;
 4         int r = s.length - 1;
 5         while (l < r) {
 6             s[l] ^= s[r];  //构造 a ^ b 的结果,并放在 a 中
 7             s[r] ^= s[l];  //将 a ^ b 这一结果再 ^ b ,存入b中,此时 b = a, a = a ^ b
 8             s[l] ^= s[r];  //a ^ b 的结果再 ^ a ,存入 a 中,此时 b = a, a = b 完成交换
 9             l++;
10             r--;
11         }
12     }
13 }
14 
15 // 第二种方法用temp来交换数值更多人容易理解些
16 class Solution {
17     public void reverseString(char[] s) {
18         int l = 0;
19         int r = s.length - 1;
20         while(l < r){
21             char temp = s[l];
22             s[l] = s[r];
23             s[r] = temp;
24             l++;
25             r--;
26         }
27     }
28 }
复制代码

扩充

交换两个值的写法有两种。

写法一:

1 int tmp = s[i];
2 s[i] = s[j];
3 s[j] = tmp;

写法二(位运算):

1 s[i] ^= s[j];
2 s[j] ^= s[i];
3 s[i] ^= s[j];

541. 反转字符串II

题目链接:541. 反转字符串 II - 力扣(LeetCode)

题目

给定一个字符串 s 和一个整数 k,从字符串开头算起,每计数至 2k 个字符,就反转这 2k 字符中的前 k 个字符。

如果剩余字符少于 k 个,则将剩余字符全部反转。
如果剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符,其余字符保持原样。
 

示例 1:

输入:s = "abcdefg", k = 2
输出:"bacdfeg"

示例 2:

输入:s = "abcd", k = 2
输出:"bacd"

思路

按照题目描述的一步一步来,当走2k的时候翻转前k个。注意,刚进入循环的时候就要判断目前字符串的长度是否足够2k,如果不够2k,是否又够k,还有就是每次翻转完成也要判断一次。

代码

复制代码
 1 //解法一
 2 class Solution {
 3     public String reverseStr(String s, int k) {
 4         StringBuffer res = new StringBuffer();
 5         int length = s.length();
 6         int start = 0;
 7         while (start < length) {
 8             // 找到k处和2k处
 9             StringBuffer temp = new StringBuffer();
10             // 与length进行判断,如果大于length了,那就将其置为length
11             int firstK = (start + k > length) ? length : start + k;
12             int secondK = (start + (2 * k) > length) ? length : start + (2 * k);
13 
14             //无论start所处位置,至少会反转一次
15             temp.append(s.substring(start, firstK));
16             res.append(temp.reverse());
17 
18             // 如果firstK到secondK之间有元素,这些元素直接放入res里即可。
19             if (firstK < secondK) { //此时剩余长度一定大于k。
20                 res.append(s.substring(firstK, secondK));
21             }
22             start += (2 * k);
23         }
24         return res.toString();
25     }
26 }
27 
28 //解法二(似乎更容易理解点)
29 //题目的意思其实概括为 每隔2k个反转前k个,尾数不够k个时候全部反转
30 class Solution {
31     public String reverseStr(String s, int k) {
32         char[] ch = s.toCharArray();
33         for(int i = 0; i < ch.length; i += 2 * k){
34             int start = i;
35             //这里是判断尾数够不够k个来取决end指针的位置
36             int end = Math.min(ch.length - 1, start + k - 1);
37             //用异或运算反转 
38             while(start < end){
39                 ch[start] ^= ch[end];
40                 ch[end] ^= ch[start];
41                 ch[start] ^= ch[end];
42                 start++;
43                 end--;
44             }
45         }
46         return new String(ch);
47     }
48 }
复制代码
复制代码
 1 // 解法3
 2 class Solution {
 3     public String reverseStr(String s, int k) {
 4         char[] ch = s.toCharArray();
 5         // 1. 每隔 2k 个字符的前 k 个字符进行反转
 6         for (int i = 0; i< ch.length; i += 2 * k) {
 7             // 2. 剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符
 8             if (i + k <= ch.length) {
 9                 reverse(ch, i, i + k -1);
10                 continue;
11             }
12             // 3. 剩余字符少于 k 个,则将剩余字符全部反转
13             reverse(ch, i, ch.length - 1);
14         }
15         return  new String(ch);
16 
17     }
18     // 定义翻转函数
19     public void reverse(char[] ch, int i, int j) {
20     for (; i < j; i++, j--) {
21         char temp  = ch[i];
22         ch[i] = ch[j];
23         ch[j] = temp;
24     }
25 
26     }
27 }
复制代码

 

剑指Offer 05.替换空格

题目链接:剑指 Offer 05. 替换空格 - 力扣(LeetCode)

题目

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

 

示例 1:

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

 

思路

方法一:使用一个新的对象(StringBuilder),在复制str的过程中判断,是空格则替换,否则直接复制。

双指针法(推荐):首先扩充数组到每个空格替换成"%20"之后的大小。然后从后向前判断替换空格,也就是双指针法,好处是时间复杂度和空间复杂度都降低到了极致。

疑问:为什么要从后向前填充,从前向后填充不行么?

从前向后填充就是O(n^2)的算法了,因为每次添加元素都要将添加元素之后的所有元素向后移动。

代码

方法一

复制代码
 1 //使用一个新的对象,复制 str,复制的过程对其判断,是空格则替换,否则直接复制,类似于数组复制
 2 public static String replaceSpace(String s) {
 3         if (s == null) {
 4             return null;
 5         }
 6         //选用 StringBuilder 单线程使用,比较快,选不选都行
 7         StringBuilder sb = new StringBuilder();
 8         //使用 sb 逐个复制 s ,碰到空格则替换,否则直接复制
 9         for (int i = 0; i < s.length(); i++) {
10             //s.charAt(i) 为 char 类型,为了比较需要将其转为和 " " 相同的字符串类型
11             //if (" ".equals(String.valueOf(s.charAt(i)))){}
12             if (s.charAt(i) == ' ') {
13                 sb.append("%20");
14             } else {
15                 sb.append(s.charAt(i));
16             }
17         }
18         return sb.toString();
19     }
复制代码

双指针法

复制代码
 1 //方式二:双指针法
 2 public String replaceSpace(String s) {
 3     if(s == null || s.length() == 0){
 4         return s;
 5     }
 6     //扩充空间,空格数量2倍
 7     StringBuilder str = new StringBuilder();
 8     for (int i = 0; i < s.length(); i++) {
 9         if(s.charAt(i) == ' '){
10             str.append("  ");
11         }
12     }
13     //若是没有空格直接返回
14     if(str.length() == 0){
15         return s;
16     }
17     //有空格情况 定义两个指针
18     int left = s.length() - 1;//左指针:指向原始字符串最后一个位置
19     s += str.toString();
20     int right = s.length()-1;//右指针:指向扩展字符串的最后一个位置
21     char[] chars = s.toCharArray();
22     while(left>=0){
23         if(chars[left] == ' '){
24             chars[right--] = '0';
25             chars[right--] = '2';
26             chars[right] = '%';
27         }else{
28             chars[right] = chars[left];
29         }
30         left--;
31         right--;
32     }
33     return new String(chars);
34 }
复制代码

 

151.翻转字符串里的单词

题目链接:151. 反转字符串中的单词 - 力扣(LeetCode)

题目

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

单词 是由非空格字符组成的字符串。s 中使用至少一个空格将字符串中的 单词 分隔开。

返回 单词 顺序颠倒且 单词 之间用单个空格连接的结果字符串。

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

 

示例 1:

输入:s = "the sky is blue"
输出:"blue is sky the"

示例 2:

输入:s = "  hello world  "
输出:"world hello"
解释:反转后的字符串中不能存在前导空格和尾随空格。


示例 3:

输入:s = "a good   example"
输出:"example good a"
解释:如果两个单词间有多余的空格,反转后的字符串需要将单词间的空格减少到仅有一个。

思路

方法一(不推荐):使用split库函数,分隔单词,然后定义一个新的string字符串,最后再把单词倒序相加。

方法二:不使用辅助空间,空间复杂度为O(1)。分三步。

  • 移除多余空格
  • 将整个字符串反转
  • 将每个单词反转

举个例子,源字符串为:"    the   sky is      blue       "

  • 移除多余空格 : "the sky is blue"
  • 字符串反转:"eulb si yks eht"
  • 单词反转:"blue is sky the"

 

这样我们就完成了翻转字符串里的单词。

 

代码

复制代码
 1 class Solution {
 2    /**
 3      * 不使用Java内置方法实现
 4      * <p>
 5      * 1.去除首尾以及中间多余空格
 6      * 2.反转整个字符串
 7      * 3.反转各个单词
 8      */
 9     public String reverseWords(String s) {
10         // System.out.println("ReverseWords.reverseWords2() called with: s = [" + s + "]");
11         // 1.去除首尾以及中间多余空格
12         StringBuilder sb = removeSpace(s);
13         // 2.反转整个字符串
14         reverseString(sb, 0, sb.length() - 1);
15         // 3.反转各个单词
16         reverseEachWord(sb);
17         return sb.toString();
18     }
19 
20     private StringBuilder removeSpace(String s) {
21         // System.out.println("ReverseWords.removeSpace() called with: s = [" + s + "]");
22         int start = 0;
23         int end = s.length() - 1;
24         while (s.charAt(start) == ' ') start++;
25         while (s.charAt(end) == ' ') end--;
26         StringBuilder sb = new StringBuilder();
27         while (start <= end) {
28             char c = s.charAt(start);
29             //当c为空格时,会判断当前sb是否已经存在一个空格,存在则不进行操作,不存在则加入空格
30             if (c != ' ' || sb.charAt(sb.length() - 1) != ' ') {
31                 sb.append(c);
32             }
33             start++;
34         }
35         // System.out.println("ReverseWords.removeSpace returned: sb = [" + sb + "]");
36         return sb;
37     }
38 
39     /**
40      * 反转字符串指定区间[start, end]的字符
41      */
42     public void reverseString(StringBuilder sb, int start, int end) {
43         // System.out.println("ReverseWords.reverseString() called with: sb = [" + sb + "], start = [" + start + "], end = [" + end + "]");
44         while (start < end) {
45             char temp = sb.charAt(start);
46             sb.setCharAt(start, sb.charAt(end));
47             sb.setCharAt(end, temp);
48             start++;
49             end--;
50         }
51         // System.out.println("ReverseWords.reverseString returned: sb = [" + sb + "]");
52     }
53 
54     private void reverseEachWord(StringBuilder sb) {
55         int start = 0;
56         int end = 1;
57         int n = sb.length();
58         while (start < n) {
59             while (end < n && sb.charAt(end) != ' ') {
60                 end++;
61             }
62             reverseString(sb, start, end - 1);
63             start = end + 1;
64             end = start + 1;
65         }
66     }
67 }
复制代码

 

剑指Offer58-II.左旋转字符串

题目链接:剑指 Offer 58 - II. 左旋转字符串 - 力扣(LeetCode)

题目

字符串的左旋转操作是把字符串前面的若干个字符转移到字符串的尾部。请定义一个函数实现字符串左旋转操作的功能。比如,输入字符串"abcdefg"和数字2,该函数将返回左旋转两位得到的结果"cdefgab"。

 

示例 1:

输入: s = "abcdefg", k = 2
输出: "cdefgab"

示例 2:

输入: s = "lrloseumgh", k = 6
输出: "umghlrlose"

思路

为了让本题更有意义,提升一下本题难度:不能申请额外空间,只能在本串上操作。

不能使用额外空间的话,模拟在本串操作要实现左旋转字符串的功能还是有点困难的。

那么我们可以用整体反转+局部反转就可以实现反转单词顺序的目的。

具体步骤为:

  1. 反转区间为前n的子串
  2. 反转区间为n到末尾的子串
  3. 反转整个字符串

最后就可以达到左旋n的目的,而不用定义新的字符串,完全在本串上操作。

注意:

  1. Java中String底层是用char数组所以我们使用toCharArray方法实际上不会产生额外的空间。
  2. 可以使用substr,来做这道题。 其实使用substr 和 反转 时间复杂度是一样的 ,都是O(n),但是使用substr申请了额外空间,所以空间复杂度是O(n),而反转方法的空间复杂度是O(1)。

如果想让这套题目有意义,就不要申请额外空间。

代码

复制代码
 1 //思路为:先整个字符串反转,再反转前面的,最后反转后面 n 个
 2 class Solution {
 3     public String reverseLeftWords(String s, int n) {
 4         char[] chars = s.toCharArray();
 5         reverse(chars, 0, chars.length - 1);
 6         reverse(chars, 0, chars.length - 1 - n);
 7         reverse(chars, chars.length - n, chars.length - 1);
 8         return new String(chars);
 9     }
10 
11     public void reverse(char[] chars, int left, int right) {
12         while (left < right) {
13             chars[left] ^= chars[right];
14             chars[right] ^= chars[left];
15             chars[left] ^= chars[right];
16             left++;
17             right--;
18         }
19     }
复制代码

 

本文作者:颜欢兮

本文链接:https://www.cnblogs.com/xpp3/p/17113734.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   颜欢兮  阅读(28)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起
  1. 1 《起风了》 林俊杰
  2. 2 晴天 (Live) 林俊杰,周杰伦
  3. 3 兰亭序 (Live) 周杰伦
  4. 4 反方向的钟 (Live) 我也不太了解她
晴天 (Live) - 林俊杰,周杰伦
00:00 / 00:00
An audio error has occurred, player will skip forward in 2 seconds.

晴天 (Live) - 林俊杰 (JJ Lin)/周杰伦 (Jay Chou)

词:周杰伦

曲:周杰伦

故事的小黄花

从出生那年就飘着

童年的荡秋千

随记忆一直晃到现在

Re so so si do si la

So la si si si si la si la so

唱好朋友写的歌

唱好朋友写的歌

其实我是来找你打电动的

为你翘课的那一天

花落的那一天

教室的那一间

我怎么看不见

消失的下雨天

我好想再淋一遍

没想到失去的勇气我还留着

没想到失去的勇气我还留着

好想再问一遍

好想再问一遍

你会等待还是离开

刮风这天我试着握着你手

刮风这天我试着握着你手

但偏偏雨渐渐大到我看你不见

还要多久我才能在你身边

还要多久我才能在你身边

等到放晴的那天

也许我会比较好一点

从前从前有个人爱你很久

但偏偏风渐渐把距离吹得好远

好不容易又能再多爱一天

好不容易又能再多爱一天

但故事的最后你好像还是说了拜拜

为你翘课的那一天

为你翘课的那一天

教室的那一间

花落的那一天

我怎么看不见

消失的下雨天

我好想再淋一遍

没想到失去的勇气我还留着

没想到失去的勇气我还留着

好想再问一遍

你会等待还是离开

刮风这天我试着握着你手

但偏偏雨渐渐大到我看你不见

还要多久我才能在你身边

还要多久我才能在你身边

等到放晴的那天

也许我会比较好一点

从前从前有个人爱你很久

但偏偏风渐渐把距离吹得好远

好不容易又能再多爱一天

好不容易又能再多爱一天

但故事的最后你好像还是说了拜拜

但故事的最后你好像还是说了拜拜

但故事的最后你好像还是说了拜拜