华为机考高频题解析:快速排序、两数之和、二分查找
好的,我来帮你把这三道高频机考题逐一拆解,讲清楚思路、代码逻辑和关键细节,让你彻底搞懂它们。
1. 快速排序(quickSort)
题目描述
给定一个数组,使用快速排序算法将其升序排列。
核心思路
快速排序的核心是“分治思想”:
选基准(pivot):从数组中选一个元素作为基准(这里选中间元素)。
分区(partition):把数组分成三部分——比基准小的元素、等于基准的元素、比基准大的元素。
递归排序:对“比基准小”和“比基准大”的两个子数组分别递归排序。
合并结果:把排序后的左子数组、基准元素、排序后的右子数组合并成最终数组。
代码逐行解析
function quickSort(arr) {
// 递归终止条件:数组长度 <= 1 时,本身就是有序的,直接返回
if (arr.length <= 1) return arr;
// 1. 选基准:取数组中间的元素
const pivot = arr[Math.floor(arr.length / 2)];
// 2. 分区:把数组分成三部分
const left = arr.filter(x => x < pivot); // 比基准小的元素
const middle = arr.filter(x => x === pivot); // 等于基准的元素
const right = arr.filter(x => x > pivot); // 比基准大的元素
// 3. 递归排序 + 合并
return [...quickSort(left), ...middle, ...quickSort(right)];
}关键细节
**为什么选中间元素当基准?**这是为了避免极端情况(如数组已有序时)导致的时间复杂度退化,让分区更均衡。
filter** 的作用**:这是一种简洁的分区写法,但会额外创建数组,空间复杂度略高。在性能要求极高的场景下,可以用原地分区(双指针交换元素)来优化空间。时间复杂度:平均
O(n log n),最坏O(n²)(如数组已有序且选首/尾为基准时),但实际中是效率很高的排序算法。
2. 两数之和(twoSum)
题目描述
给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那两个整数,并返回它们的数组下标。
核心思路
这道题的最优解法是用**哈希表(Map)**来降低时间复杂度:
遍历数组:对于每个元素
nums[i],计算它与目标值的差值diff = target - nums[i]。检查哈希表:如果差值
diff已经在哈希表中,说明之前遍历过的某个元素和当前元素的和正好是target,直接返回这两个元素的下标。存入哈希表:如果差值不在哈希表中,就把当前元素的值和下标存入哈希表,继续遍历。
代码逐行解析
function twoSum(nums, target) {
const map = new Map(); // 哈希表:键是数组元素值,值是元素下标
for (let i = 0; i < nums.length; i++) {
const diff = target - nums[i]; // 计算当前元素需要的“补数”
// 如果补数已经在哈希表中,说明找到了答案
if (map.has(diff)) {
return [map.get(diff), i]; // 返回补数的下标和当前下标
}
// 否则,把当前元素和下标存入哈希表
map.set(nums[i], i);
}
return []; // 遍历完没找到,返回空数组
}关键细节
**为什么用哈希表?**哈希表的查找和插入操作都是
O(1),所以整个算法的时间复杂度是O(n),比暴力法(O(n²))高效很多。**为什么不能先把所有元素存入哈希表再遍历?**这样会导致无法处理“同一个元素被使用两次”的情况(如
nums = [3,3], target = 6)。边遍历边存入,就能保证每个元素只和它之前的元素配对。**哈希表存的是什么?**存的是“元素值 → 下标”的映射,这样可以通过值快速找到对应的下标。
3. 二分查找(binarySearch)
题目描述
给定一个升序排列的整数数组 arr 和一个目标值 target,写一个函数搜索 arr 中的 target,如果目标值存在返回它的下标,否则返回 -1。
核心思路
二分查找的前提是数组有序,它通过不断缩小搜索范围来找到目标值:
初始化边界:左边界
low = 0,右边界high = arr.length - 1。循环缩小范围:在
low <= high的条件下,计算中间位置mid。比较中间值:
如果
arr[mid] === target:找到目标,返回下标mid。如果
arr[mid] < target:目标在右半部分,更新左边界low = mid + 1。如果
arr[mid] > target:目标在左半部分,更新右边界high = mid - 1。
未找到:如果循环结束还没找到,返回
-1。
代码逐行解析
function binarySearch(arr, target) {
let low = 0, high = arr.length - 1; // 初始化左右边界
while (low <= high) { // 只要范围有效,就继续查找
const mid = Math.floor((low + high) / 2); // 计算中间位置(向下取整)
if (arr[mid] === target) {
return mid; // 找到目标,返回下标
} else if (arr[mid] < target) {
low = mid + 1; // 目标在右边,更新左边界
} else {
high = mid - 1; // 目标在左边,更新右边界
}
}
return -1; // 遍历完没找到,返回-1
}关键细节
**为什么用 **
Math.floor((low + high) / 2)**?**这是为了避免low + high数值过大导致溢出。在 JS 中数字精度较高,溢出风险小,但这是通用的安全写法。另一种等价写法是low + Math.floor((high - low) / 2)。**循环条件为什么是 **
low <= high**?**因为当low === high时,范围里还有最后一个元素需要检查。如果写成low < high,会漏掉这个元素。时间复杂度:
O(log n),每次循环都把搜索范围缩小一半,效率非常高。
总结
| 题目 | 核心考点 | 时间复杂度 | 空间复杂度 |
|---|---|---|---|
| 快速排序 | 分治思想、递归 | 平均 O(n log n),最坏 O(n²) | 平均 O(log n),最坏 O(n) |
| 两数之和 | 哈希表的使用、空间换时间 | O(n) | O(n) |
| 二分查找 | 有序数组的高效查找、边界处理 | O(log n) | O(1) |
| 这三道题都是华为机考的高频考点,理解它们的思路比背代码更重要。 |
如果需要,我可以帮你把这三道题的测试用例和边界情况整理成一份清单,让你写完代码后可以快速验证是否正确。需要吗?
(注:文档部分内容可能由 AI 生成)