Publicado · en mejora
Guía de Divide y vencerás · 6/6
Por ahora, este capítulo solo está disponible en inglés.
Divide and conquer is rarely something you write from scratch at work, but it runs inside many tools you use every day. This chapter shows where it appears, which library functions to reach for, and the mistakes that show up when you implement it yourself.
list.sort and Java's Arrays.sort for objects use Timsort, a merge-based sort; Java sorts primitive arrays with dual-pivot quicksort. std::stable_sort in C++ is typically a merge sort and std::sort an introsort (quicksort that falls back to heapsort).std::nth_element in C++ and numpy.partition (introselect by default) are quickselect variants with protection against bad pivots.n into two of size n/2, turning O(n^2) into O(n log n).git bisect binary-searches the commit history to find the change that introduced a bug.import heapq
import statistics
MOD = 1_000_000_007
print(pow(3, 10**18, MOD)) # built-in fast modular power
print(pow(3, -1, MOD)) # modular inverse (Python 3.8+)
data = [9, 1, 8, 2, 7, 3]
print(heapq.nsmallest(2, data)) # [1, 2], O(n log k)
print(statistics.median(data)) # 5.0
print(sorted(data)) # Timsort, stable, O(n log n)In C++, std::nth_element puts the k-th element in its sorted position and partitions the rest around it in linear time on average:
#include <algorithm>
#include <iostream>
#include <vector>
int main() {
std::vector<int> v{9, 1, 8, 2, 7, 3};
std::nth_element(v.begin(), v.begin() + 2, v.end());
std::cout << v[2] << '\n'; // 3, the third smallest
}log n deep, but a split that removes one element at a time (a bad pivot, a skewed tree) can overflow. Prefer loops for decrease-and-conquer and random pivots for quickselect.a[:mid] copies half the list. Slicing at every level is still O(n log n) overall, but it allocates heavily; pass index ranges and reuse one buffer when speed matters.[lo, hi) or closed [lo, hi] and stick to it. In a binary search with mid = (lo + hi) // 2, the update lo = mid never moves on a two-element range, so the loop runs forever.2^53; see the implementation chapter.O(n^2) when all values are equal. Use a three-way partition, as in the practice problems.Merge sort can also run without recursion by merging runs of width 1, 2, 4 and so on:
def merge_sort_bottom_up(a):
a = list(a)
n, width = len(a), 1
while width < n:
for lo in range(0, n, 2 * width):
mid, hi = min(lo + width, n), min(lo + 2 * width, n)
left, right = a[lo:mid], a[mid:hi]
i = j = 0
for k in range(lo, hi):
if j == len(right) or (i < len(left) and left[i] <= right[j]):
a[k] = left[i]
i += 1
else:
a[k] = right[j]
j += 1
width *= 2
return a
print(merge_sort_bottom_up([5, 2, 4, 7, 1, 3, 2, 6])) # [1, 2, 2, 3, 4, 5, 6, 7]This is also the shape external sorting and parallel merge passes take.
sorted, pow, heapq, std::nth_element or numpy.partition before writing your own.
0 comentarios
Iniciar sesión · Inicia sesión para dejar un comentario.
Sé el primero en comentar.