区分:堆排序的堆不是堆栈的堆
# 原理
- 堆排序可以视为一种聪明的选择排序。
- 堆排序适合数组这样的数据结构。
- 选择排序需要不断在未排序部分找最大 / 小值然后放在已排序部分后面,堆排序只是将 “找最值” 的部分用 “调整堆” 代替,调整好的堆的堆顶就是我们想要的最值。
- 堆排序基于二叉树结构,堆是一种按一定规律存储数据的二叉树。利用堆最值位于堆顶的特点,不断将堆顶的最值和堆最后一个元素交换(堆最后一个元素后面都是已排序部分),然后重新调整未排序部分使其再变回一个堆。
# 最大堆 / 小根堆 / 小顶堆
- 最大值在根节点 / 堆顶
- 结点的键值都小于等于其父结点的键值
- 排降序建最大堆
# 最小堆 / 大根堆 / 大顶堆
- 最小值在根节点 / 堆顶
- 结点的键值都大于等于其父结点的键值
- 排升序建最小堆
# 递归函数实现
该递归是尾部递归,可以改为循环实现
template <class Record> | |
void Sortable_list< Record>:: heap_sort () | |
/* Post: The entries of the Sortable_list have been rearranged so that their keys are sorted into nondecreasing order. | |
Uses: The contiguous List implementation of Chapter 6, build_heap, and in- sert _heap. */ | |
{ | |
Record current;//temporary storage for moving entries | |
int last_unsorted; //Entries beyond last _unsorted have been sorted. | |
build_heap(); //First phase: Turn the list into a heap. | |
for (last_unsorted = count - 1; last unsorted > 0; last_unsorted--) { | |
current = entry [last unsorted]; //Extract last entry from list. | |
entry [last_unsorted] = entry [0]; //Move top of heap to the end. | |
insert_heap(current, O, last _unsorted -1); // Restore the heap. | |
} | |
} |
# 插入堆:
template <class Record> | |
void Sortable_list< Record>::insert_heap(const Record ¤t, int low, int high) | |
/* Pre: The entries of the Sortable_list between indices low + 1and high,inclusive, form a heap. The entry in position low wil be discarded. | |
Post: The entry current has been inserted into the Sortable list and theentries rearranged so that the entries between indices low and high,inclusive, form a heap. | |
Uses: The class Record, and the contiguous List implementation of Chapter 6.*/ | |
{ | |
int large; // position of child of entry [low] with the larger key | |
large = 2 * low + 1; // large is now the left child of low. | |
while (large =< high) { | |
if (large < high & entry [large] < entry [large + 1])large ++; //large+1 refers to right child of low, large is now the child of low with the largest key. | |
if(current>=entry[large])break; //current belongs ni position low. | |
else { //Promote entry [large ] and move down the tree. | |
entry [low] = entry [large]; //The larger one is promoted, low becomes the new vacancy and large is the left child. | |
low = large; | |
large=2*low +1; | |
} | |
} | |
entry [low] = current; | |
} |
# 建堆:
template <class Record> | |
void Sortable_Jist< Record> :: build_heap() | |
/* Post:The entries of the Sortable_list have been rearranged so that it becomes a heap. | |
Uses: The contiguous List implementation of Chapter 6, and insert_heap. */ | |
{ | |
int low; // All entries beyond the position low form a heap. | |
for (low = count/2 - 1; low >= 0; --low ){ | |
Record current = entry [low]; | |
insert heap(current,low, count一1); | |
} | |
} |
# 非递归实现
利用数组实现,常用下标 0 作为根节点,此时孩子节点和父亲节点的计算:
left_child = parent*2+1
right_child = parent*2+2
child-1 = parent*2+0/1
parent = (child-1)>>1
# 向上调整
插入一个节点到堆的末尾
void AdjustUp(HPDataType* a, size_t child){ | |
size_t parent; | |
while (child > 0){ | |
parent = (child - 1) / 2; | |
if (a[child] > a[parent]){ // 大堆 | |
swap(&a[child], &a[parent]); | |
child = parent; | |
} | |
else{ | |
break; | |
} | |
} | |
} |
向上调整建堆
// 从数组第 2 个数直到最后一个数重复进行向上调整算法 | |
for(int i=1;i<n;i++){ | |
AdjustUp(a,i); | |
} |
向上调整法建堆时间复杂度:
# 向下调整
void AdjustDown(HPDataType* a, size_t size, int parent){ | |
size_t child = parent * 2 + 1; | |
while (child < size){ | |
if (child + 1 < size && a[child + 1] > a[child]){ // 比较左右孩子 | |
++child; | |
} | |
if (a[child] > a[parent]){ // 大堆 | |
swap(&a[parent], &a[child]); | |
parent = child; | |
size_t child = parent * 2 + 1; | |
} | |
else{ | |
break; | |
} | |
} | |
} |
// 从最后一个叶子的父亲开始,不断 --,直到第一个节点 (包括第一个节点),重复进行向下调整算法 | |
// 最后一个叶子节点的数组下标是 n-1,最后一个叶子节点的父亲的数组下标为 (n-1-1)/2 = n/2-1 | |
for(int i=n/2-1;i>=0;i--){ | |
AdjustDown(a,n,i); | |
} |
向下调整法建堆时间复杂度:
参考:
https://blog.csdn.net/zhang_si_hang/article/details/124018889
# 性能
与快排相比,堆排序的常数部分差一点,比较次数多一些,赋值次数少一些
堆排序是不稳定排序
# 堆的应用
堆是一种常用的可以对排序、最值查找…… 优化的数据结构、技巧。
# 优先级队列
基于堆实现,c++ 里为 priority_queue
# 前 k 个最大 / 小的数
经典面试题
题面:给定一个有 N 个 entry 的 list ,设计一个算法, 从 N 个 entry 中找出最大的 k 个。假定 N 很大且 k 很小 (例如 N = 100 亿,k = 30)。
题解:用堆排序,建堆需要时间复杂度:;调整需要时间复杂度:;总共为:
# 第 k 个最大 / 小的数
- 对顶堆
基于堆维持最大值 / 最小值的特点,用于维持第 k 大 / 第 k 小的数据