搜索与排序
搜索和排序是最基础的算法,在面试中出现频率极高。掌握高效的搜索和排序算法对于优化程序性能至关重要。
二分搜索(Binary Search)
基本二分搜索
def binary_search(arr, target):
left, right = 0, len(arr) - 1
while left <= right:
mid = (left + right) // 2
if arr[mid] == target:
return mid
elif arr[mid] < target:
left = mid + 1
else:
right = mid - 1
return -1
查找边界
查找第一个等于 target 的位置
def find_first(arr, target):
left, right = 0, len(arr) - 1
result = -1
while left <= right:
mid = (left + right) // 2
if arr[mid] == target:
result = mid
right = mid - 1 # 继续向左查找
elif arr[mid] < target:
left = mid + 1
else:
right = mid - 1
return result
查找最后一个等于 target 的位置
def find_last(arr, target):
left, right = 0, len(arr) - 1
result = -1
while left <= right:
mid = (left + right) // 2
if arr[mid] == target:
result = mid
left = mid + 1 # 继续向右查找
elif arr[mid] < target:
left = mid + 1
else:
right = mid - 1
return result
应用:搜索旋转排序数组
def search_rotated(nums, target):
left, right = 0, len(nums) - 1
while left <= right:
mid = (left + right) // 2
if nums[mid] == target:
return mid
# 左半部分有序
if nums[left] <= nums[mid]:
if nums[left] <= target < nums[mid]:
right = mid - 1
else:
left = mid + 1
# 右半部分有序
else:
if nums[mid] < target <= nums[right]:
left = mid + 1
else:
right = mid - 1
return -1
排序算法
1. 快速排序(Quick Sort)
def quick_sort(arr):
if len(arr) <= 1:
return arr
pivot = arr[len(arr) // 2]
left = [x for x in arr if x < pivot]
middle = [x for x in arr if x == pivot]
right = [x for x in arr if x > pivot]
return quick_sort(left) + middle + quick_sort(right)
# 原地排序版本
def quick_sort_inplace(arr, low, high):
if low < high:
pi = partition(arr, low, high)
quick_sort_inplace(arr, low, pi - 1)
quick_sort_inplace(arr, pi + 1, high)
def partition(arr, low, high):
pivot = arr[high]
i = low - 1
for j in range(low, high):
if arr[j] <= pivot:
i += 1
arr[i], arr[j] = arr[j], arr[i]
arr[i + 1], arr[high] = arr[high], arr[i + 1]
return i + 1
- 时间复杂度:平均 O(n log n),最坏 O(n²)
- 空间复杂度:O(log n)
- 稳定性:不稳定
2. 归并排序(Merge Sort)
def merge_sort(arr):
if len(arr) <= 1:
return arr
mid = len(arr) // 2
left = merge_sort(arr[:mid])
right = merge_sort(arr[mid:])
return merge(left, right)
def merge(left, right):
result = []
i = j = 0
while i < len(left) and j < len(right):
if left[i] <= right[j]:
result.append(left[i])
i += 1
else:
result.append(right[j])
j += 1
result.extend(left[i:])
result.extend(right[j:])
return result
- 时间复杂度:O(n log n)
- 空间复杂度:O(n)
- 稳定性:稳定
3. 堆排序(Heap Sort)
import heapq
def heap_sort(arr):
heap = []
for num in arr:
heapq.heappush(heap, num)
result = []
while heap:
result.append(heapq.heappop(heap))
return result
# 原地堆排序
def heapify(arr, n, i):
largest = i
left = 2 * i + 1
right = 2 * i + 2
if left < n and arr[left] > arr[largest]:
largest = left
if right < n and arr[right] > arr[largest]:
largest = right
if largest != i:
arr[i], arr[largest] = arr[largest], arr[i]
heapify(arr, n, largest)
def heap_sort_inplace(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)
- 时间复杂度:O(n log n)
- 空间复杂度:O(1)
- 稳定性:不稳定
4. 计数排序(Counting Sort)
def counting_sort(arr):
if not arr:
return arr
min_val, max_val = min(arr), max(arr)
count = [0] * (max_val - min_val + 1)
for num in arr:
count[num - min_val] += 1
result = []
for i, cnt in enumerate(count):
result.extend([i + min_val] * cnt)
return result
- 时间复杂度:O(n + k),k 是值域范围
- 空间复杂度:O(k)
- 稳定性:稳定
- 适用场景:整数排序,值域范围较小
5. 基数排序(Radix Sort)
def radix_sort(arr):
max_val = max(arr)
exp = 1
while max_val // exp > 0:
counting_sort_by_digit(arr, exp)
exp *= 10
return arr
def counting_sort_by_digit(arr, exp):
n = len(arr)
output = [0] * n
count = [0] * 10
for i in range(n):
index = (arr[i] // exp) % 10
count[index] += 1
for i in range(1, 10):
count[i] += count[i - 1]
for i in range(n - 1, -1, -1):
index = (arr[i] // exp) % 10
output[count[index] - 1] = arr[i]
count[index] -= 1
for i in range(n):
arr[i] = output[i]
- 时间复杂度:O(d × (n + k)),d 是位数
- 空间复杂度:O(n + k)
- 稳定性:稳定
排序算法比较
| 算法 | 平均时间复杂度 | 最坏时间复杂度 | 空间复杂度 | 稳定性 |
|---|---|---|---|---|
| 快速排序 | O(n log n) | O(n²) | O(log n) | 不稳定 |
| 归并排序 | O(n log n) | O(n log n) | O(n) | 稳定 |
| 堆排序 | O(n log n) | O(n log n) | O(1) | 不稳定 |
| 计数排序 | O(n + k) | O(n + k) | O(k) | 稳定 |
| 基数排序 | O(d × n) | O(d × n) | O(n + k) | 稳定 |
常见题目
1. 合并两个有序数组
def merge_sorted_arrays(nums1, m, nums2, n):
i, j, k = m - 1, n - 1, m + n - 1
while i >= 0 and j >= 0:
if nums1[i] > nums2[j]:
nums1[k] = nums1[i]
i -= 1
else:
nums1[k] = nums2[j]
j -= 1
k -= 1
while j >= 0:
nums1[k] = nums2[j]
j -= 1
k -= 1
2. 寻找峰值
def find_peak_element(nums):
left, right = 0, len(nums) - 1
while left < right:
mid = (left + right) // 2
if nums[mid] > nums[mid + 1]:
right = mid
else:
left = mid + 1
return left
3. 数组中的第 K 个最大元素
import heapq
def find_kth_largest(nums, k):
return heapq.nlargest(k, nums)[-1]
# 使用快速选择
def find_kth_largest_quickselect(nums, k):
def quick_select(left, right, k_smallest):
if left == right:
return nums[left]
pivot_index = partition(left, right)
if k_smallest == pivot_index:
return nums[k_smallest]
elif k_smallest < pivot_index:
return quick_select(left, pivot_index - 1, k_smallest)
else:
return quick_select(pivot_index + 1, right, k_smallest)
def partition(left, right):
pivot = nums[right]
i = left
for j in range(left, right):
if nums[j] <= pivot:
nums[i], nums[j] = nums[j], nums[i]
i += 1
nums[i], nums[right] = nums[right], nums[i]
return i
return quick_select(0, len(nums) - 1, len(nums) - k)
总结
搜索和排序是算法的基础:
- 二分搜索:O(log n) 时间查找,适用于有序数组
- 快速排序:平均 O(n log n),适合通用排序
- 归并排序:稳定排序,O(n log n) 时间
- 堆排序:原地排序,O(n log n) 时间
- 特殊排序:计数排序、基数排序适用于特定场景
掌握这些算法能够帮助你高效解决各种问题。
接下来,让我们学习递归与回溯,这是解决复杂问题的重要方法。
