目录
算法相关资料
[译] JavaScript 中的数据结构:写给前端软件工程师 - 掘金
【精选】Js中常见的数据结构(Data Structure)_js数据结构_傻乎乎的小不点的博客-CSDN博客
JS 常见的排序算法_js排序算法_东风过境F的博客-CSDN博客
【精选】贪心算法(greedy algorithm,又称贪婪算法)详解(附例题)-CSDN博客
1.有效的括号
题目:
解法:
var isValid = function(s) {
const stack = []
for(let i = 0; i < s.length; i++) {
const start = s[i]
if(s[i] == '(' || s[i] == '{' || s[i] == '[') {
stack.push(s[i])
} else {
const end = stack[stack.length - 1]
if(start == ')' && end == '(' || start == '}' && end == '{' || start == ']' && end == '[' ) {
stack.pop()
} else {
return false
}
}
}
return stack.length == 0
}
2.删除字符串中所有的相邻重复项
题目:
解法:
var removeDuplicates = function(s) {
let stack = []
for(let v of s) {
if(stack[stack.length - 1] != v) {
stack.push(v)
} else {
stack.pop()
}
}
return stack.join('')
}
3.简化路径
题目:
解法:
var simplifyPath = function(path) {
let stack = []
let str = ''
let arr = path.split('/')
arr.forEach( val => {
if(val && val == '..') {
stack.pop()
} else if(val && val != '.') {
stack.push(val)
}
})
arr.length ? str = '/' + stack.join('/') : str = '/'
return str
}
4.最近的请求次数
题目:
解法:
var RecentCounter = function() {
this.stack = []
}
RecentCounter.prototype.ping = function(t) {
this.stack.push(t)
while(this.stack[0] < t - 3000) {
this.stack.shift()
}
return this.stack.length
}
5.环形链表
题目:
解法:
var hasCycle = function(head) {
// 快慢指针初始化指向head
let f = head, s = head
// 快指针走到末尾时停止
while(f && f.next) {
// 慢指针走一步,快指针走两步
s = s.next
f = f.next.next
// 快慢指针相遇,说明含有环
if(s == f) {
return true
}
}
// 不包含环
return false
}
6.删除链表中的节点
题目:
解法:
var deleteNode = function(node) {
node.val = node.next.val
node.next = node.next.next
}
7.删除链表中的重复元素
题目:
解法:
var deleteDuplicates = function(head) {
if(!head) {
return head
}
// 指定cur指针指向头部head
let cur = head
while(cur && cur.next) {
if(cur.val == cur.next.val) {
cur.next = cur.next.next
} else {
cur = cur.next
}
}
return head
}
8.反转链表
题目:
解法:
var reverseList = function(head) {
let prev = null
let curr = head
while(curr) {
// 将curr的下一个节点储存在变量里
const next = curr.next
// 让curr的下一个节点指针指向prev
curr.next = prev
// 将prev和curr往后顺移一位
prev = curr
curr = next
}
// 此时prev为头节点
return prev
}
9.两数之和
题目:
解法:
var twoSum = function(nums, target) {
const map = new Map()
for(let i = 0; i < nums.length; i++) {
let num = target - nums[i]
if(map.has(num)) {
return [map.get(num), i]
} else {
map.set(nums[i], i)
}
}
return []
}
10.存在重复元素
题目:
解法:
var containsDuplicate = function(nums) {
const map = new Map()
for(let i of nums) {
if(map.has(i)) {
return true
} else {
map.set(i, 1)
}
}
return false
}
11.两个数组的交集
题目:
解法:
var intersection = function(nums1, nums2) {
const set = new Set(nums2)
return [...new Set(nums1)].filter((val) => set.has(val))
}
12.独一无二的出现次数
题目:
解法:
var uniqueOccurrences = function(arr) {
const map = new Map()
for(let i of arr) {
if(map.has(i)) {
map.set(i, map.get(i) + 1)
} else {
map.set(i, 1)
}
}
const set = new Set()
for(let [key, value] of map) {
set.add(value)
}
return set.size == map.size
}
13.无重复字符的最长子串
题目:
解法1(队列):
var lengthOfLongestSubstring = function(s) {
let res = []
let max = 0
for(let str of s) {
while(res.includes(str)) {
res.shift()
}
res.push(str)
max = Math.max(max, res.length)
}
return max
}
解法2(滑动窗口):
var lengthOfLongestSubstring = function(s) {
const map = new Map()
// 左指针
let l = 0
let max = 0
for(let i = 0; i < s.length; i++) {
if(map.has(s[i]) && map.get(s[i]) >= l) {
l = map.get(s[i]) + 1
}
map.set(s[i], i)
max = Math.max(max, i-l+1)
}
return max
}
14.二叉树的前序遍历
题目:
解法1(递归):
var preorderTraversal = function(root) {
let arr = []
const fun = (node) => {
if(node) {
// 遍历根节点
arr.push(node.val)
// 遍历左子树
fun(node.left)
// 遍历右子树
fun(node.right)
}
}
fun(root)
return arr
}
解法2(栈):
var preorderTraversal = function(root) {
if(!root) return []
let arr = []
// 根节点入栈
let stack = [root]
while(stack.length) {
// 出栈
let o = stack.pop()
arr.push(o.val)
o.right && stack.push(o.right)
o.left && stack.push(o.left)
}
return arr
}
15.二叉树的中序遍历
题目:
解法1(递归):
var inorderTraversal = function(root) {
const arr = []
const fun = (root) => {
if(!root) return;
fun(root.left)
arr.push(root.val)
fun(root.right)
}
fun(root)
return arr
}
解法2(栈):
var inorderTraversal = function(root) {
const arr = []
const stack = []
let o = root
while(stack.length || o) {
while(o) {
stack.push(o)
o = o.left
}
const n = stack.pop()
arr.push(n.val)
o = n.right
}
return arr
}
16.二叉树的后序遍历
题目:
解法1(递归):
var postorderTraversal = function(root) {
const arr = []
const fun = (root) => {
if(root) {
fun(root.left)
fun(root.right)
arr.push(root.val)
}
}
fun(root)
return arr
}
解法2(栈):
var postorderTraversal = function(root) {
if(!root) return []
const arr = []
const stack = [root]
while(stack.length) {
const o = stack.pop()
arr.unshift(o.val)
o.left && stack.push(o.left)
o.right && stack.push(o.right)
}
return arr
}
17.二叉树的最小深度
题目:
解法1(递归):
var minDepth = function(root) {
if(!root) return 0
// 到叶子节点,返回1
if(!root.left && !root.right) return 1
// 只有右节点时,递归右节点
if(!root.left) return minDepth(root.right) + 1
// 只有左节点时,递归左节点
if(!root.right) return minDepth(root.left) + 1
return Math.min(minDepth(root.right), minDepth(root.left)) + 1
}
解法2(迭代):
var minDepth = function(root) {
if(!root) return 0
const queue = [root]
let dep = 0
while(true) {
let size = queue.length
dep++
while(size--) {
const node = queue.shift()
// 到第一个叶子节点,返回当前深度
if(!node.left && !node.right) return dep
node.left && queue.push(node.left)
node.right && queue.push(node.right)
}
}
}
18.二叉树的最大深度
题目:
解法1(递归):
var maxDepth = function(root) {
if(!root) return 0
else {
const left = maxDepth(root.left)
const right = maxDepth(root.right)
return Math.max(left, right) + 1
}
}
解法2(迭代):
var maxDepth = function(root) {
if(!root) return 0
const queue = [root]
let dep = 0
while(queue.length) {
let len = queue.length
dep++
while(len--) {
const o = queue.shift()
o.left && queue.push(o.left)
o.right && queue.push(o.right)
}
}
return dep
}
19.翻转二叉树
题目:
解法:
var invertTree = function(root) {
if(!root) return null
let temp = root.left
root.left = root.right
root.right = temp
invertTree(root.left)
invertTree(root.right)
return root
}
20.相同的树
题目:
解法:
var isSameTree = function(p, q) {
if(p == null && q == null) return true
if(p == null || q == null) return false
if(p.val != q.val) return false
return isSameTree(p.left, q.left) && isSameTree(p.right, q.right)
}
21.数组中的第k个最大元素
题目:
解法:
var findKthLargest = function(nums, k) {
let heapSize = nums.length
// 构建好的大顶堆
buildMaxHeap(nums, heapSize)
// 进行下沉,最大元素下沉到末尾
for(let i = nums.length - 1; i >= nums.length - k + 1; i--) {
swap(nums, 0, i)
// 下沉后的元素不参与到大顶堆的调整
--heapSize
// 重新调整大顶堆
maxHeapify(nums, 0, heapSize)
}
return nums[0]
// 自下而上构建大顶堆
function buildMaxHeap(nums, heapSize) {
for(let i = Math.floor(heapSize / 2) - 1; i >= 0; i--) {
maxHeapify(nums, i, heapSize)
}
}
// 从左向右,自上而下的调整节点
function maxHeapify(nums, i, heapSize) {
let l = i * 2 + 1
let r = i * 2 + 2
let largest = i
if(l < heapSize && nums[l] > nums[largest]) {
largest = l
}
if(r < heapSize && nums[r] > nums[largest]) {
largest = r
}
if(largest !== i) {
// 进行节点调整
swap(nums, i ,largest)
// 继续调整下面的叶子节点
maxHeapify(nums, largest, heapSize)
}
}
function swap(a, i, j) {
let temp = a[i]
a[i] = a[j]
a[j] = temp
}
}
22.猜数字大小
题目:
解法:
var guessNumber = function(n) {
let start = 1
let end = n
while(start <= end) {
const mid = Math.floor((start + end) / 2)
const res = guess(mid)
if(res == 0) {
return mid
} else if(res == -1) {
end = mid
} else if(res == 1) {
start = mid + 1
}
}
}
23.多数元素
题目:
解法:
var majorityElement = function(nums) {
if(nums.length == 1) {
return nums[0]
}
const map = new Map()
for(let i of nums) {
if(!map.get(i)) {
map.set(i, 1)
} else {
map.set(i, map.get(i) + 1)
if(map.get(i) > nums.length / 2) return i
}
}
}
24.爬楼梯
题目:
解法:
var climbStairs = function(n) {
const arr = []
arr[0] = 1
arr[1] = 1
for(let i = 2; i <= n; i++) {
arr[i] = arr[i - 1] + arr[i - 2]
}
return arr[n]
}
思路:
25.打家劫舍
题目:
解法:
var rob = function(nums) {
const l = nums.length
if(l == 0) return 0
const dp = new Array(l + 1)
dp[0] = 0
dp[1] = nums[0]
for(let i = 2; i <= l; i++) {
dp[i] = Math.max(dp[i - 1], dp[i - 2] + nums[i - 1])
}
return dp[l]
}
思路:
26.跳跃游戏
题目:
解法1(动态规划):
var canJump = function(nums) {
// 初始化dp
let dp = new Array(nums.length).fill(false)
// 第一格能到达
dp[0] = true
for(let i = 1; i < nums.length; i++) {
for(let j = 0; j < i; j++) {
// 当前位置j能到达,并且当前位置j加上可跳跃距离如果超过了i,则将dp[i]更新为ture,即i位置也可以到达
if(dp[j] && j + nums[j] >= i) {
dp[i] = true
break
}
}
}
return dp[nums.length - 1]
}
解法2(贪心算法):
var canJump = function(nums) {
// 长度为1直接就是终点
if(nums.length == 1) return true
// 能覆盖的最远距离
let cover = nums[0]
for(let i = 0; i <= cover; i++) {
// 在 当前覆盖距离cover 和 当前位置加可跳跃距离 中取一个较大者
cover = Math.max(cover, i + nums[i])
if(cover >= nums.length - 1) {
// 覆盖距离大于等于nums.length - 1说明能到达终点
return true
}
}
return false
}
27.分发饼干
题目:
解法:
var findContentChildren = function(g, s) {
// 先将两个数组进行排序
g = g.sort((a, b) => a - b)
s = s.sort((a, b) => a - b)
let res = 0
// 使用index来控制饼干数组的遍历
let index = s.length - 1
// 优先满足胃口大的孩子
for(let i = g.length - 1; i >= 0; i--) {
if(index >= 0 && s[index] >= g[i]) {
res++
index--
}
}
return res
}
28.买卖股票的最佳时机2
题目:
解法1(动态规划):
var maxProfit = function(prices) {
const n = prices.length
let dp0 = 0, dp1 = -prices[0]
for(let i = 1; i < n; i++) {
let newDp0 = Math.max(dp0, dp1 + prices[i])
let newDp1 = Math.max(dp1, dp0 - prices[i])
dp0 = newDp0
dp1 = newDp1
}
return dp0
}
思路:
解法2(贪心算法):
var maxProfit = function(prices) {
let ans = 0
let n = prices.length
for(let i = 1; i < n; i++) {
ans += Math.max(0, prices[i] - prices[i - 1])
}
return ans
}
思路:
29.字符串解码
题目:
解法1:
var decodeString = function(s) {
let numStack = [] // 存倍数的栈
let strStack = [] // 存待拼接字符串的栈
let num = 0 // 倍数的搬运工
let res = '' // 字符串的搬运工
for(let k of s) { // 逐字符扫描
if(!isNaN(k)) { // 遇到数字
num = num * 10 + Number(k) // 算出倍数
} else if(k == '[') { // 遇到'['
strStack.push(res) // res串入栈
res = '' // 入栈后清零
numStack.push(num) // 倍数num入栈
num = 0 // 入栈后清零
} else if(k == ']') { // 遇到']',两个栈的栈顶出栈
let repeatTimes = numStack.pop() // 获取拷贝次数
res = strStack.pop() + res.repeat(repeatTimes) // 构建子串
} else {
res += k // 遇到字母,追加给res串
}
}
return res
}
解法2:
var decodeString = function(s) {
let stack = []
for(let k of s) {
// ']'前的字符串都入栈
if(k !== ']') {
stack.push(k)
continue
}
let cur = stack.pop()
let str = ''
// 接下来遇到字母,直到遇到'['
while(cur !== '[') {
str = cur + str
cur = stack.pop()
}
// 此时cur为'[',接下来遇到数字
let num = ''
cur = stack.pop()
while(!isNaN(cur)) {
num = cur + num
cur = stack.pop()
}
// 现在要么是字母,要么是'['
stack.push(cur)
stack.push(str.repeat(num))
}
return stack.join('')
}
30.Pow(x, n)
题目:
解法:
var myPow = function(x, n) {
// n=0直接返回1
if(n == 0) return 1
// n<0时,x的n次方等于1除以x的-n次方
if(n < 0) {
return 1 / myPow(x, -n)
}
// n为奇数时,x的n次方等于x乘以x的n-1次方
if(n % 2) {
return x * myPow(x, n - 1)
}
// n为偶数时,使用分治一分为二,等于x*x的n/2次方
return myPow(x * x, n / 2)
}