数据结构与算法 - 字符串中的基本应用
wangjing013 opened this issue · comments
基本算法技能
反转字符串
在 JS 中反转字符串直接调用现成API即可:
function reverse() {
// 定义被反转的字符串
const str = 'wangjing'
// 定义反转后的字符串
const res = str.split('').reverse().join('')
console.log(res) // jingwang
}
判断字符串是不是回文
回文概念: 正反读的时都是一样的字符串. 如下:
'abcba'
下面我们来实现判断字符串是不是回文:
function isPalindrome(str) {
const reverseStr = reverse(str);
return reverseStr === str
}
同时我们可以利用对称性
原则来解决这个问题.
function isPalindrome(str) {
const len = str.length;
for (let i = 0; i < len / 2; i++){
if (str[i] !== str[len - i - 1]) {
return false
}
}
return true;
}
面试题
真题描述: 给定一个非空字符串 s, 最多删除一个字符. 判断是否能成为回文字符串.
示例:
// 输入
"abcdecba"
// 输出
true
// 删除 d or e
// 输入
"abeccba"
// 输出
true
// 可以删除 e 得到回文
题干中可以获取信息, 可以通过删除一个字符的方式来判断字符串是不是回文. 最简单的解决方案就是: 逐个删除, 判断删除后的字符串是不是回文.
function main() {
let str = "abcdfddcba";
let len = str.length;
if(isPalindrome(str)) {
console.log("是回文");
return;
}
for (let i = 0; i < len; i++) {
let arr = str.split('');
arr.splice(i, 1);
if (isPalindrome(arr.join())) {
console.log('是回文');
return;
}
}
console.log("不是回文");
}
function isPalindrome(str) {
const len = str.length;
for (let i = 0; i < len / 2; i++){ // n/2
if (str[i] !== str[len - i - 1]) {
return false
}
}
return true;
}
main();
上述方式实现了, 但是效率不高, 衡量效率高低通过时间复杂度, 上面的实现复杂度为 O(n^2). 有没有更高效的方式? 有
针对回文类的首先应该想到对称性
和双向指针
. 下面通过双向指针的实现方式:
下面通过代码来实现:
function validPalindrome(str) {
// 先保存两个指针,分别是头指针、尾指针
let start = 0;
let end = str.length - 1;
// 第一个条件表示循环终止条件,防止指针碰撞
// 第二个为当只要出现不等时也应该终止
while (start < end && str[start] === str[end]) {
start++;
end--;
}
// 左指针移动
if (isPalindrome(start++, end)) {
return true;
}
// 右指针移动
if (isPalindrome(start, end--)) {
return true;
}
function isPalindrome(st, ed) {
while (st < ed) {
if (str[st] != str[ed]) {
return false;
}
st++;
end--;
}
return true;
}
return false;
}
let bool = validPalindrome("abcdfefgdcba");
console.log(bool)
真题描述: 计一个支持以下两种操作的数据结构
void addWord(word){...}
bool search(word){...}
其中查询时支持搜索普通字符串或正则表达式字符串, 字符串只包含字母 .
或 a-z
. .
可以表示任何一个字母.
从题目中应该考虑几个问题:
- 数据如何存储
- 如何区分普通字符串或正则表达式字符串
通常存储数据考虑用 数组、Map 等. 在这道题中通过 Map 来实现, 至于为什么通过实现后再来看这个问题.
如何区分普通字符串或正则表达式? 判断 word
是否包含 .
字符
下面我们通过代码来实现:
const WordDictionary = function() {
// 主要用来储存单词
this.words = {}
}
WordDictionary.prototype.addWord = function(word) {
// 边界检查
if (!word) { return; }
const len = word.length;
// 先判断数组中有没有存在相同长度的
if (!this.words[len]) {
this.words[len] = [word]
} else {
this.words[len].push(word);
}
}
WordDictionary.prototype.search = function(word) {
if (!this.words[word.length]) {
return;
}
// 缓存目标元素的长度
const len = word.length;
// word 是不是包含点"."(区分普通字符串和正则字符串)
if (!word.containes('.')) {
return this.words[len].includes(word);
}
// 创建正则表达式
const regex = new Regex(word);
return this.words[len].some((item) => regex.test(item))
}
回到最开始选用数据结构的问题, 为什么用 Map ? 在储存时我们根据 length
作为Map 的 key
, 在搜索时可以有效的缩小查找的范围,从而提高其查找的效率.
真题描述:请你来实现一个 atoi 函数,使其能将字符串转换成整数.
atoi 函数有什么特性:
atoi 函数
会跳过前面的空白字符.- 如果输入的字符串为空 或 不能转换为整数时则直接返回 0.
atoi
返回整数有大小限制, 也就是在整数范围之内, 否则也会返回 0.
其实特性就是代码文字描述, 下面通过代码来实现:
function atoi(str) {
// 主要通过正则来实现的, 主要匹配到分组中表达式内容就是我们要到内容
const regex = /^\s*([+\--]?\d*).*/;
const groups = str.match(regex);
// 获取分组表达式中内容
let result = 0;
if (groups) {
result = +groups[1];
if (isNaN(result)) {
result = 0;
}
}
// 范围判断
const max = Math.pow(2, 31) - 1;
const min = - max - 1;
if (result < min || result > max) {
return 0;
}
// 返回结果
return result;
}
console.log(atoi(' 58')); //=> 58
console.log(atoi(' abcded58')); //=> 0
总结
- 什么是回文? 字符串正序和逆序读取内容相等
- 判断回文几种方式
- 字符串反转
- 对称性
通过前面题目来看, 解决回文类似题目我们首先应该想到对称性
和双指针
方法来实现.