题目链接:344. 反转字符串 - 力扣(LeetCode)
能用库函数解决核心问题的,就不要用库函数,自己尝试着去写写。
既然是反转字符,肯定是字符串的某个中点将两边进行一下交换。
var reverseString = function(s) {
let flag = s.length - 1;
let mid; // 字符串的中点
if(s.length % 2 == 0) { // 数组长度为偶数
mid = s.length / 2;
} else { // 数组长度为奇数
mid = Math.floor(s.length / 2)
}
for(let i = 0; i < mid; i++) {
let temp = s[i];
s[i] = s[flag - i];
s[flag - i] = temp;
}
return s
}
在反转链表中,使用了双指针的方法。
那么反转字符串依然是使用双指针的方法,只不过对于字符串的反转,其实要比链表简单一些。
因为字符串也是一种数组,所以元素在内存中是连续分布,这就决定了反转链表和反转字符串方式上还是有所差异的。
var reverseString = function(s) {
//Do not return anything, modify s in-place instead.
reverse(s)
};
var reverse = function(s) {
let l = -1, r = s.length;
while(++l < --r) [s[l], s[r]] = [s[r], s[l]];
};
题目链接:541. 反转字符串 II - 力扣(LeetCode)
给定一个字符串
s
和一个整数k
,从字符串开头算起,每计数至2k
个字符,就反转这2k
字符中的前k
个字符。
- 如果剩余字符少于
k
个,则将剩余字符全部反转。- 如果剩余字符小于
2k
但大于或等于k
个,则反转前k
个字符,其余字符保持原样。
原本想着,遍历字符串中的每一个字母,当遍历到(i % (2 * k) == 0)
个时,就让当前字母和下一个字母交换一下位置,但是发现打印出来行不通。
原来反转部分字符串确实有些细节点要把握住。
比如有这样的一个字符串,长度为7,s='abcdefg'
,k=2
那么每四个数处理一次,最后剩余 e, f, g
直接进行反转。
刚开始去解题的时候,甚至想过单独写一个函数,实现字符串切出来的每切出来的 2k 个字符中前 k 个字符进行反转。后来发现要考虑的地方太多,而且代码比较冗余。后来参考了Carl老师的解题思路,自己去写了一下,终于AC了。
var reverseStr = function(s, k) {
let strArr = s.split('');
for(let i = 0; i < strArr.length; i += 2*k) {
let len = (i + k) > strArr.length ? strArr.length - i : k;
let left = i, right = i + len;
while(left < right) {
let temp = strArr[left];
strArr[left] = strArr[right - 1];
strArr[right - 1] = temp;
left++;
right--;
}
}
return strArr.join('')
};
注意:reverse() 方法将数组中元素的位置颠倒,并返回该数组。
首先解题思想就是双指针法,left
指向要反转的字符串最左边,right
指向要反转的字符串最右边。
for(let i = 0; i < strArr.length; i += 2*k)
解读:每次处理的字母都是 2k 的倍数去处理,因为要找的也就是每2 * k 区间的起点,所以 for 循环里面不要习惯性的写 i++ ,这里应该写 i += 2*k
。本题中就相当于每次取4个字母,让前两个位置反转一下。
(i + k) > strArr.length ? strArr.length - i : k
解读:借用( s=‘abcdefghij’ k=4)来解释一下。取出8个字母,要反转前4个。那么第一次for循环的时候, left = strArr[0],经过三元运算可知,right = [4]。自己草稿纸上写写,就能知道接下来while循环可以把前4个字母反转过来。但是到第二次for循环的时候,只剩两个字母['i', 'j']
。此时如果右边的指针还是从 i 的下标处(8)再加上4肯定会报错,所以要对 right 的位置进行处理。
strArr.length - i
即10-8=2,则len = 2,right = 10。长度为10的数组中,下标10是不存在的,所以写成strArr[right - 1]。交换的就是 strArr[8] 和 strArr[9] 的位置。
三元运算符(也称为条件运算符)可用于执行内联条件检查,而不是使用
if...else
语句。它使代码更短,更易读。它可用于根据条件为变量赋值,或根据条件执行表达式。
如果,i+k <= s.length 每次翻转,是反转 i 到 i+k
这一段
var reverseStr = function(s, k) {
const len = s.length;
let resArr = s.split("");
for(let i = 0; i < len; i += 2 * k) {
let l = i - 1, r = i + k > len ? len : i + k;
while(++l < --r) [resArr[l], resArr[r]] = [resArr[r], resArr[l]];
}
return resArr.join("");
};
题目链接:https://leetcode.cn/problems/ti-huan-kong-ge-lcof/
var replaceSpace = function(s) {
let arr = s.split(' ');
let newStr = arr.join('%20');
return newStr
};
当时考虑的比较简单,直接使用库函数就解决了问题。实际上想要在算法上面有所进步,还是得思考一下库函数底层怎么实现的。
其实很多数组填充类的问题,都可以先预先给数组扩容带填充后的大小,然后在从后向前进行操作。
有两个指针,left 指向旧长度的末尾,right 指向新长度的末尾。
var replaceSpace = function(s) {
// 字符串转为数组
const strArr = Array.from(s);
let count = 0;
// 计算空格数量
for(let i = 0; i < strArr.length; i++) {
if (strArr[i] === ' ') {
count++;
}
}
let left = strArr.length - 1;
let right = strArr.length + count * 2 - 1;
while(left >= 0) {
if (strArr[left] === ' ') {
strArr[right--] = '0';
strArr[right--] = '2';
strArr[right--] = '%';
left--;
} else {
strArr[right--] = strArr[left--];
}
}
// 数组转字符串
return strArr.join('');
};
let right = strArr.length + count * 2 - 1;
指向新长度的末尾strArr.length + count * 2
?题目链接:151. 反转字符串中的单词 - 力扣(LeetCode)
先说一下我的思路,或许可以给小伙伴们提个醒。
"We are one"
,那么就会变成['We', 'are', 'one']
var reverseWords = function (s) {
let strArr = s.split(' ');
let flag = strArr.length - 1;
let mid; // 字符串的中点
if (strArr.length % 2 == 0) { // 数组长度为偶数
mid = strArr.length / 2;
} else { // 数组长度为奇数
mid = Math.floor(strArr.length / 2)
}
for (let i = 0; i < mid; i++) {
let temp = strArr[i];
strArr[i] = strArr[flag - i];
strArr[flag - i] = temp;
}
return strArr.join(' ')
};
如果遇到" hello world "
,首尾各有两个空格,那么期望得到的是"world hello"
。
但是在代码第一步let strArr = s.split(' ');
时,得到的就是['', '', 'hello', 'world', '', '']
,只能去除中间的空格。
参考文章里面写了Array原生API,没有提到去除首尾空格的操作,所以具体怎么去除,还得看下面卡神的解法。
不要使用辅助空间,空间复杂度要求为O(1)。
将整个字符串都反转过来,那么单词的顺序指定是倒序了,只不过单词本身也倒序了,那么再把单词反转一下,单词不就正过来了。
所以解题思路如下:
举个例子,源字符串为:"the sky is blue "
var reverseWords = function(s) {
// 字符串转数组
const strArr = Array.from(s);
// 移除多余空格
removeExtraSpaces(strArr);
// 翻转
reverse(strArr, 0, strArr.length - 1);
let start = 0;
for(let i = 0; i <= strArr.length; i++) {
if (strArr[i] === ' ' || i === strArr.length) {
// 翻转单词
reverse(strArr, start, i - 1);
start = i + 1;
}
}
return strArr.join('');
};
// 删除多余空格
function removeExtraSpaces(strArr) {
let slowIndex = 0;
let fastIndex = 0;
while(fastIndex < strArr.length) {
// 移除开始位置和重复的空格
if (strArr[fastIndex] === ' ' && (fastIndex === 0 || strArr[fastIndex - 1] === ' ')) {
fastIndex++;
} else {
strArr[slowIndex++] = strArr[fastIndex++];
}
}
// 移除末尾空格
strArr.length = strArr[slowIndex - 1] === ' ' ? slowIndex - 1 : slowIndex;
}
// 翻转从 start 到 end 的字符
function reverse(strArr, start, end) {
let left = start;
let right = end;
while(left < right) {
// 交换
[strArr[left], strArr[right]] = [strArr[right], strArr[left]];
left++;
right--;
}
}
题目链接:剑指 Offer 58 - II. 左旋转字符串 - 力扣(LeetCode)
字符串的左旋转操作是把字符串前面的若干个字符转移到字符串的尾部。请定义一个函数实现字符串左旋转操作的功能。比如,输入字符串"abcdefg"和数字2,该函数将返回左旋转两位得到的结果"cdefgab"。
在下不才,目前能想到的解法就是如下:
var reverseLeftWords = function(s, n) {
let strArr = Array.from(s);
let leftTR = [];
for(let i = 0; i < n; i++) {
leftTR.push(strArr.shift())
}
// console.log(strArr, leftTR)
let comBine = [...strArr, ...leftTR]
return comBine.join('')
};
为了让本题更有意义,提升一下本题难度:不能申请额外空间,只能在本串上操作。
具体步骤为:
var reverseLeftWords = function (s, n) {
/** Utils */
function reverseWords(strArr, start, end) {
let temp;
while (start < end) {
temp = strArr[start];
strArr[start] = strArr[end];
strArr[end] = temp;
start++;
end--;
}
}
/** Main code */
let strArr = s.split('');
let length = strArr.length;
reverseWords(strArr, 0, length - 1);
reverseWords(strArr, 0, length - n - 1);
reverseWords(strArr, length - n, length - 1);
return strArr.join('');
};