排序 | 堆排序
先用一段 C++ 代码来看看推排序的过程:
模拟 sort_heap 的过程
1 #include <iostream> 2 #include <algorithm> 3 using namespace std; 4 5 bool cmp(const int lhs, const int rhs) 6 { 7 return lhs > rhs; 8 } 9 10 int main() 11 { 12 int arr[] = {4, 2, 10, 9, 5, 6, 8, 6, 7, 1, 3}; 13 make_heap(arr, arr+10); // 构造堆 14 for(int i = 11; i >= 0; --i) { // 不断 pop_heap 15 pop_heap(arr, arr+i); // heap 的 pop 非真正意义上的 pop,其实是 swap(arr, arr+i-1),然后再 percolate_down(下溯) 16 for(int i = 0; i < 11; ++i) cout << arr[i] << ' '; 17 cout << endl; 18 } 19 20 return 0; 21 }
主要就是 make_heap、pop_heap
make_heap:建堆,默认最大堆-》得到递增数组(因为pop_heap是把堆顶取出依次放到后面)
pop_heap:arr[0]和arr[i]交换,并维护arr[0]到arr[i-1]的堆结构。
meke_heap 里面可以细分为从下往上建堆,递归的来说就是要维护 i,就得维护 i.left 和 i.right。
而 pop_heap 就是 n 次维护 root 节点。
所以,实际上,只需要写一个 perlocateDown 来维护以 i 为 root 的子堆。
// perlocate_down
1 void perlocateDown(int arr[], int n, int i) 2 { 3 int l = 2*i + 1; 4 int r = 2*i + 2; 5 int largest = i; 6 if(l < n && arr[l] > arr[largest]) 7 largest = l
9 if(r < n && arr[r] > arr[largest]) 10 largest = r; 11 12 if(largest == i) return; 13 swap(arr[i], arr[largest]); 14 perlocateDown(arr, n, largest); 15 }
维护堆的过程简单来说就是不断往下沉。
后面的的 meke_heap 和 pop_heap 也就不难写了。
makeHeap(arr, n) :
for i = n/2 downto 1 : // 所有非叶子节点
perlocateDown(arr, n, i)
popHeap(arr, n) :
swap(arr[0], arr[n-1])
perlocateDown(arr, n, 0)
然后 sortHeap 也容易写出来了:
sortHeap(arr, n) :
makeHeap(arr, n)
for i = n downto 0 :
popHeap(arr, i)
另外,还有个push_heap,加入一个元素至数组尾部,即作为堆的叶子。然后再上溯。
上溯过程比较简单:
perlocateUp(arr, i) :
parent = (i-1)/2
if i==0 or arr[i] < arr[parent] : return
swap(arr[parent], arr[i])
perlocate(arr, parent)