前端面试算法类型
1. 数组与字符串
1.1 数组题目
-
求最大/最小值:通常可以使用一次遍历来找到最大或最小值。
- 示例:求数组中的最大值
function findMax(arr) { return Math.max(...arr); }
- 示例:求数组中的最大值
-
排序:可以使用内置的排序方法或者实现常见的排序算法(如快速排序、归并排序)。
- 示例:数组排序
function sortArray(arr) { return arr.sort((a, b) => a - b); }
- 示例:数组排序
-
查找重复元素:可以使用哈希表来记录出现的元素。
- 示例:查找数组中的重复元素
function findDuplicates(arr) { let seen = new Set(); let duplicates = new Set(); for (let num of arr) { if (seen.has(num)) { duplicates.add(num); } else { seen.add(num); } } return [...duplicates]; }
- 示例:查找数组中的重复元素
-
滑动窗口:用于处理固定长度的子数组或子字符串问题。
- 示例:找到和为定值的连续子数组
function subarraySum(arr, target) { let sum = 0; let map = new Map(); map.set(0, -1); for (let i = 0; i < arr.length; i++) { sum += arr[i]; if (map.has(sum - target)) { return [map.get(sum - target) + 1, i]; } map.set(sum, i); } return []; }
- 示例:找到和为定值的连续子数组
1.2 字符串题目
-
反转字符串:可以通过双指针或内置方法来实现。
- 示例:反转字符串
function reverseString(str) { return str.split('').reverse().join(''); }
- 示例:反转字符串
-
判断回文:通过双指针比较字符串的两端。
- 示例:判断字符串是否为回文
function isPalindrome(str) { let left = 0; let right = str.length - 1; while (left < right) { if (str[left] !== str[right]) { return false; } left++; right--; } return true; }
- 示例:判断字符串是否为回文
-
最长子串:使用滑动窗口或动态规划。
- 示例:找到最长不含重复字符的子串
function lengthOfLongestSubstring(s) { let map = new Map(); let maxLen = 0; let start = 0; for (let end = 0; end < s.length; end++) { if (map.has(s[end])) { start = Math.max(map.get(s[end]) + 1, start); } map.set(s[end], end); maxLen = Math.max(maxLen, end - start + 1); } return maxLen; }
- 示例:找到最长不含重复字符的子串
2. 链表
-
反转链表:使用迭代或递归。
- 示例:反转链表
function reverseList(head) { let prev = null; let curr = head; while (curr) { let nextTemp = curr.next; curr.next = prev; prev = curr; curr = nextTemp; } return prev; }
- 示例:反转链表
-
链表中环的检测:使用快慢指针。
- 示例:判断链表中是否有环
function hasCycle(head) { let slow = head; let fast = head; while (fast && fast.next) { slow = slow.next; fast = fast.next.next; if (slow === fast) { return true; } } return false; }
- 示例:判断链表中是否有环
3. 栈与队列
-
有效的括号:使用栈来匹配括号。
- 示例:有效的括号
function isValid(s) { let stack = []; let map = { '(': ')', '{': '}', '[': ']' }; for (let char of s) { if (char in map) { stack.push(char); } else { if (char !== map[stack.pop()]) { return false; } } } return stack.length === 0; }
- 示例:有效的括号
-
队列实现栈:用两个队列来模拟栈的操作。
- 示例:用队列实现栈
class MyStack { constructor() { this.queue1 = []; this.queue2 = []; } push(x) { this.queue1.push(x); } pop() { while (this.queue1.length > 1) { this.queue2.push(this.queue1.shift()); } let popped = this.queue1.shift(); let temp = this.queue1; this.queue1 = this.queue2; this.queue2 = temp; return popped; } top() { return this.queue1[this.queue1.length - 1]; } empty() { return this.queue1.length === 0; } }
- 示例:用队列实现栈
4. 树与图
4.1 树
-
二叉树的遍历:包括前序遍历、中序遍历和后序遍历。
- 示例:前序遍历
function preorderTraversal(root) { let result = []; function traverse(node) { if (!node) return; result.push(node.val); traverse(node.left); traverse(node.right); } traverse(root); return result; }
- 示例:前序遍历
-
二叉树的层序遍历:使用队列实现。
- 示例:层序遍历
function levelOrder(root) { let result = []; if (!root) return result; let queue = [root]; while (queue.length) { let level = []; let size = queue.length; for (let i = 0; i < size; i++) { let node = queue.shift(); level.push(node.val); if (node.left) queue.push(node.left); if (node.right) queue.push(node.right); } result.push(level); } return result; }
- 示例:层序遍历
4.2 图
-
深度优先搜索(DFS):递归实现或使用栈。
- 示例:DFS
function dfs(graph, start) { let visited = new Set(); function traverse(node) { if (visited.has(node)) return; visited.add(node); for (let neighbor of graph[node]) { traverse(neighbor); } } traverse(start); return visited; }
- 示例:DFS
-
广度优先搜索(BFS):使用队列。
- 示例:BFS
function bfs(graph, start) { let visited = new Set(); let queue = [start]; while (queue.length) { let node = queue.shift(); if (visited.has(node)) continue; visited.add(node); for (let neighbor of graph[node]) { queue.push(neighbor); } } return visited; }
- 示例:BFS
5. 动态规划
-
斐波那契数列:使用递归或动态规划。
- 示例:斐波那契数列
function fib(n) { let dp = [0, 1]; for (let i = 2; i <= n; i++) { dp[i] = dp[i - 1] + dp[i - 2]; } return dp[n]; }
- 示例:斐波那契数列
-
背包问题:经典的动态规划问题。
- 示例:0/1 背包问题
function knapsack(weights, values, capacity) { let n = weights.length; let dp = Array.from({ length: n + 1 }, () => Array(capacity + 1).fill(0)); for (let i = 1; i <= n; i++) { for (let w = 1; w <= capacity; w++) { if (weights[i - 1] <= w) { dp[i][w] = Math.max(dp[i - 1][w], dp[i - 1][w - weights[i - 1]] + values[i - 1]); } else { dp[i][w] = dp[i - 1][w]; } } } return dp[n][capacity]; }
- 示例:0/1 背包问题
6. 贪心算法
- 找零钱问题:通过贪心策略选择最大的面额。
- 示例:找零钱
function coinChange(coins,
- 示例:找零钱
amount) { let count = 0; coins.sort((a, b) => b - a); for (let coin of coins) { if (amount >= coin) { count += Math.floor(amount / coin); amount %= coin; } } return amount === 0 ? count : -1; } ```
7. 回溯算法
- 全排列:使用回溯生成所有可能的排列。
- 示例:全排列
function permute(nums) { let result = []; function backtrack(path, used) { if (path.length === nums.length) { result.push([...path]); return; } for (let i = 0; i < nums.length; i++) { if (used[i]) continue; path.push(nums[i]); used[i] = true; backtrack(path, used); path.pop(); used[i] = false; } } backtrack([], Array(nums.length).fill(false)); return result; }
- 示例:全排列
8. 数学与位运算
-
素数判断:常用于数论问题。
- 示例:判断素数
function isPrime(num) { if (num <= 1) return false; if (num <= 3) return true; if (num % 2 === 0 || num % 3 === 0) return false; for (let i = 5; i * i <= num; i += 6) { if (num % i === 0 || num % (i + 2) === 0) return false; } return true; }
- 示例:判断素数
-
位运算:处理位级操作,如与、或、异或、左移、右移等。
- 示例:计算二进制中1的个数
function hammingWeight(n) { let count = 0; while (n !== 0) { count += n & 1; n >>>= 1; } return count; }
- 示例:计算二进制中1的个数