堆排序
堆排序(Heap Sort)是一种基于比较的排序算法,利用堆这种数据结构来实现排序。堆是一种特殊的完全二叉树,分为最大堆和最小堆。
堆的定义
-
最大堆:对于每个节点,节点的值都大于或等于其子节点的值。
-
最小堆:对于每个节点,节点的值都小于或等于其子节点的值。
一、堆排序的步骤
堆排序(从最下开始)(时间复杂度为O(NlogN+N),N可省略,即为O(NlogN) 1.建堆(若要降序排序则建小堆,若要升序排序则建大堆) 2.排序
二、相关函数
typedef class {
public:
symbol* m_a;
int m_size;
int m_capacity;
}Heap;
//交换
void Swap(symbol* a, symbol* b);
//向下调整法
void AdjustDown(symbol* m_a, int size, int root);
//向上调整法
void AdjustUp(symbol* m_a, int size, int child);
//堆的初始化
void HeapInit(Heap* hp, symbol* a, int size);
//堆的排序
void HeapSort(Heap* hp);
//堆的删除
void HeapDestroy(Heap* hp);
//向堆内推入一个数
void HeapPush(Heap* hp, symbol pushnumber);
//推出堆顶的数
void HeapPop(Heap* hp);
//返回堆顶的数
symbol HeapTop(Heap* hp);
三、前提概要
求二叉树某一结点的孩子结点:左孩子lchild = parent * 2 + 1;右孩子rchild = parent * 2 + 2(或lchild++)
求某一结点的父节点:parent=(child-1)/2;
四、相关函数的实现
-
交换
void Swap(symbol* a1, symbol* b1) {
symbol tmp = *a1;
*a1 = *b1;
*b1 = tmp;
}
-
向下调整算法:时间复杂度为O(logN==h)
void AdjustDown(symbol* m_a, int size, int root) {
symbol parent = root;
symbol child = parent * 2 + 1;
while (child < size) {
if (child + 1 < size && m_a[child + 1] < m_a[child])//当建的是小堆时为<,当建的是大堆时为>
++child;
if (m_a[child] < m_a[parent])//当建的是小堆时为<,当建的是大堆时为>
{
Swap(&m_a[child], &m_a[parent]);
parent = child;
child = parent * 2 + 1;
}
else break;
}
}
-
向上调整算法:时间复杂度为O(logN==h)
void AdjustUp(symbol* m_a, int size, int child) {
symbol parent = (child - 1) / 2;
while (child > 0) {
if (m_a[child] < m_a[parent]) {
Swap(&m_a[child], &m_a[parent]);
child = parent;
parent = (child - 1) / 2;
}
else break;
}
}
-
堆的初始化
void HeapInit(Heap* hp, symbol* a, int size) {
hp->m_a = new int[size];
hp->m_capacity =hp->m_size= size;
memcpy(hp->m_a, a, sizeof(int)*size);
//建堆(时间复杂度为O(N),利用错位相减法求时间复杂度)
for (int i = (size - 1 - 1) / 2; i >= 0; i--) {
AdjustDown(hp->m_a, hp->m_size, i);
}
}
-
排序
void HeapSort(Heap*hp) {
//排序(时间复杂度为O(NlogN)
//通过建堆可选出最小(大)数位于堆顶,且左右子树是小(大)堆
//将堆顶的数与最后一个数交换,再从堆顶开始向下调整算法并将交换至末尾的数排除,即可选出次小(大)的数,将其置于最小(大)的前一位
//共有N个节点,故重复N次,即可完成降序排序
//每次向下调整算法的时间复杂度为O(logN),所以排序的时间复杂度为O(NlogN)
int end = hp->m_size - 1;
while (end > 0) {
Swap(&hp->m_a[0], &hp->m_a[end]);
AdjustDown(hp->m_a, end, 0);
--end;
}
}
-
向堆内推入一个数
//先将该数推入堆的末尾,再通过向上调整法和父亲比较,若小则交换,再和父亲比较直至堆顶,若大则停止
void HeapPush(Heap* hp, symbol pushnumber) {
if (hp->m_capacity == hp->m_size) {
hp->m_capacity *= 2;
symbol* tmp = (symbol*)realloc(hp->m_a, sizeof(symbol) * hp->m_capacity);
if (tmp == NULL)
{
cout << "内存不足";
exit(-1);
}
else hp->m_a = tmp;
}
hp->m_a[hp->m_size] = pushnumber;
++hp->m_size;
AdjustUp(hp->m_a, hp->m_size, hp->m_size-1);
}
-
推出堆顶的数
//将堆顶的数和最后一个数交换,然后通过size-1删去交换到最后一个数的堆顶,因为左右子树都是小堆,故进行一次向下调整法一个获得一个小堆
void HeapPop(Heap* hp) {
Swap(&hp->m_a[0], &hp->m_a[hp->m_size-1]);
--hp->m_size;
AdjustDown(hp->m_a, hp->m_size, 0);
}
//返回堆顶的数
symbol HeapTop(Heap* hp) {
return hp->m_a[0];
}
-
删除堆
//堆的删除
void HeapDestroy(Heap* hp) {
delete hp->m_a;
hp->m_a = NULL;
hp->m_capacity = hp->m_size = 0;
}