算法题目
一、将一个数组旋转K步
1.输入一个数组[1,2,3,4,5,6,7]
2.k = 3,即旋转3步
3.输入[5,6,7,1,2,3,4]
思路:
a.把未尾的元素挨个pop,然后unshift道数组前面(时间复杂度O(n^2),空间复杂度O(1))
function rotate(arr, k) {
const len = arr.length
if(!k || len === 0) return arr
const step = Math.abs(k % len)
for (let i = 0; i < step; i++) {
const n = arr.pop()
if (n) {
arr.unshift(n)
}
}
return arr
}
console.log(rotate([1,2,3,4,5,6,7], 3))
推荐:
b.把数组拆分,最后concat拼接到一起(时间复杂度O(1),空间复杂度O(n))
function rotate(arr, k) {
const len = arr.length
if(!k || len === 0) return arr
const step = Math.abs(k % len)
const part1 = arr.slice(-step)
const part2 = arr.slice(0, len-step)
const res = part1.concat(part2)
return res
}
console.log(rotate([1,2,3,4,5,6,7], 3))
二、判断字符串是否括号匹配
1.一个字符串s可能包含{}()[]三种括号
2.判断s是否是括号匹配的
3.如(a{b}c)匹配,而{a(b或{a(b}c)}就不匹配
思路:
a.遇到左括号{([就压栈
b.遇到右括号})]就判断栈顶,匹配则出栈
c.最后判断length是不是为0
function isMatch(left, right) {
if (left === '{' && right === '}') return true
if (left === '[' && right === ']') return true
if (left === '(' && right === ')') return true
return false
}
function matchBracket(str) {
const len = str.length
if (len === 0) return true
const stack = []
const leftSymbols = '{[('
const rightSymbols = '}])'
for (let i = 0
const s = str[i]
if (leftSymbols.includes(s)) {
// 左括号,压栈
stack.push(s)
} else if (rightSymbols.includes(s)) {
// 右括号,判断栈顶(是否出栈)
const top = stack[stack.length - 1]
if (isMatch(top, s)) {
stack.pop()
} else {
return false
}
}
}
return stack.length === 0
}
console.log(matchBracket('{a(b[c])}'))
三、用JS实现快速排序
思路:
1.找到中间位置midValue
2.遍历数组,小于midValue放在left,否则放在right
3.继续递归,最后concat拼接,返回
细节:
1.获取midValue的两种方式
2.使用splice,会修改原数组
3.使用slice,不会修改原数组--更加推荐
4.math.floor 返回小于等于x的最大整数Math.floor(1.6) = 1
推荐:
function quilckSort(arr) {
const len = arr.length
if(len === 0) return arr
const midIndex = Math.floor(len / 2)
const midValue = arr.splice(midIndex, 1)[0]
const left = []
const right = []
for (let i = 0; i < arr.length; i++) {
const n = arr[i]
if (n < midValue) {
left.push(n)
} else {
right.push(n)
}
}
return quilckSort(left).concat([midValue], quilckSort(right))
}
function quilckSort(arr) {
const len = arr.length
if (len === 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 < len; i++) {
if (i !== midIndex) {
const n = arr[i]
if (n < midValue) {
left.push(n)
} else {
right.push(n)
}
}
}
return quilckSort(left).concat([midValue],quilckSort(right))
}
console.log(quilckSort([1,3,6,4,8,5]))
四、用JS实现二分查找
思路:
a.递归-代码逻辑更加清晰
function binarySearch(arr, target, startIndex, endIndex) {
const len = arr.length
if (len === 0) return -1
if (startIndex == null) startIndex = 0
if (endIndex == null) endIndex = len - 1
if (startIndex > endIndex) return - 1
const midIndex = Math.floor((startIndex + endIndex) / 2)
const midValue = arr[midIndex]
if (target < midValue) {
return binarySearch(arr, target, startIndex, midIndex - 1)
} else if (target > midValue) {
return binarySearch(arr, target, midIndex + 1, endIndex)
} else {
return midIndex
}
}
console.log(binarySearch([1,2,3,4,5,6,19,30,50], 1))
推荐
b.非递归-性能更好
function binarySearch(arr, target) {
const len = arr.length
if (len === 0) return -1
let startIndex = 0
let endIndex = len - 1
while (startIndex <= endIndex) {
const midIndex = Math.floor((startIndex+endIndex) / 2)
const midValue = arr[midIndex]
if (target < midValue) {
endIndex = midIndex - 1
} else if (target > midValue) {
startIndex = midIndex + 1
} else {
return midIndex
}
}
return -1
}
console.log(binarySearch([1,2,3,4,5,6,19,30,50], 1))
五、给一个数组,找出其中和为n的两个元素
1.有一个递增的数组[1,2,4,7,11,15]和一个n=15
2.数组中有两个数,和是n。即4 + 11 === 15
思路:
1.嵌套循环,找到一个数,然后去遍历下一个数,求和,判断
function findTowNumbers(arr, n) {
const res = []
const len = arr.length
if (len === 0) return res
for (let i = 0
const n1 = arr[i]
let flag = false // 是否得到了结果
for (let j = i + 1
const n2 = arr[j]
if (n1 + n2 === n) {
res.push(n1)
res.push(n2)
flag = true
break
}
}
if (flag) break
}
return res
}
console.log(findTowNumbers([1,2,4,7,11,15], 15))
推荐:
2.利用递增(有序)的特性
a.随便找两个数
b.如果和大于n,则需要向前寻找
c.如果和小于n,则需要向后寻找---二分法
function findTowNumbers(arr, n) {
const res = []
const len = arr.length
if (len === 0) return res
let i = 0 // 头
let j = len - 1 //尾
while (i < j) {
const n1 = arr[i]
const n2 = arr[j]
const sum = n1 + n2
if (sum > n) {
// sum大于n,则j要向前移动
j--
} else if (sum < n) {
// sum小于n,则i要向后移动
i++
} else {
// 相等
res.push(n1)
res.push(n2)
break
}
}
return res
}
console.log(findTowNumbers([1,2,4,7,11,15], 15))
六、斐波那契数列 '0 1 1 2 3 5 8 13 21 34'
1.用JS计算斐波那契数列的第n个值
f(0) = 0
f(1) = 1
f(n) = f(n - 1) + f(n - 2)
思想:
1.递归 - 大量的重复计算
function fibonacci(n) {
if (n <= 0) return 0
if (n === 1) return 1
return fibonacci(n - 1) + fibonacci(n - 2)
}
console.log(fibonacci(9))
推荐
2.不用递归,用循环,记录中间结果
function fibonacci(n) {
if (n <= 0) return 0
if (n === 1) return 1
let n1 = 1
let n2 = 0
let res = 0
for (let i = 2; i <= n; i++) {
res = n1 + n2
n2 = n1
n1 = res
}
return res
}
console.log(fibonacci(3))
青蛙跳台阶
一只青蛙,一次可跳1级,也可跳2级
问:青蛙跳到n级台阶,总共有多少种方式
思路:
1.要跳到1级台阶,就1种方式f(1) = 1
2.要跳到1级台阶,就2种方式f(2) = 2
1.要跳到n级台阶,f(n) = f(n-1) + f(n-2)
七、将数组中的0移动到未尾
1.如输入[1,0,3,0,11,0],输入[1,3,11,0,0,0]
2.只移动0,其他顺序不变
3.必须在原数组进行操作
思路:
1.遍历
1.遍历数组,遇到0则push到数组未尾
2.用splice截取掉当前元素
function moveZero(arr) {
const len = arr.length
if (len === 0) return
let zeroLen = 0
for (let i = 0
if (arr[i] === 0) {
arr.push(0)
arr.splice(i, 1) // 本身就有O(n)
i-- // 数组截取了一个元素,i要递减,否则连续0就会有错误
zeroLen++
}
}
return arr
}
console.log(moveZero([1,22,0,0,4,0,5,0]))
2.双指针
1.定义j指向第一个0,i指向j后面的第一个非0
2.交换i和j的值,继续向后移动
function moveZero(arr) {
const len = arr.length
if (len === 0) return
let i
let j = -1 // 指向第一个0
for (i = 0
if (arr[i] === 0) {
// 第一个0
if (j < 0) {
j = i
}
}
if (arr[i] !== 0 && j >= 0) {
// 交换
const n = arr[i]
arr[i] = arr[j]
arr[j] = n
j++
}
}
return arr
}
console.log(moveZero([0,0,0,2,3,4,0,4,0,6]))
八、字符串中连续最多的字符,以及次数
1.如,输入'abbcccddeeee1234',计算得到
2.连续最多的字符是'e', 4次
思路:
1.嵌套循环,找出每个字符的连续次数,并记录
function findContinuousChar(str) {
const res = {
char: '',
len: 0
}
const len = str.length
if (len === 0) return res
let tempLen = 0 // 临时记录当前连续字符的长度
for (let i = 0
tempLen = 0 // 重置
for (let j = i
if (str[i] === str[j]) {
tempLen++
}
if (str[i] !== str[j] || j === len - 1) {
// 不相等,或者已经到了最后一个元素,要去判断最大值
if (tempLen > res.len) {
res.char = str[i]
res.len = tempLen
}
if(i < len - 1) {
i = j - 1 //跳步
}
break
}
}
}
return res
}
console.log(findContinuousChar('abbcccddeee1234'))
推荐
2.双指针
1.定义指针i和j,j不动,i继续移动
2.如果i和j的值一直相等,则i继续移动
3.直到i和j的值不想等,记录处理,让j追上i,继续第一步
function findContinuousChar(str) {
const res = {
char: '',
len: 0
}
const len = str.length
if (len === 0) return res
let tempLen = 0 // 临时记录当前连续字符的长度
let j = 0
for (let i = 0
if (str[i] === str[j]){
tempLen++
}
if (str[i] !== str[j] || i === len - 1) {
// 不想等,或者i到了字符串的未尾
if (tempLen > res.len) {
res.char = str[j]
res.len = tempLen
}
tempLen = 0 // reset
if (i < len - 1) {
j = i // 让j追求i
i-- // 细节
}
}
}
return res
}
console.log(findContinuousChar('abbcccddee1234'))
九、求1-10000之间的所有对称数(回文)
例如:0,1,2,11,22,101,232,1221..
思路:
1.数字转换为字符串,再转换为数组
2.数组reverse,再join为字符串
3.前后字符串进行对比
function findPalidromeNumbers(n) {
const res = []
if (n <= 0) return res
for (let i = 1
// 转换为字符串,转换为数组,再反转,比较
const s = i.toString()
if (s === s.split('').reverse().join('')){
res.push(i)
}
}
return res
}
console.log(findPalidromeNumbers(20))
字符串头尾比较
1.数字转换为字符串
2.字符串头尾字符比较
3.也可以用栈,像括号匹配,但要注意奇偶数
function findPalidromeString(str) {
const res = []
if (str <= 0) return res
for (let i = 0
const s = i.toString()
const len = s.length
// 字符串头尾比较
let flag = true
let startIndex = 0 // 字符串开始
let endIndex = len - 1 // 字符串结束
while (startIndex < endIndex) {
if(s[startIndex] !== s[endIndex]) {
flag = false
break
} else {
// 继续比较
startIndex++
endIndex--
}
}
if (flag) res.push(i)
}
return res
}
console.log(findPalidromeString(200))
生成翻转数
1.使用%和Math.floow生成反转数
2.前后数字进行对比
function findPalidromeString(n) {
const res = []
if (n <= 0) return res
for (let i = 1
let n = i
let rev = 0 // 存储翻转数
// 生成翻转数
while (n > 0) {
rev = rev * 10 + n % 10
n = Math.floor(n / 10)
}
if (i == rev) res.push(i)
}
return res
}
console.log(findPalidromeString(200))
十、数字千分位格式化
1.将数字千分位格式化,输出字符串
2.如输入数字12050100,输出字符串12,050,100
3.注意:逆序判断
思路:
1.转换为数组,reverse,每3位拆分
function format(n) {
s = Math.floor(n)
const arr = s.split('').reverse()
return arr.reduce((prev, val, index) => {
if (index % 3 === 0) {
if(prev) {
return val + ',' + prev
} else {
return val
}
} else {
return val + prev
}
}, '')
}
2.使用正则表达式
function format (num) {
const reg=/\d{1,3}(?=(\d{3})+$)/g;
return (num + '').replace(reg, '$&,');
}
3.使用字符串拆分
const n = 120501000
function format(n) {
n = Math.floor(n)
let s = n.toString()
let res = ''
let len = s.length
for (let i = len - 1; i >= 0; i--) {
let j = len - i
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(format(n), 'format(n)')
十一、切换字母大小写
1.输入一个字符串,切换其中字母的大小写
2.如输入字符串12aBc34,输入字符串12AbC34
思路:
1.正则表达式
function switchLetterCase(s) {
let res = ''
const len = s.length
if (len === 0) return res
const reg1 = /[a-z]/
const reg2 = /[A-Z]/
for (let i = 0; i < len; i++) {
const n = s[i]
if (reg1.test(n)) {
res += n.toUpperCase()
} else if (reg2.test(n)) {
res += n.toLowerCase()
} else {
res += n
}
}
return res
}
console.log(switchLetterCase('12aBc34'))
2.通过ASCII码判断
function switchLetterCase(s) {
let res = ''
const len = s.length
if (len === 0) return res
for (let i = 0; i < len; i++) {
const n = s[i]
const code = n.charCodeAt(0)
if (code >= 65 && code <= 90) {
res += n.toLowerCase()
} else if (code >= 97 && code <= 122) {
res += n.toUpperCase()
} else {
res += n
}
}
return res
}
console.log(switchLetterCase('12aBc34'))
十二、数组中只出现一次的数字
function filterArr(arr) {
// 这里写下你的代码
let array = []
let res = {}
let len = arr.length
for (let i = 0
if (arr[i] in res) {
res[arr[i]] += 1
} else {
res[arr[i]] = 1
}
}
for (let key in res) {
if (res[key] === 1) {
array.push(key)
}
}
return array
}
console.log(filterArr([1, 2, 3, 4, 2, 3, 4, 1, 5, 6, 23, 32, 1]))
function filterArr(arr) {
let len = arr.length
let res = []
for (let i = 0
let count = 0
for (let j = 0
if (arr[i] === arr[j]) {
count++
}
}
if (count === 1) {
res.push(arr[i])
}
}
return res
}
console.log(filterArr([1, 2, 3, 4, 2, 3, 4, 1, 5, 6, 23, 32, 1]))
function filterArr(arr) {
let res = []
let len = arr.length
for (let i = 0
if (arr.indexOf(arr[i]) === arr.lastIndexOf(arr[i])) {
res.push(arr[i])
}
}
return res
}
console.log(filterArr([1, 2, 3, 4, 2, 3, 4, 1, 5, 6, 23, 32, 1]))
十三、js实现无序数组中的两数之和,并返回它们的索引
let array = [1,2,3,4,5,6,7]
function add(arr, target) {
const len = arr.length
let res = []
for (let i = 0
for (j = i
if(arr[i] + arr[j] === target) {
res = [i, j]
}
}
}
return res
}
console.log(add(array, 3))
十四、sleep函数
function sleep(timer){
return new Promise((resolve, reject) => {
setTimeout(resolve, timer)
})
}
sleep(3000).then(function () {
console.log(3)
})
1s后输出1 2s后输出2 3s后输出3
const log = (target) => {
console.log(target)
}
const sleep = (timer) => {
return new Promise(resolve => setTimeout(resolve, timer))
}
const main = async() => {
await sleep(1000)
log(1)
await sleep(2000)
log(2)
await sleep(3000)
log(3)
}
main()
十五、用setTimeout实现setInterval和setInterval实现setTimeout
function newSetInterval(timer, target) {
setTimeout(() => {
for (let i = 1; i <= target; i++) {
console.log(i)
}
newSetInterval(timer, target)
}, timer)
}
newSetInterval(3000, 5)
function newSetTimeout(timer, target) {
let time = null
timer = setInterval(() => {
for (let i = 0; i <= target; i++) {
console.log(i)
}
clearInterval(timer)
}, timer)
}
newSetTimeout(3000, 5)
十六、JS数组取交集、并集
数组取交集、并集
indexOf() 方法可返回某个指定的字符串值在字符串中首次出现的位置。
如果没有找到匹配的字符串则返回 -1
let a = [1,2,3], b = [2,3,4,5]
let interSection = a.filter(item => b.indexOf(item) > -1)
console.log(interSection, 'interSection')
let union = a.concat(b.filter(item => a.indexOf(item) === -1))
console.log(union,'union')
let a = [1,2,3], b = [2,3,4,5]
let interSection = a.filter(item => new Set(b).has(item))
console.log(interSection, 'interSection')
let union = Array.from(new Set([...a, ...b]))
console.log(union)
数组对象取交集、并集
const c = [{id: 1, name: 'jack'}, {id: 2, name: 'bob'}]
const d = [{id: 2, name: 'bob'}, {id: 3, name: 'tom'}]
function jiaoji(arr1,arr2) {
const res = []
const len1 = arr1.length
const len2 = arr2.length
for (let i = 0
for (let j = 0
if (arr1[i].id === arr2[j].id) {
res.push(arr2[j])
}
}
}
return res
}
console.log(jiaoji(c,d))
findIndex() 方法返回传入一个测试条件(函数)符合条件的数组第一个元素位置。
findIndex() 方法为数组中的每个元素都调用一次函数执行:
当数组中的元素在测试条件时返回 true 时, findIndex() 返回符合条件的元素的索引位置,之后的值不会再调用执行函数。
如果没有符合条件的元素返回 -1
注意: findIndex() 对于空数组,函数是不会执行的。
注意: findIndex() 并没有改变数组的原始值。
function bingji(arr1,arr2) {
let res = [...arr1, ...arr2]
const len1 = arr1.length
const len2 = arr2.length
for (let i = 0
for (let j = 0
if (arr1[i].id === arr2[j].id) {
res.splice(res.findIndex(item => arr1[i].id === item.id), 1)
}
}
}
return res
}
console.log(bingji(c,d))