面试题之:字符串
// 公用 html <input type="text" id="input" placeholder="请输入" /> <button onclick="set()">button</button> <div class="result"> 结果:<div id="result"></div> </div>
// 公用 js function set(){ let str = document.getElementById('input').value this.handle(str) } // 显示结果于界面中 function showResult(v){ ... // 结果处理,此处为了与核心代码分离,所以提取了出来 let ret = ... // 处理后得到的结果 document.getElementById('result').innerText = ret }
1、统计字符串中每个字符出现的次数
function handle(str) { // str : 需要统计的字符串 let newStr = {} // 没有重复的数据 for(let i=0; i< str.length; i++){ let key = str[i] newStr[key] = newStr[key] ? newStr[key]+1 : 1 } let max = 0 // 出现次数最多的值 for(let j in newStr){ max = newStr[j] > max ? newStr[j] : max } this.showResult(max) // 显示结果 }
2、打印出字符串中出现次数最多的字符
function handle(str){ // str : 需要统计的字符串 let newStr = {} // 没有重复的数据 for(let i=0; i< str.length; i++){ let key = str[i] newStr[key] = newStr[key] ? newStr[key]+1 : 1 } let max = 0 //出现次数最多的值 let maxArr = [] // 存储次数最多的字符 数组 for(let j in newStr){ if(newStr[j] > max) { // 大于最大次数,更新存储数组 max = newStr[j] maxArr = [] let obj = { key: j, value: max } maxArr.push(obj) }else if(newStr[j] == max){ // 考虑到可能有最多次数相等的情况 let obj = { key: j, value: max } maxArr.push(obj) } } this.showResult(maxArr)
}
3、替换字符串中某一段字符串。
如将字符串“hello, annil, nice to meet you! annil, ahh…” 中的 'annil' 替换成 ‘anderson’
// 方法一: replace() function handle(str) { let res = str.replace('annil', 'anderson') // 只会匹配第一个匹配值 // str.replace(/annil/g, 'anderson') // 匹配所有 // str.replace(new RegExp(key, 'g'), 'b') //需要替换的字符串是一个变量 this.showResult(res) } // 方法二: 不采用replace 方法实现 该方法可以看做replace的实现 function handle(str){ let newStr = str let patt = new RegExp('annil', 'g') // patt = /annil/g let ret // ret = patt.exec(str) ret = ['annil']
// patt.global = true // 全局正则
while((ret = patt.exec(str)) != null) { let arr = newStr.split(ret[0]) // arr = ['hello, ', ', nice to meet you! ', ', ahh...'] newStr = arr.join('anderson') } this.showResult(newStr) }
知识点:
RegExp对象:表示正则表达式,对字符串执行模式匹配的强大工具。
exec():检索字符串中的正则表达式的匹配。
4、找字符串中最长 / 最短的单词
function handle(str){ let arr = str.split(' ') arr.sort((a, b) => { return b.length - a.length // 返回值为正,后面的数在前面; 为负,前面的数在前面。 sort 在原数组上进行排序,不生成副本 }) this.showResult(arr[0]) }
5、输入两个字符串,从第一个字符串中删除第二个字符串中的所有字符串。不可以使用replace。
例如:输入“They are students” 和“aeiou”
则删除之后的第一个字符串变成: “Thy r stdnts”
方法一:通过遍历,依次判定第一个字符串中是否存在第二个字符串中的第 i 个字符,如果存在,则删除该字符。因此就删除而言的时间复杂度为 O(n^2)
方法二:(思路来源:https://blog.csdn.net/simpldz/article/details/52986790)
空间兑换时间的方法:
对于字符串,由于ASCII码的所有符号为256个。(标准ASCII码字符集总共的编码有128个,包括32个通用控制符,10个十进制数码,52个英文大小写字母和34个专用符号)
那么,可以声明一个数组用来代表这256个字符是否存在于第二个字符串中,如果有,则标记为1,没有则标记为0。所以,我们可以通过查询,对应ASCII值号下,数组是否为1,来判定是否删除。
第一步:
第二个字符串为“aeiou”,对应的ASCII为97,101,105,111,117,所以将数组中hashTable[97], hashTable[101], hashTable[105], hashTable[111], hashTable[117]的值设为1,其他为0。
第二步:
依次查找第一个字符串,如,第一个字符为“T”,对应ASCII码为84,因此查看hashTable[84]的值,发现为0,因此不删除,同理,以此类推。最后时间复杂度为O(n).
完整的代码:
function set(){ let targetStr = document.getElementById('input').value // 第一个字符串 let originStr = document.getElementById('originInput').value // 第二个字符串 this.handle(targetStr, originStr) } // 方法二: function handle(targetStr, originStr){ let hashTable = new Array(256) // ASCII数组 hashTable.fill(0) // fill() 方法用一个固定值填充一个数组中从起始索引到终止索引内的全部元素。不包括终止索引。 let originArr = originStr.split('') let originNewArr = Array.from(new Set(originArr))// 去重后的数组 // new Set() 生成一个Set 结构的数据: // Set(4){'a', 'e', 'i', 'o', u} // [[Entres]] // 0: 'a' // 1: 'e' // 2: 'i' // 3: 'o' // 4: 'u' // size: 4 // __proto__: Set // 需要用 Array.from() 转换成数组的格式
let originLen = originNewArr.length
for(let i = 0; i< originLen; i++){ hashTable[originNewArr[i]] = 1 } for(let j = 0; j< targetStr.length; j++){ if(hashTable[targetStr[j]] == 1){ targetStr = targetStr.split(targetStr[j]).join('') } } this.showResult(targetStr) }
时间复杂度简介:
(详情参见:https://www.jianshu.com/p/f4cca5ce055a)
执行的运算次数 : T(n) 时间复杂度: O(f(n))
定义:存在常数 c 和函数 f(n), 使得当 n >= c 时, T(n) <= f(n),表示为 T(n) = O(f(n))
int aFunc(void) { printf("Hello, World!\n"); // 需要执行 1 次 return 0; // 需要执行 1 次 } // T(n) = 1 + 1 int aFunc(int n) { for(int i = 0; i<n; i++) { // 需要执行 (n + 1) 次 printf("Hello, World!\n"); // 需要执行 n 次 } return 0; // 需要执行 1 次 } // T(n) = n+1 + n + 1 = 2n + 2
时间复杂度规则:
1、忽略常数项。T(n) = c , c 为一个常数, 时间复杂度 = O(1)
2、忽略低次项。 T(n) = n^3 + n^2 + 29,时间复杂度 = O(n^3)
3、忽略最高阶相乘的常数。 T(n) = 3n^3 ,时间复杂度 = O(n^3)
常见计算:
// 1、对于一个循环,假设循环体的时间复杂度为 O(n),循环次数为 m, 则这个循环的时间复杂度为 O(n*m) void aFunc(int n) { for(int i = 0; i < n; i++) { // 循环次数为 n printf("Hello, World!\n"); // 循环体时间复杂度为 O(1) } } // 时间复杂度 = O(n * 1) = O(n) // 2、对于多个循环,假设循环体的时间复杂度为 O(n),各个循环的循环次数分别为a,b,c,…,则这个循环的时间复杂度为 O(n*a*b*c...).分析的时候应该由里向外分析这些循环。 void aFunc(int n) { for(int i = 0; i < n; i++) { // 循环次数为 n for(int j = 0; j < n; j++) { // 循环次数为 n printf("Hello, World!\n"); // 循环体时间复杂度为 O(1) } } } // 时间复杂度 = O(n*n*1) = O(n^2) // 3、对于顺序执行的语句或者算法,总的时间复杂度等于其中最大的时间复杂度。 void aFunc(int n) { // 第一部分时间复杂度为 O(n^2) for(int i = 0; i < n; i++) { for(int j = 0; j < n; j++) { printf("Hello, World!\n"); } } // 第二部分时间复杂度为 O(n) for(int j = 0; j < n; j++) { printf("Hello, World!\n"); } } // 时间复杂度 = max(O(n^2), O(n)),即O(n^2) 4、对于条件判断语句,总的时间复杂度等于其中 时间复杂度最大的路径 的时间复杂度。 void aFunc(int n) { if (n >= 0) { // 第一条路径时间复杂度为 O(n^2) for(int i = 0; i < n; i++) { for(int j = 0; j < n; j++) { printf("输入数据大于等于零\n"); } } } else { // 第二条路径时间复杂度为 O(n) for(int j = 0; j < n; j++) { printf("输入数据小于零\n"); } } } // 时间复杂度 = max(O(n^2), O(n)),即 O(n^2)