代码随想录算法训练营第八天 | LeetCode,剑指Offer
344. 反转字符串
let reverseString = function (s) {
const len = s.length
// if (len === 0) return ""
let left = 0, right = len - 1
while (left < right) {
[s[left], s[right]] = [s[right], s[left]]
left++
right--
}
return s
}
方法是使用左右双指针,一个在数组头一个在数组尾,向中间对称地交换值。
输入为字符数组,就不用将s转换为数组了,否则要转。
错误与反思
while(++left < --right)
不建议这么写,坑多while (left < right)
写成了for。这里不是Go![s[left], s[right]] = [s[right], s[left]]
为ES6的交换两个值的新方式解构赋值,即[a, b] = [b, a]
541. 反转字符串 II
let reverseStr = function (s, k) {
const len = s.length
const resArr = s.split("")
for (let i = 0; i < len; i += 2 * k) {
let left = i
// 细节:反转前k个字符,i要加k-1(距离)
let right = i + k - 1 > len ? len : i + k - 1
reverse(resArr, left, right)
}
return resArr.join("")
}
function reverse(arr, left, right) {
while (left < right) {
[arr[left], arr[right]] = [arr[right], arr[left]]
left++
right--
}
return arr
}
将字符串转换为字符数组的目的是方便对字符串的每个字符进行操作。字符串是不可变的,一旦创建就无法修改,而字符数组是可变的,我们可以直接对其中的元素进行操作,比如交换位置、修改值等。
在这段代码中,我们需要反转字符串中每个长度为 k 的块的前 k 个字符。如果直接操作字符串,需要用到字符串的 subString 和 splice 方法,这样写起来比较麻烦。而将字符串转换为字符数组之后,我们可以直接通过索引来访问和修改其中的元素,简化了操作的过程。
剑指 Offer 05. 替换空格
/**
* @param {string} s
* @return {string}
*/
let replaceSpace = function (s) {
// 将字符串转换为数组
const arr = Array.from(s)
let len = arr.length
let count = 0
for (const char of arr) {
if (char === ' ') {
count++
}
}
// arr.length = len + count
不用填充,后面都是undefined
// for (let i = len; i < len + count; i++) {
// arr[i] = ' '
// }
let left = len - 1
let right = len + count * 2 - 1 // 每个count是要多腾出3-1=2个位置
// left >= 0是保证从后向前遍历到index = 0位置
// 也可以写 left < right, 当left == right的时候说明它俩的差值(count) 已经被“抵消”了 也就是完成了
while (left >= 0) {
if (arr[left] !== ' ') {
arr[right] = arr[left]
left--
right--
} else {
arr[right--] = '0'
arr[right--] = '2'
arr[right--] = '%'
left--
}
}
// 返回的是字符串类型,元素间join连接的字符为空字符
return arr.join('')
}
console.log(replaceSpace("I love you"))
语法拾遗
JavaScript数组越界访问不会出错,是因为JavaScript数组是一种特殊对象,可以将数组看成以下对象结构。
let arr = {
0: 'v1',
1: 'v2'
}
console.log(arr[0]) // v1
console.log(arr[2]) // undefined
console.log(arr[2][0]) // 报错,因为不存在第undefined个的第0个arr
151. 反转字符串中的单词
let reverseWords = function (s) {
// 修剪掉字符串首尾的空格并转换为数组
const arr = Array.from(s.trim())
// 原地去掉字符数组中间的多余空格
const len = arr.length
let cur = 0
// 因为原地修改数组的话后半段会乱掉,所以建立新数组放入去掉连续空格后的元素
let newArr = []
while (cur < len) {
if (arr[cur] === ' ' && arr[cur + 1] === ' ') {
cur++
} else {
newArr.push(arr[cur++])
}
}
// 全部反转
newArr.reverse()
// 反转每一个单词
let left = 0, right = 0
while (right < len) {
if (newArr[right] === ' ') {
// 这里传的必须是right - 1 因为不包括空格
reverse(newArr, left, right - 1)
// right先自增 相当于left等于原来的right + 1
left = ++right
} else {
right++
}
}
return newArr.join('')
}
// 注意这个函数是把区间[left, right]内的值反转
function reverse(arr, left, right) {
while (left < right) {
[arr[left], arr[right]] = [arr[right], arr[left]]
left++
right--
}
return arr
}
/**
* @param {string} s
* @return {string}
*/
let reverseWords = function (s) {
// 修剪掉字符串首尾的空格并转换为数组
const arr = Array.from(s.trim())
// 原地去掉字符数组中间的多余空格
const len = arr.length
let cur = 0
// 因为原地修改数组的话后半段会乱掉,所以建立新数组放入去掉连续空格后的元素
let newArr = []
while (cur < len) {
if (arr[cur] === ' ' && arr[cur + 1] === ' ') {
cur++
} else {
newArr.push(arr[cur++])
}
}
// 全部反转
newArr.reverse()
// 反转每一个单词
let left = 0, right = 0
while (right < len) {
if (newArr[right + 1] === ' ' || newArr[right + 1] === undefined) {
// 这里传的是left到right因为不包括空格
reverse(newArr, left, right)
// right先自增 相当于left等于原来的right + 1
right++
left = right + 1
} else {
right++
}
}
return newArr.join('')
}
// 把区间[left, right]内的值反转
function reverse(arr, left, right) {
while (left < right) {
[arr[left], arr[right]] = [arr[right], arr[left]]
left++
right--
}
return arr
}
注意特殊位置(如起始条件)
剑指 Offer 58 - II. 左旋转字符串
利用JS数组的push和unshift方法解决
/**
* @param {string} s
* @param {number} n
* @return {string}
*/
let reverseLeftWords = function (s, n) {
const arr = Array.from(s)
const result = []
for (let i = 0; i < n; i++) {
result.push(arr[i])
}
// 这里是i >= n 容易写错
for (let i = arr.length - 1; i >= n ; i--){
result.unshift(arr[i])
}
return result.join('')
}