快排与堆排
本文复习一下快速排序和堆排序 2 种排序算法(为了多快好省地刷 leetcode )。
快排
主要思想:
通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
时间复杂度 \(O(n \log n)\) ,空间复杂度取决于是否使用递归实现。
代码实现:
int partition(vector<int> &v, int p, int r)
{
int x = v[r];
int i = p - 1;
for (int j = p; j < r; j++)
{
if (v[j] < x)
i = i + 1, swap(v[i], v[j]);
}
swap(v[i + 1], v[r]);
return i + 1;
}
void quickSort(vector<int> &v, int p, int r)
{
if (p < r)
{
int q = partition(v, p, r);
quickSort(v, p, q - 1);
quickSort(v, q + 1, r);
}
}
对 partition
函数的图解(图源为《算法导论》):
在这里的实现,我们默认区间的最右侧 v[r] = 4
为主元,将上面所示的数组分为 2 部分,左侧小于等于 4,右侧大于 4 。下面是函数中几个临时变量所表示的含义:
堆排
堆的性质:
子结点的键值总是小于(或者大于)它的父节点。
三个步骤:
- 建立大顶堆,这样
nums[0]
总是为最大的元素 - 把最大的的元素放在堆的最末尾 ,即
swap(nums[0], nums[j]), j = n-1, ..., 1
. - 调整堆,使其满足大顶堆的性质,即
heapify(nums, j, 0)
.
代码实现:
class Solution {
public:
vector<int> sortArray(vector<int>& nums)
{
heapSort(nums);
return nums;
}
void heapSort(vector<int> &nums)
{
int n = nums.size();
buildHeap(nums);
for (int i = n - 1; i >= 0; --i)
{
swap(nums[i], nums[0]); /* nums[0] is the max element. */
heapify(nums, i, 0);
}
}
/* O(n) time. */
void buildHeap(vector<int> &nums)
{
int n = nums.size();
for (int i = n / 2; i >= 0; --i)
heapify(nums, n, i);
}
/* @nums - a max-heap
* @n - make [0, n) be a heap
* @idx - heapify nums[idx]
* O(logn) time.
*/
void heapify(vector<int> &nums, int n, int idx)
{
int l = (idx << 1) + 1;
int r = l + 1;
int largest = idx;
if (l < n && nums[largest] < nums[l]) largest = l;
if (r < n && nums[largest] < nums[r]) largest = r;
if (largest != idx)
{
swap(nums[largest], nums[idx]);
heapify(nums, n, largest);
}
}
};
heapify
heapify
函数图解如下图所示。需要注意的是,图中数组下标是从 1 开始的,而上面的代码实现是从 0 开始的。
时间复杂度 \(O(\log n)\) .
buildHeap
在表示堆的数组中,范围 $\lfloor n/2 \rfloor $ 到 \(n-1\) 是叶子节点(下标从 0 开始),对于叶子节点,自然而然会满足堆的性质,对叶子节点调用 heapify
丝毫没有影响,因此不需要调整。这就是为什么 for
循环的范围是 size/2 -> 0
。
时间复杂度为 \(O(n)\) .
heapSort
调用 buildHeap
后的数组,是一个大顶堆,所以 v[0]
是最大的数字,我们把它交换到数组的最末尾处。然后对 [0, heapSize)
范围内的数字进行 heapify
,因为影响的只有位置 0 ,所以只需要调用一次 heapify(v, 0)
就能使得数组满足堆的性质。
时间复杂度 \(O(n\log n)\) .
heapSort
的图解如下: