前言
字符串反转是关于字符串算法里的重要问题,虽然不是太难,但需要考虑到一些边界问题。本篇文章就对几道字符串反转题目进行分析。
1.反转字符串
力扣344题,编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 s
的形式给出。不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1)
的额外空间解决这一问题。
分析:这是最基础的字符串反转问题,我们除了可以用语言内置函数解决(面试时基本不会让用),还可以采用双指针的办法解决,算法步骤如下:
- 设置
left = 0, right = strArray.length - 1
- 当
left < right
时:- 交换
strArray[left]
和strArray[right]
left
右移一位,right
左移一位
- 交换
- 当
left >= right
时,反转完成,对反转后的字符串数组进行拼接strArray.join('')
,最后返回拼接后的字符串
本题代码如下:
/*** @param {character[]} s* @return {void}* */
function reverseStr(s) {// 特判if (s === null || s.length === 0) {return s;}// 双指针交换let left = 0, right = s.length - 1;while (left <= right) {[s[left], s[right]] = [s[right], s[left]];left++;right--;}
}
注意:如果实际给你的是字符串而不是这道题给的字符串数组,就需要利用JS的
Array.from(s)
将字符串转换为字符串数组
2.每2k个字符就反转前k个字符
力扣541题,给定一个字符串 s 和一个整数 k,从字符串开头算起,每计数至 2k 个字符,就反转这 2k 字符中的前 k 个字符。
- 如果剩余字符少于 k 个,则将剩余字符全部反转。
- 如果剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符,其余字符保持原样。
分析:这道题没有什么好说的,就是让每2k个字符就反转前k个字符。记得先把字符串转换为字符串数组,如果字符串长度不足k,就反转整个字符串。
代码如下:
// 每2k个字符就反转前k个字符
function reverseStr(s, k) {if (s === null || s.length === 0) {return s;}// 转换为字符串数组const strArray = Array.from(s);const lengthStr = strArray.length;for (let i = 0; i < lengthStr; i += 2*k) {// 字符串长度不足k,就反转整个字符串reverse(strArray, i, Math.min(i + k, lengthStr) - 1);}// 拼接反转后的字符串return strArray.join('');
}/*** 反转字符函数* */
function reverse(strArray, left, right) {while (left < right) {[strArray[left], strArray[right]] = [strArray[right], strArray[left]];left++;right--;}
}
3.仅仅反转字母
力扣917题,给定一个字符串 S
,返回 “反转后的” 字符串,其中不是字母的字符都保留在原地,而所有字母的位置发生反转。
分析:首先想到的就是利用双指针和字母对应的ASCII码判断来实现:
- 字符串转为字符串数组,
left = 0
,right = Array.length - 1
; - 当
left < right
时- 如果两个指针指向的都是字母,就交换位置
- 如果右指针指向的是字母,左指针不是,则
left++
- 同样如果左指针指向的是字母,右指针不是,则
right--
- 如果两个指针指向的都不是字母,则
left++,right--
- 拼接反转后的字符串并返回
代码如下:
// 首先想到利用双指针实现
function reverseOnlyLetters(s) {// 特判if (s === null || s.length === 0) {return s;}// 得到字符串数组const strArray = Array.from(s);const lengthStr = s.length;let left = 0, right = lengthStr - 1;while (left < right) {// 两个指针的指向都是字符串if (isLetter(s, left) && isLetter(s, right)) {[strArray[left], strArray[right]] = [strArray[right], strArray[left]];left++;right--;}// 一个指向的是字母,一个不是else if (!isLetter(s, left)) {left++;}else if (!isLetter(s, right)) {right--;}// 两个指针指向的都不是字母else {left++;right--;}}return strArray.join('');
}// 判断当前字符是否是字母
function isLetter(s, index) {if (s.charCodeAt(index) >= 65 && s.charCodeAt(index) <= 90 || s.charCodeAt(index) >= 97 && s.charCodeAt(index) <= 122) {return true;}return false;
}
4.反转字符串里的单词
力扣151题,给你一个字符串 s ,逐个反转字符串中的所有 单词 。单词是由非空格字符组成的字符串。s 中使用至少一个空格将字符串中的 单词 分隔开。请你返回一个反转 s 中单词顺序并用单个空格相连的字符串。
说明:
- 输入字符串 s 可以在前面、后面或者单词间包含多余的空格。
- 反转后单词间应当仅用一个空格分隔。
- 反转后的字符串中不应包含额外的空格。
分析:本题同样可以用语言内置函数来实现,且比较简单,实际应用当中可以,但为了我们加深我们对数据结构的认知,最好还是自己实现一遍。思路大概都是一致的:
- 将字符串转换为字符串数组,去除字符串里多余的空格
- 反转整个字符串,此时单词字母也是逆序
- 再反转每个单词
本题代码如下:
// 使用内置函数
function reverseWords(s) {// 去除开头和结尾的空格s = s.trim();// 按空格分割字符串,匹配所有空格const reg = /\s+/; const words = s.split(reg);words.reverse();return words.join(' ');
}/*---------------------------------------*/// 不使用内置函数,自己实现
function reverseWords(s) {// 字符串转数组const strArray = Array.from(s);// 得到去除了多余空格后的字符串数组const trimedStrArray = trimSpaces(strArray);// 得到反转字符串数组reverse(trimedStrArray, 0, trimedStrArray.length - 1 );// 对反转字符串数组的每个单词进行反转const res = reverseEachWord(trimedStrArray);return res.join('');
}/*** 去除字符串数组多余空格* */
function trimSpaces(strArray) {let left = 0, right = 0, arrLength = strArray.length;while (left < arrLength) {// 移除开始位置和重复的空格if (strArray[left] === ' ' && (left === 0 || strArray[left - 1] === ' ')) {left++;} else {strArray[right++] = strArray[left++];}}// 移除末尾空格strArray.length = (strArray[right - 1] === ' ' ? right - 1 : right);return strArray;
}/*** 反转每个单词* */
// 双指针
function reverseEachWord(strArray) {const lengthOfStrArray = strArray.length;let start = 0, end = 0;while (start < lengthOfStrArray) {// 找到一个单词的末尾while (end < lengthOfStrArray && strArray[end] !== ' ') {end++;}// 反转单词,更新start的位置reverse(strArray, start, end - 1);start = end + 1;end++;}return strArray;
}/*** 反转字符串数组* */
function reverse(strArray, start, end) {let left = start, right = end;while (left < right) {[strArray[left], strArray[right]] = [strArray[right], strArray[left]];left++;right--;}
}