算法题六:字符串中连续最多的字符,以及次数
如:输入‘abbcccddeeee1234’,计算得到:连续最多的字符是e,4次
传统思路:嵌套循环,找出每个字符的连接次数,并记录,看似简单,时间复杂度是O(n^2)
真实的时间复杂度是O(n),因为有跳步
function findFun(str) {
var res = {
char: "",
length: 0
}
const length = str.length
if (length === 0) return res
for (var i = 0; i < length; i++) {
let temLength = 0
for (j = i; j < length; j++) {
if (str[i] === str[j]) {
temLength++
}
if (str[i] !== str[j]) {
if (temLength > res.length) {
res.char = str[i]
res.length = temLength
}
if (i < length - 1) {
i = j - 1//跳步
}
break
}
}
}
return res
}
console.log(findFun('abbcccddeeee1234'))//{char:'e',length:4}
优化思路:双指针
定义指针i和j。j不动,i继续移动
如果i和j的值一直相等,则i继续移动
直到i和j的值不相等,记录处理,让j追上i。继续上一步
function findfun2(str) {
var res = {
char: "",
length: 0
}
const length = str.length
if (length === 0) return res
let i = 0
let j = 0
let temLength = 0
for (; i < length; i++) {
if (str[i] === str[j]) {
temLength++
}
if (str[i] !== str[j] || i === length - 1) {
if (temLength > res.length) {
res.char = str[j]
res.length = temLength
}
temLength = 0
if (i < length - 1) {
j = i
i--
}
}
}
return res
}
console.log(findfun2('abbcccddeeee1234'))//{char:'e',length:4}
正则表达式—效率非常低,慎用
累计各个元素的连续长度,最后求最大值,徒增空间复杂度
注意:算法题尽量用“低级”代码,慎用语法糖或者高级api
算法题七: 用js实现快速排序,并且说明时间复杂度
固定算法,固定思路
找到中间位置midValue
遍历数组,小于midValue放在left,否则放在right
继续递归,最后concat拼接,返回
获取midValue方式:
1、使用splice,会修改原数组
2、使用slice,不会修改原数组—推荐使用
//splice
function quickSort1(arr){
const length = arr.length
if(length===0)return arr
const midIndex = Math.floor(length/2)
const midValue = arr.splice(midIndex,1)[0]
const left = []
const right = []
for(let i = 0;i<arr.length;i++){
if(arr[i]<midValue){
left.push(arr[i])
}else if(arr[i]>midValue){
right.push(arr[i])
}
}
return quickSort1(left).concat([midValue],quickSort1(right))
}
console.log(quickSort1([2,8,4,5,6,9,1,3,7]))//1,2,3,4,5,6,7,8,9
//slice
function quickSort2(arr) {
const length = arr.length
if (length === 0) return arr
const midIndex = Math.floor(length / 2)
const midValue = arr.slice(midIndex, midIndex + 1)[0]
const left = []
const right = []
for (let i = 0; i < length; i++) {
if (i !== midIndex) {
if (arr[i] < midValue) {
left.push(arr[i])
} else if (arr[i] > midValue) {
right.push(arr[i])
}
}
}
return quickSort1(left).concat([midValue], quickSort1(right))
}
console.log(quickSort2([2, 8, 4, 5, 6, 9, 1, 3, 7]))
有遍历,有二分----时间复杂度:O(nlogn)或O(nlogn)
常规排序,嵌套循环,复杂度是O(n^2)
splice 和 slice 没有区分出来
算法本身的复杂度就够高O(nlogn),外加上,splice是逐步二分之后执行的,二分会快速消减数量级
如果单独比较splice和slice,效果会很明显
算法题八:求对称数
求1-10000之间的所有对称数(回文)
例如:0,1,2,11,22,101,232,1221…
思路一:使用数组翻转、比较
数字转换为字符串,再转换为数组,数组reverse,再join为字符串,前后字符串进行对比
function findPalindromeNumbers1(max){
const res = []
if(max<=0)return res
for(var i=0;i<=max;i++){
var s = i.toString()
if(s === s.split('').reverse().join('')){
res.push(i)
}
}
return res
}
console.info(findPalindromeNumbers1(200)) //[0,1,2,3,4,5,6,7,8,9,11,22,33,44,55,66,77,88,99,101,111,121, 131,141,151, 161, 171,181,191]
思路2-字符串头尾比较
数字转换为字符串,字符串头尾字符比较(也可以用栈,像括号匹配,但是要注意奇偶数)
function findPalindromeNumbers2(max) {
const res = []
if (max <= 0) return res
for (let i = 0; i <= max; i++) {
let s = i.toString()
let length = s.length
let startIndex = 0
let endIndex = length - 1
var flag = true
while (startIndex < endIndex) {
if (s[startIndex] !== s[endIndex]) {
flag = false
break
} else {
startIndex++
endIndex--
}
}
if (flag) {
res.push(i)
}
}
return res
}
console.info(findPalindromeNumbers2(200)) //[0,1,2,3,4,5,6,7,8,9,11,22,33,44,55,66,77,88,99,101,111,121, 131,141,151, 161, 171,181,191]
思路3-生成翻转数
使用%和Math.floor生成反转数
前后数字进行对比(看起来比较复杂)
性能分析
思路1-看似是O(n),但数组转换、操作都需要时间,所以慢
思路2vs思路3,操作数字更快(电脑原型就是计算器)
思路2要用栈,不合适。因为栈也一般用数组实现,比较慢
注意:
1、尽量不用转换数据结构,尤其数组这种有序结构
2、尽量不要用内置API,如reverse,不好识别复杂度
3、数字操作最快,其次是字符串
算法题九:高效的字符串前缀匹配
有一个英文单词库(数组),里面有几十万个英文单词
输入一个字符串,快速判断是不是某个单词的前缀
思路:
英文字母一共26个,可以提前把单词库数组拆分为26个
既然第一层拆分为26个,第二层、第三层,还可以继续拆分
最后把单词库拆分为一棵树
性能分析:
如遍历数组,时间复杂度至少O(n)起步(n是数组长度)
而改为树,时间复杂度降低到 O(m) (m 是单词的长度)
Ps:哈希表(对象)通过key 查询,时间复度是O(1)
注意:
考虑优化原始数据结构(需和面试官沟通确认)
有明确范围的数据(如26个英文字母),考虑使用哈希表(对象)
算法题十:数字千分位格式化
将数字千分位格式化,输出字符串
如输入数字12050100,输出字符串12,050,100
常见思路:转换为数组,reverse,每3位拆分
使用正则表达式
使用字符串进行拆分
//使用数组
function format1(n){
n = Math.floor(n)//只考虑整数
const s = n.toString()
const arr = s.split('').reverse()
console.log(arr)
return arr.reduce((pre,val,index)=>{
if(index%3===0){
if(pre){
return val+','+ pre
}else{
return val
}
}else{
return val+ pre
}
},'')
}
console.log(format1(243244500))//243,244,500
function format2(n) {
n = Math.floor(n)//只考虑整数
const s = n.toString()
const length = s.length
let res = ""
for (var i = length-1; i >= 0; i--) {//i=6 i=5
let j = length - i//j=1 j=2
if (j % 3 === 0) {
if (i === 0) {
res = s[i] + res
} else {
res = "," + s[i] + res
}
} else {
res = s[i] + res
}
}
return res
}
console.log(format2(1023040))//1,023,040
性能分析
使用数组,转换影响性能
使用正则表达式,性能较差
使用字符串,性能较好-推荐
算法题十一:切换字母大小写
输入一个字符串,切换其中字母的大小写
如,输入字符串传12aBc34,输入字符串12ABC34
常见思路:正则表达式,
通过ASCII编码
function switchLetterCase1(str){
let length = str.length
const reg1 = /[a-z]/
const reg2 = /[A-Z]/
let res = ''
for(var i=0;i<length;i++){
let c = str[i]
if(reg1.test(c)){
res+=c.toUpperCase()
}else if(reg2.test(c)){
res+=c.toLowerCase()
}else{
res+=c
}
}
return res
}
console.log(switchLetterCase1('Aan2132/3Uye'))//aAN2132/3uYE
function switchLetterCase2(str){
let length = str.length
let res = ''
for(var i=0;i<length;i++){
let c = str[i]
let code = c.charCodeAt(0)
if(code>=65&&code<=90){
res+=c.toLowerCase()
}else if(code>=97&&code<=122){
res+=c.toUpperCase()
}else{
res+=c
}
}
return res
}
console.log(switchLetterCase2('Aan2132/3Uye'))//aAN2132/3uYE
0.1+0.2!==0.3
计算机使用二进制存储数据
整数转换二进制没有误差,如9转换为二进制是1001
而小数可能无法用二进制准确表达,如0.2转换为1.1001100…