文章摘要
O(n log n)时间复杂度是算法效率的重要指标,表示每个元素需参与log n次分组或合并操作。典型应用包括归并排序、堆排序等分治算法。以排序1000本书为例,采用分治策略:先将书分成两半各自排序再合并,每本书都会经历约10(log₂1000≈10)次分组合并。归并排序代码展示了如何递归拆分数组并有序合并,其时间复杂度为n次操作乘以log n层分解。堆排序通过建堆和反复调整实现同样效率。这类算法比O(n²)快得多,是排序算法的效率天花板,适用于需要高效处理大规模数据的场景。
一、什么是 O(n log n)?
- n:数据规模
- log n:每次操作都能把问题“分成两半”,比如二分法
- O(n log n):每个元素都要参与“分组”或“合并”log n次
常见场景:排序(归并排序、快速排序)、堆排序、某些高级数据结构的批量操作等。
二、生活化比喻
想象你有1000本书要按字母排序:
- 你不会一口气全排好,而是先分成两堆,各自排好,再合并。
- 每次分一半,分到最后每堆只剩一本书。
- 然后一层层合并,每本书都要经历“分组”和“合并”log n次。
就像开运动会,先班级选拔,再年级选拔,最后全校决赛,每个人都要参加每一轮。
三、C#代码示例:归并排序(Merge Sort)
void MergeSort(int[] arr, int left, int right)
{
if (left >= right) return;
int mid = (left + right) / 2;
MergeSort(arr, left, mid); // 分左边
MergeSort(arr, mid + 1, right); // 分右边
Merge(arr, left, mid, right); // 合并
}
void Merge(int[] arr, int left, int mid, int right)
{
int[] temp = new int[right - left + 1];
int i = left, j = mid + 1, k = 0;
while (i <= mid && j <= right)
{
if (arr[i] < arr[j])
temp[k++] = arr[i++];
else
temp[k++] = arr[j++];
}
while (i <= mid) temp[k++] = arr[i++];
while (j <= right) temp[k++] = arr[j++];
for (int t = 0; t < temp.Length; t++)
arr[left + t] = temp[t];
}
时间复杂度分析
- 分组次数:每次分一半,分到只剩一个元素,总共 log n 层。
- 每层操作量:每层都要把所有元素“合并”一遍,总共 n 次。
- 总操作量:n * log n
四、再举一个例子:堆排序(Heap Sort)
void HeapSort(int[] arr)
{
int n = arr.Length;
// 建堆
for (int i = n / 2 - 1; i >= 0; i--)
Heapify(arr, n, i);
// 取出堆顶元素,重建堆
for (int i = n - 1; i > 0; i--)
{
int temp = arr[0];
arr[0] = arr[i];
arr[i] = temp;
Heapify(arr, i, 0);
}
}
void Heapify(int[] arr, int n, int i)
{
int largest = i;
int l = 2 * i + 1;
int r = 2 * i + 2;
if (l < n && arr[l] > arr[largest]) largest = l;
if (r < n && arr[r] > arr[largest]) largest = r;
if (largest != i)
{
int swap = arr[i];
arr[i] = arr[largest];
arr[largest] = swap;
Heapify(arr, n, largest);
}
}
时间复杂度分析
- 建堆 O(n)
- 每次取出堆顶并重建堆 O(log n),共 n 次
- 总体 O(n log n)
五、总结口诀
- O(n log n) = 每个人都要参加 log n 轮比赛
- 排序算法的“天花板”:比 O(n²) 快得多,但比 O(n) 慢
六、常见O(n log n)算法
- 归并排序(Merge Sort)
- 堆排序(Heap Sort)
- 快速排序(Quick Sort,平均O(n log n))
- 平衡树/线段树/树状数组的批量操作
七、生活场景再补充
- 分组比赛:每个人都要打每一轮,轮数是 log n
- 分治法:每次分一半,分到最小再合并