1. 數(shù)組與字符串:滑動窗口
題目描述:
給定一個字符串 s,請你找出其中不含有重復字符的 最長子串 的長度。
示例 1:
輸入: s = "abcabcbb"
輸出: 3
解釋: 因為無重復字符的最長子串是 "abc",所以其長度為 3。
示例 2:
輸入: s = "bbbbb"
輸出: 1
解釋: 因為無重復字符的最長子串是 "b",所以其長度為 1。
JavaScript解決方案:
/**
* @param {string} s
* @return {number}
*/
function lengthOfLongestSubstring(s) {
// 使用Map存儲字符及其最后出現(xiàn)的位置
const charIndexMap = new Map();
let left = 0; // 滑動窗口左邊界
let maxLength = 0;
for (let right = 0; right < s.length; right++) {
const currentChar = s[right];
// 如果字符已在窗口內(nèi),移動左邊界
if (charIndexMap.has(currentChar) && charIndexMap.get(currentChar) >= left) {
left = charIndexMap.get(currentChar) + 1;
}
// 更新字符位置
charIndexMap.set(currentChar, right);
// 更新最大長度
maxLength = Math.max(maxLength, right - left + 1);
}
return maxLength;
}
// 測試
console.log("最長無重復子串:");
console.log('"abcabcbb" ->', lengthOfLongestSubstring("abcabcbb")); // 3
console.log('"bbbbb" ->', lengthOfLongestSubstring("bbbbb")); // 1
console.log('"pwwkew" ->', lengthOfLongestSubstring("pwwkew")); // 3
2. 鏈表:反轉(zhuǎn)鏈表
題目描述:
給你單鏈表的頭節(jié)點 head,請你反轉(zhuǎn)鏈表,并返回反轉(zhuǎn)后的鏈表。
示例:
輸入: head = [1,2,3,4,5]
輸出: [5,4,3,2,1]
JavaScript解決方案:
/**
* 鏈表節(jié)點定義
*/
class ListNode {
constructor(val = 0, next = null) {
this.val = val;
this.next = next;
}
}
/**
* @param {ListNode} head
* @return {ListNode}
*/
function reverseList(head) {
let prev = null;
let current = head;
while (current !== null) {
const nextNode = current.next; // 暫存下一個節(jié)點
current.next = prev; // 反轉(zhuǎn)當前節(jié)點的指針
prev = current; // 移動prev到當前節(jié)點
current = nextNode; // 移動current到下一個節(jié)點
}
return prev; // prev最終成為新的頭節(jié)點
}
// 輔助函數(shù):創(chuàng)建鏈表
function createList(arr) {
if (arr.length === 0) return null;
const head = new ListNode(arr[0]);
let current = head;
for (let i = 1; i < arr.length; i++) {
current.next = new ListNode(arr[i]);
current = current.next;
}
return head;
}
// 輔助函數(shù):打印鏈表
function printList(head) {
const result = [];
let current = head;
while (current !== null) {
result.push(current.val);
current = current.next;
}
return result;
}
// 測試
console.log("\n反轉(zhuǎn)鏈表:");
const head = createList([1, 2, 3, 4, 5]);
console.log("原始鏈表:", printList(head)); // [1,2,3,4,5]
const reversed = reverseList(head);
console.log("反轉(zhuǎn)后:", printList(reversed)); // [5,4,3,2,1]
3. 棧:有效的括號
題目描述:
給定一個只包括 '(',')','{','}','[',']' 的字符串 s,判斷字符串是否有效。
有效字符串需滿足:
- 左括號必須用相同類型的右括號閉合。
- 左括號必須以正確的順序閉合。
- 每個右括號都有一個對應的相同類型的左括號。
示例 1:
輸入: s = "()"
輸出: true
示例 2:
輸入: s = "()[]{}"
輸出: true
示例 3:
輸入: s = "(]"
輸出: false
JavaScript解決方案:
/**
* @param {string} s
* @return {boolean}
*/
function isValid(s) {
const stack = [];
const bracketPairs = {
')': '(',
'}': '{',
']': '['
};
for (let i = 0; i < s.length; i++) {
const char = s[i];
// 如果是右括號
if (bracketPairs[char]) {
// 檢查棧頂元素是否匹配
const topElement = stack.length === 0 ? '#' : stack.pop();
if (topElement !== bracketPairs[char]) {
return false;
}
}
// 如果是左括號,壓入棧
else {
stack.push(char);
}
}
// 如果棧為空,說明所有括號都匹配
return stack.length === 0;
}
// 測試
console.log("\n有效的括號:");
console.log('"()" ->', isValid("()")); // true
console.log('"()[]{}" ->', isValid("()[]{}")); // true
console.log('"(]" ->', isValid("(]")); // false
console.log('"([)]" ->', isValid("([)]")); // false
console.log('"{[]}" ->', isValid("{[]}")); // true
4. 哈希表:兩數(shù)之和
題目描述:
給定一個整數(shù)數(shù)組 nums 和一個整數(shù)目標值 target,請你在該數(shù)組中找出和為目標值 target 的那兩個整數(shù),并返回它們的數(shù)組下標。
你可以假設(shè)每種輸入只會對應一個答案,并且你不能使用同一個元素兩次。
你可以按任意順序返回答案。
示例 1:
輸入: nums = [2,7,11,15], target = 9
輸出: [0,1]
解釋: 因為 nums[0] + nums[1] = 2 + 7 = 9
示例 2:
輸入: nums = [3,2,4], target = 6
輸出: [1,2]
JavaScript解決方案:
/**
* @param {number[]} nums
* @param {number} target
* @return {number[]}
*/
function twoSum(nums, target) {
// 使用Map存儲數(shù)字和對應的索引
const numMap = new Map();
for (let i = 0; i < nums.length; i++) {
const complement = target - nums[i];
// 檢查補數(shù)是否已在Map中
if (numMap.has(complement)) {
return [numMap.get(complement), i];
}
// 將當前數(shù)字和索引存入Map
numMap.set(nums[i], i);
}
return []; // 根據(jù)題目描述,總會有一個解
}
// 測試
console.log("\n兩數(shù)之和:");
console.log("nums = [2,7,11,15], target = 9 ->", twoSum([2,7,11,15], 9)); // [0,1]
console.log("nums = [3,2,4], target = 6 ->", twoSum([3,2,4], 6)); // [1,2]
console.log("nums = [3,3], target = 6 ->", twoSum([3,3], 6)); // [0,1]
5. 二叉樹:二叉樹的最大深度
題目描述:
給定一個二叉樹 root,返回其最大深度。
二叉樹的最大深度是指從根節(jié)點到最遠葉子節(jié)點的最長路徑上的節(jié)點數(shù)。
示例:
3
/ \
9 20
/ \
15 7
輸入: root = [3,9,20,null,null,15,7]
輸出: 3
解釋: 最大深度為3,路徑為 3→20→7 或 3→20→15
JavaScript解決方案:
/**
* 二叉樹節(jié)點定義
*/
class TreeNode {
constructor(val = 0, left = null, right = null) {
this.val = val;
this.left = left;
this.right = right;
}
}
/**
* @param {TreeNode} root
* @return {number}
*/
function maxDepth(root) {
// 遞歸解法:深度優(yōu)先搜索
if (root === null) {
return 0;
}
const leftDepth = maxDepth(root.left);
const rightDepth = maxDepth(root.right);
return Math.max(leftDepth, rightDepth) + 1;
}
// 廣度優(yōu)先搜索解法
function maxDepthBFS(root) {
if (root === null) return 0;
const queue = [root];
let depth = 0;
while (queue.length > 0) {
depth++;
const levelSize = queue.length;
for (let i = 0; i < levelSize; i++) {
const node = queue.shift();
if (node.left) queue.push(node.left);
if (node.right) queue.push(node.right);
}
}
return depth;
}
// 創(chuàng)建測試二叉樹
const root = new TreeNode(3);
root.left = new TreeNode(9);
root.right = new TreeNode(20);
root.right.left = new TreeNode(15);
root.right.right = new TreeNode(7);
// 測試
console.log("\n二叉樹的最大深度:");
console.log("遞歸解法:", maxDepth(root)); // 3
console.log("BFS解法:", maxDepthBFS(root)); // 3
6. 堆:數(shù)組中的第K個最大元素
題目描述:
給定整數(shù)數(shù)組 nums 和整數(shù) k,請返回數(shù)組中第 k 個最大的元素。
請注意,你需要找的是數(shù)組排序后的第 k 個最大的元素,而不是第 k 個不同的元素。
示例 1:
輸入: nums = [3,2,1,5,6,4], k = 2
輸出: 5
解釋: 排序后數(shù)組是 [1,2,3,4,5,6],第2個最大元素是5
示例 2:
輸入: nums = [3,2,3,1,2,4,5,5,6], k = 4
輸出: 4
JavaScript解決方案:
/**
* @param {number[]} nums
* @param {number} k
* @return {number}
*/
// 方法1:使用內(nèi)置排序(簡單但效率較低)
function findKthLargestSort(nums, k) {
nums.sort((a, b) => b - a); // 降序排序
return nums[k - 1];
}
// 方法2:最小堆(推薦面試使用)
function findKthLargestHeap(nums, k) {
// 最小堆實現(xiàn)
class MinHeap {
constructor() {
this.heap = [];
}
push(val) {
this.heap.push(val);
this._bubbleUp(this.heap.length - 1);
}
pop() {
const min = this.heap[0];
const last = this.heap.pop();
if (this.heap.length > 0) {
this.heap[0] = last;
this._sinkDown(0);
}
return min;
}
peek() {
return this.heap[0];
}
size() {
return this.heap.length;
}
_bubbleUp(index) {
const element = this.heap[index];
while (index > 0) {
const parentIndex = Math.floor((index - 1) / 2);
const parent = this.heap[parentIndex];
if (element >= parent) break;
this.heap[parentIndex] = element;
this.heap[index] = parent;
index = parentIndex;
}
}
_sinkDown(index) {
const length = this.heap.length;
const element = this.heap[index];
while (true) {
let leftChildIndex = 2 * index + 1;
let rightChildIndex = 2 * index + 2;
let swap = null;
let leftChild, rightChild;
if (leftChildIndex < length) {
leftChild = this.heap[leftChildIndex];
if (leftChild < element) {
swap = leftChildIndex;
}
}
if (rightChildIndex < length) {
rightChild = this.heap[rightChildIndex];
if (
(swap === null && rightChild < element) ||
(swap !== null && rightChild < leftChild)
) {
swap = rightChildIndex;
}
}
if (swap === null) break;
this.heap[index] = this.heap[swap];
this.heap[swap] = element;
index = swap;
}
}
}
const minHeap = new MinHeap();
for (const num of nums) {
minHeap.push(num);
// 保持堆的大小為k
if (minHeap.size() > k) {
minHeap.pop(); // 移除最小的元素
}
}
return minHeap.peek(); // 堆頂就是第k個最大元素
}
// 測試
console.log("\n數(shù)組中的第K個最大元素:");
const nums1 = [3,2,1,5,6,4];
console.log("排序方法:", findKthLargestSort([...nums1], 2)); // 5
console.log("最小堆方法:", findKthLargestHeap([...nums1], 2)); // 5
const nums2 = [3,2,3,1,2,4,5,5,6];
console.log("排序方法:", findKthLargestSort([...nums2], 4)); // 4
console.log("最小堆方法:", findKthLargestHeap([...nums2], 4)); // 4
7. 回溯:全排列
題目描述:
給定一個不含重復數(shù)字的數(shù)組 nums,返回其所有可能的全排列。你可以按任意順序返回答案。
示例 1:
輸入: nums = [1,2,3]
輸出: [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
示例 2:
輸入: nums = [0,1]
輸出: [[0,1],[1,0]]
JavaScript解決方案:
/**
* @param {number[]} nums
* @return {number[][]}
*/
function permute(nums) {
const result = [];
// 回溯函數(shù)
function backtrack(currentPermutation, used) {
// 如果當前排列長度等于原數(shù)組長度,說明找到了一個排列
if (currentPermutation.length === nums.length) {
result.push([...currentPermutation]); // 深拷貝
return;
}
for (let i = 0; i < nums.length; i++) {
// 如果數(shù)字已使用,跳過
if (used[i]) continue;
// 選擇當前數(shù)字
used[i] = true;
currentPermutation.push(nums[i]);
// 遞歸探索
backtrack(currentPermutation, used);
// 撤銷選擇(回溯)
currentPermutation.pop();
used[i] = false;
}
}
backtrack([], new Array(nums.length).fill(false));
return result;
}
// 測試
console.log("\n全排列:");
console.log("輸入: [1,2,3]");
console.log("輸出:", permute([1,2,3]));
console.log("\n輸入: [0,1]");
console.log("輸出:", permute([0,1]));
8. 分治:歸并排序
題目描述:
給你一個整數(shù)數(shù)組 nums,請你將該數(shù)組升序排列。
示例 1:
輸入: nums = [5,2,3,1]
輸出: [1,2,3,5]
示例 2:
輸入: nums = [5,1,1,2,0,0]
輸出: [0,0,1,1,2,5]
JavaScript解決方案:
/**
* @param {number[]} nums
* @return {number[]}
*/
function mergeSort(nums) {
// 分治法的典型應用
if (nums.length <= 1) {
return nums;
}
// 1. 分:將數(shù)組分成兩半
const mid = Math.floor(nums.length / 2);
const left = nums.slice(0, mid);
const right = nums.slice(mid);
// 2. 治:遞歸排序左右兩半
const sortedLeft = mergeSort(left);
const sortedRight = mergeSort(right);
// 3. 合:合并兩個有序數(shù)組
return merge(sortedLeft, sortedRight);
}
/**
* 合并兩個有序數(shù)組
*/
function merge(left, right) {
const result = [];
let i = 0, j = 0;
// 比較兩個數(shù)組的元素,將較小的放入結(jié)果
while (i < left.length && j < right.length) {
if (left[i] <= right[j]) {
result.push(left[i]);
i++;
} else {
result.push(right[j]);
j++;
}
}
// 將剩余元素添加到結(jié)果
while (i < left.length) {
result.push(left[i]);
i++;
}
while (j < right.length) {
result.push(right[j]);
j++;
}
return result;
}
// 測試
console.log("\n歸并排序:");
console.log("輸入: [5,2,3,1]");
console.log("輸出:", mergeSort([5,2,3,1])); // [1,2,3,5]
console.log("\n輸入: [5,1,1,2,0,0]");
console.log("輸出:", mergeSort([5,1,1,2,0,0])); // [0,0,1,1,2,5]
9. BFS:二叉樹的層序遍歷
題目描述:
給你二叉樹的根節(jié)點 root,返回其節(jié)點值的層序遍歷。(即逐層地,從左到右訪問所有節(jié)點)。
示例:
3
/ \
9 20
/ \
15 7
輸入: root = [3,9,20,null,null,15,7]
輸出: [[3],[9,20],[15,7]]
JavaScript解決方案:
/**
* @param {TreeNode} root
* @return {number[][]}
*/
function levelOrder(root) {
if (!root) return [];
const result = [];
const queue = [root];
while (queue.length > 0) {
const levelSize = queue.length;
const currentLevel = [];
for (let i = 0; i < levelSize; i++) {
const node = queue.shift();
currentLevel.push(node.val);
if (node.left) queue.push(node.left);
if (node.right) queue.push(node.right);
}
result.push(currentLevel);
}
return result;
}
// 創(chuàng)建測試二叉樹
const tree = new TreeNode(3);
tree.left = new TreeNode(9);
tree.right = new TreeNode(20);
tree.right.left = new TreeNode(15);
tree.right.right = new TreeNode(7);
// 測試
console.log("\n二叉樹的層序遍歷:");
console.log("輸出:", levelOrder(tree)); // [[3],[9,20],[15,7]]
10. 動態(tài)規(guī)劃:爬樓梯
題目描述:
假設(shè)你正在爬樓梯。需要 n 階你才能到達樓頂。
每次你可以爬 1 或 2 個臺階。你有多少種不同的方法可以爬到樓頂呢?
示例 1:
輸入: n = 2
輸出: 2
解釋: 有兩種方法可以爬到樓頂。
1. 1 階 + 1 階
2. 2 階
示例 2:
輸入: n = 3
輸出: 3
解釋: 有三種方法可以爬到樓頂。
1. 1 階 + 1 階 + 1 階
2. 1 階 + 2 階
3. 2 階 + 1 階
JavaScript解決方案:
/**
* @param {number} n
* @return {number}
*/
function climbStairs(n) {
if (n <= 2) return n;
// dp[i] 表示爬到第i階樓梯的方法數(shù)
const dp = new Array(n + 1).fill(0);
dp[1] = 1; // 爬1階有1種方法
dp[2] = 2; // 爬2階有2種方法
for (let i = 3; i <= n; i++) {
// 狀態(tài)轉(zhuǎn)移方程:dp[i] = dp[i-1] + dp[i-2]
dp[i] = dp[i - 1] + dp[i - 2];
}
return dp[n];
}
// 空間優(yōu)化版本
function climbStairsOptimized(n) {
if (n <= 2) return n;
let prev1 = 2; // dp[i-1],從i=3開始,prev1=dp[2]=2
let prev2 = 1; // dp[i-2],從i=3開始,prev2=dp[1]=1
for (let i = 3; i <= n; i++) {
const current = prev1 + prev2;
prev2 = prev1;
prev1 = current;
}
return prev1;
}
// 測試
console.log("\n爬樓梯:");
console.log("n=1 ->", climbStairs(1)); // 1
console.log("n=2 ->", climbStairs(2)); // 2
console.log("n=3 ->", climbStairs(3)); // 3
console.log("n=4 ->", climbStairs(4)); // 5
console.log("n=5 ->", climbStairs(5)); // 8
console.log("\n空間優(yōu)化版本:");
console.log("n=5 ->", climbStairsOptimized(5)); // 8
11. 貪心算法:分發(fā)餅干
題目描述:
假設(shè)你是一位很棒的家長,想要給你的孩子們一些小餅干。但是,每個孩子最多只能給一塊餅干。
對每個孩子 i,都有一個胃口值 g[i],這是能讓孩子們滿足胃口的餅干的最小尺寸;并且每塊餅干 j,都有一個尺寸 s[j]。如果 s[j] >= g[i],我們可以將這個餅干 j 分配給孩子 i,這個孩子會得到滿足。你的目標是盡可能滿足越多數(shù)量的孩子,并輸出這個最大數(shù)值。
示例 1:
輸入: g = [1,2,3], s = [1,1]
輸出: 1
解釋:
你有三個孩子和兩塊小餅干,3個孩子的胃口值分別是:1,2,3。
雖然你有兩塊小餅干,由于他們的尺寸都是1,你只能讓胃口值是1的孩子滿足。
所以你應該輸出1。
示例 2:
輸入: g = [1,2], s = [1,2,3]
輸出: 2
解釋:
你有兩個孩子和三塊小餅干,2個孩子的胃口值分別是1,2。
你擁有的餅干數(shù)量和尺寸都足以讓所有孩子滿足。
所以你應該輸出2。
JavaScript解決方案:
/**
* @param {number[]} g - 孩子的胃口值
* @param {number[]} s - 餅干的尺寸
* @return {number}
*/
function findContentChildren(g, s) {
// 貪心策略:用小餅干滿足小胃口的孩子
g.sort((a, b) => a - b); // 孩子按胃口排序
s.sort((a, b) => a - b); // 餅干按尺寸排序
let childIndex = 0;
let cookieIndex = 0;
let satisfiedCount = 0;
while (childIndex < g.length && cookieIndex < s.length) {
if (s[cookieIndex] >= g[childIndex]) {
// 當前餅干可以滿足當前孩子
satisfiedCount++;
childIndex++;
cookieIndex++;
} else {
// 當前餅干太小,嘗試下一塊餅干
cookieIndex++;
}
}
return satisfiedCount;
}
// 測試
console.log("\n分發(fā)餅干:");
console.log("g=[1,2,3], s=[1,1] ->", findContentChildren([1,2,3], [1,1])); // 1
console.log("g=[1,2], s=[1,2,3] ->", findContentChildren([1,2], [1,2,3])); // 2
12. 雙指針:盛最多水的容器
題目描述:
給定一個長度為 n 的整數(shù)數(shù)組 height。有 n 條垂線,第 i 條線的兩個端點是 (i, 0) 和 (i, height[i])。
找出其中的兩條線,使得它們與 x 軸共同構(gòu)成的容器可以容納最多的水。
返回容器可以儲存的最大水量。
說明:你不能傾斜容器。
示例:
輸入: height = [1,8,6,2,5,4,8,3,7]
輸出: 49
解釋: 圖中垂直線代表輸入數(shù)組 [1,8,6,2,5,4,8,3,7]。在此情況下,容器能夠容納水(表示為藍色部分)的最大值為 49。
JavaScript解決方案:
/**
* @param {number[]} height
* @return {number}
*/
function maxArea(height) {
let left = 0;
let right = height.length - 1;
let maxWater = 0;
while (left < right) {
// 計算當前容器的水量
const width = right - left;
const currentHeight = Math.min(height[left], height[right]);
const currentWater = width * currentHeight;
// 更新最大水量
maxWater = Math.max(maxWater, currentWater);
// 移動較矮的指針,因為移動較高的指針不會增加水量
if (height[left] < height[right]) {
left++;
} else {
right--;
}
}
return maxWater;
}
// 測試
console.log("\n盛最多水的容器:");
const heights = [1,8,6,2,5,4,8,3,7];
console.log("height =", heights);
console.log("最大水量 =", maxArea(heights)); // 49
13. 排序:快速排序
題目描述:
給你一個整數(shù)數(shù)組 nums,請你將該數(shù)組升序排列。
示例 1:
輸入: nums = [5,2,3,1]
輸出: [1,2,3,5]
示例 2:
輸入: nums = [5,1,1,2,0,0]
輸出: [0,0,1,1,2,5]
JavaScript解決方案:
/**
* @param {number[]} nums
* @return {number[]}
*/
function quickSort(nums) {
if (nums.length <= 1) return nums;
// 選擇基準元素
const pivotIndex = Math.floor(nums.length / 2);
const pivot = nums[pivotIndex];
// 分割數(shù)組
const left = [];
const right = [];
for (let i = 0; i < nums.length; i++) {
if (i === pivotIndex) continue;
if (nums[i] < pivot) {
left.push(nums[i]);
} else {
right.push(nums[i]);
}
}
// 遞歸排序并合并
return [...quickSort(left), pivot, ...quickSort(right)];
}
// 原地快速排序(更高效)
function quickSortInPlace(nums, left = 0, right = nums.length - 1) {
if (left >= right) return;
const pivotIndex = partition(nums, left, right);
quickSortInPlace(nums, left, pivotIndex - 1);
quickSortInPlace(nums, pivotIndex + 1, right);
return nums;
}
function partition(nums, left, right) {
// 選擇最后一個元素作為基準
const pivot = nums[right];
let i = left - 1;
for (let j = left; j < right; j++) {
if (nums[j] <= pivot) {
i++;
[nums[i], nums[j]] = [nums[j], nums[i]];
}
}
// 將基準放到正確位置
[nums[i + 1], nums[right]] = [nums[right], nums[i + 1]];
return i + 1;
}
// 測試
console.log("\n快速排序:");
console.log("簡單版本:");
console.log("[5,2,3,1] ->", quickSort([5,2,3,1])); // [1,2,3,5]
console.log("[5,1,1,2,0,0] ->", quickSort([5,1,1,2,0,0])); // [0,0,1,1,2,5]
console.log("\n原地排序版本:");
const arr1 = [5,2,3,1];
quickSortInPlace(arr1);
console.log("[5,2,3,1] ->", arr1); // [1,2,3,5]
const arr2 = [5,1,1,2,0,0];
quickSortInPlace(arr2);
console.log("[5,1,1,2,0,0] ->", arr2); // [0,0,1,1,2,5]
14. 二分查找
題目描述:
給定一個 n 個元素有序的(升序)整型數(shù)組 nums 和一個目標值 target,寫一個函數(shù)搜索 nums 中的 target,如果目標值存在返回下標,否則返回 -1。
示例 1:
輸入: nums = [-1,0,3,5,9,12], target = 9
輸出: 4
解釋: 9 出現(xiàn)在 nums 中并且下標為 4
示例 2:
輸入: nums = [-1,0,3,5,9,12], target = 2
輸出: -1
解釋: 2 不存在 nums 中因此返回 -1
JavaScript解決方案:
/**
* @param {number[]} nums
* @param {number} target
* @return {number}
*/
function search(nums, target) {
let left = 0;
let right = nums.length - 1;
while (left <= right) {
// 防止整數(shù)溢出
const mid = Math.floor(left + (right - left) / 2);
if (nums[mid] === target) {
return mid;
} else if (nums[mid] < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return -1;
}
// 二分查找變種:查找左邊界
function searchLeftBound(nums, target) {
let left = 0;
let right = nums.length; // 注意
while (left < right) {
const mid = Math.floor(left + (right - left) / 2);
if (nums[mid] >= target) {
right = mid;
} else {
left = mid + 1;
}
}
// 檢查left是否越界或找到target
if (left < 0 || left >= nums.length || nums[left] !== target) {
return -1;
}
return left;
}
// 測試
console.log("\n二分查找:");
const nums = [-1,0,3,5,9,12];
console.log("nums =", nums);
console.log("target = 9 ->", search(nums, 9)); // 4
console.log("target = 2 ->", search(nums, 2)); // -1
console.log("\n二分查找左邊界:");
const nums2 = [1,2,2,2,3,4,5];
console.log("nums =", nums2);
console.log("target = 2 ->", searchLeftBound(nums2, 2)); // 1
console.log("target = 6 ->", searchLeftBound(nums2, 6)); // -1
總結(jié)
現(xiàn)在每個算法題目都有了:
- 完整的題目描述 - 理解問題在問什么
- 清晰的示例 - 了解輸入輸出格式
- JavaScript解決方案 - 具體的實現(xiàn)代碼
- 測試代碼 - 驗證解決方案的正確性
這些題目覆蓋了算法面試中最核心的14個知識點,掌握了這些題目,你就有了應對大多數(shù)算法面試的基礎(chǔ)。記住,不僅要會寫代碼,更要理解算法背后的思想和原理!