1. 基础算法快速复习指南
作为程序员的基本功,排序、二分、前缀和、差分、双指针这些算法概念看似简单,但在实际面试和工程应用中却经常成为区分候选人水平的关键指标。我见过太多候选人因为对这些基础算法理解不够深入而在白板编程环节翻车。本文将用最直白的语言,结合典型例题,带大家快速过一遍这些必须掌握的算法核心。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 排序算法精要
2.1 三大经典排序实现
快速排序的partition操作是核心,我习惯用这个模板:
python复制def quick_sort(arr, l, r):
if l >= r: return
i, j = l, r
pivot = arr[(l + r) // 2]
while i <= j:
while arr[i] < pivot: i += 1
while arr[j] > pivot: j -= 1
if i <= j:
arr[i], arr[j] = arr[j], arr[i]
i += 1
j -= 1
quick_sort(arr, l, j)
quick_sort(arr, i, r)
归并排序的merge操作需要注意边界:
python复制def merge_sort(arr, l, r):
if l >= r: return
mid = (l + r) // 2
merge_sort(arr, l, mid)
merge_sort(arr, mid + 1, r)
tmp = []
i, j = l, mid + 1
while i <= mid and j <= r:
if arr[i] <= arr[j]:
tmp.append(arr[i])
i += 1
else:
tmp.append(arr[j])
j += 1
tmp.extend(arr[i:mid + 1])
tmp.extend(arr[j:r + 1])
arr[l:r + 1] = tmp
堆排序的关键在于heapify:
python复制def heapify(arr, n, i):
largest = i
l = 2 * i + 1
r = 2 * i + 2
if l < n and arr[l] > arr[largest]:
largest = l
if r < n and arr[r] > arr[largest]:
largest = r
if largest != i:
arr[i], arr[largest] = arr[largest], arr[i]
heapify(arr, n, largest)
def heap_sort(arr):
n = len(arr)
for i in range(n // 2 - 1, -1, -1):
heapify(arr, n, i)
for i in range(n - 1, 0, -1):
arr[0], arr[i] = arr[i], arr[0]
heapify(arr, i, 0)
2.2 排序算法选择策略
- 数据量小(n<100):插入排序实际更快
- 数据基本有序:冒泡排序有优势
- 需要稳定性:归并排序或计数排序
- 内存受限:堆排序是唯一选择
- 平均情况:快速排序通常最优
注意:Java的Arrays.sort()对原始类型用双轴快排,对象用TimSort(归并优化)
3. 二分查找的魔鬼细节
3.1 标准模板与变种
基础二分模板必须死记:
python复制def binary_search(nums, target):
l, r = 0, len(nums) - 1
while l <= r:
mid = (l + r) // 2
if nums[mid] == target:
return mid
elif nums[mid] < target:
l = mid + 1
else:
r = mid - 1
return -1
变种1:找第一个大于等于target的(lower_bound)
python复制def lower_bound(nums, target):
l, r = 0, len(nums)
while l < r:
mid = (l + r) // 2
if nums[mid] >= target:
r = mid
else:
l = mid + 1
return l
变种2:找最后一个小于等于target的
python复制def upper_bound(nums, target):
l, r = 0, len(nums)
while l < r:
mid = (l + r) // 2
if nums[mid] > target:
r = mid
else:
l = mid + 1
return l - 1
3.2 二分答案法
当问题可以转化为"求满足条件的最大/最小值"时,考虑二分答案。例如leetcode 410题分割数组最大值:
python复制def split_array(nums, m):
def check(x):
total = 0
cnt = 1
for num in nums:
if total + num > x:
cnt += 1
total = num
else:
total += num
if cnt > m:
return False
return True
left = max(nums)
right = sum(nums)
while left < right:
mid = (left + right) // 2
if check(mid):
right = mid
else:
left = mid + 1
return left
4. 前缀和与差分技巧
4.1 前缀和的应用场景
一维前缀和模板:
python复制prefix = [0] * (len(nums) + 1)
for i in range(len(nums)):
prefix[i + 1] = prefix[i] + nums[i]
# 查询区间[i,j]和
sum_ij = prefix[j + 1] - prefix[i]
二维前缀和(leetcode 304题):
python复制class NumMatrix:
def __init__(self, matrix):
m, n = len(matrix), len(matrix[0]) if matrix else 0
self.prefix = [[0] * (n + 1) for _ in range(m + 1)]
for i in range(m):
row_sum = 0
for j in range(n):
row_sum += matrix[i][j]
self.prefix[i + 1][j + 1] = self.prefix[i][j + 1] + row_sum
def sumRegion(self, row1, col1, row2, col2):
return (self.prefix[row2 + 1][col2 + 1] - self.prefix[row1][col2 + 1]
- self.prefix[row2 + 1][col1] + self.prefix[row1][col1])
4.2 差分数组的妙用
差分是前缀和的逆操作,适用于区间更新+单点查询场景:
python复制# 初始化差分数组
diff = [0] * (len(nums) + 1)
diff[0] = nums[0]
for i in range(1, len(nums)):
diff[i] = nums[i] - nums[i - 1]
# 区间[i,j]加val
def add(i, j, val):
diff[i] += val
if j + 1 < len(diff):
diff[j + 1] -= val
# 还原数组
res = [0] * len(nums)
res[0] = diff[0]
for i in range(1, len(nums)):
res[i] = res[i - 1] + diff[i]
例题:航班预订统计(leetcode 1109题):
python复制def corpFlightBookings(bookings, n):
diff = [0] * (n + 1)
for first, last, seats in bookings:
diff[first - 1] += seats
if last < n:
diff[last] -= seats
res = []
curr = 0
for i in range(n):
curr += diff[i]
res.append(curr)
return res
5. 双指针的经典模式
5.1 同向双指针
滑动窗口模板(leetcode 3题最长无重复子串):
python复制def lengthOfLongestSubstring(s):
seen = set()
left = 0
res = 0
for right in range(len(s)):
while s[right] in seen:
seen.remove(s[left])
left += 1
seen.add(s[right])
res = max(res, right - left + 1)
return res
快慢指针(leetcode 26题去重):
python复制def removeDuplicates(nums):
if not nums: return 0
slow = 0
for fast in range(1, len(nums)):
if nums[fast] != nums[slow]:
slow += 1
nums[slow] = nums[fast]
return slow + 1
5.2 相向双指针
两数之和(leetcode 167题):
python复制def twoSum(numbers, target):
left, right = 0, len(numbers) - 1
while left < right:
s = numbers[left] + numbers[right]
if s == target:
return [left + 1, right + 1]
elif s < target:
left += 1
else:
right -= 1
return [-1, -1]
接雨水问题(leetcode 42题):
python复制def trap(height):
left, right = 0, len(height) - 1
left_max = right_max = 0
res = 0
while left < right:
if height[left] < height[right]:
if height[left] >= left_max:
left_max = height[left]
else:
res += left_max - height[left]
left += 1
else:
if height[right] >= right_max:
right_max = height[right]
else:
res += right_max - height[right]
right -= 1
return res
6. 综合应用实例
6.1 例题1:区间合并(leetcode 56题)
python复制def merge(intervals):
intervals.sort(key=lambda x: x[0])
merged = []
for interval in intervals:
if not merged or merged[-1][1] < interval[0]:
merged.append(interval)
else:
merged[-1][1] = max(merged[-1][1], interval[1])
return merged
6.2 例题2:寻找重复数(leetcode 287题)
python复制def findDuplicate(nums):
slow = fast = nums[0]
while True:
slow = nums[slow]
fast = nums[nums[fast]]
if slow == fast:
break
slow = nums[0]
while slow != fast:
slow = nums[slow]
fast = nums[fast]
return fast
6.3 例题3:最大子数组和(leetcode 53题)
python复制def maxSubArray(nums):
curr_sum = max_sum = nums[0]
for num in nums[1:]:
curr_sum = max(num, curr_sum + num)
max_sum = max(max_sum, curr_sum)
return max_sum
7. 常见错误与调试技巧
-
二分查找的死循环问题:
- 检查循环条件是否包含等号
- 确认左右指针移动是否正确(mid±1)
- 打印每次循环的l,r,mid值
-
前缀和索引越界:
- 前缀和数组通常要开n+1大小
- 查询区间和时注意是prefix[r+1]-prefix[l]
-
差分数组更新错误:
- 区间更新时要考虑右边界+1的位置
- 还原数组时要从左到右累加
-
双指针边界条件:
- 滑动窗口要检查右指针是否越界
- 快慢指针要处理空输入情况
调试建议:对于复杂问题,先在白板上画出指针移动示意图,再用小规模测试用例手动模拟算法执行过程。
