1. 堆
1.1 堆
堆(heap)是计算机科学中一类特殊的数据结构的统称。堆通常是一个可以被看做一棵树的数组对象。堆总是满足下列性质:
- 堆中某个结点的值总是不大于或不小于其父结点的值;
- 堆总是一棵完全二叉树。
1.2 分类
- 最大堆:根结点最大的堆
- 最小堆:根结点最小的堆
1.3 建堆步骤
- 从最后一个非叶子节点开始(自下而上构建每一个子树),对每一个非叶子节点进行向下调整操作(最后一个节点的父节点 i=(end-1) / 2,其中end=n-1);
- i–,继续向前调整非叶子节点,直到调整完所有的非叶子节点,得到相应的最小堆或者最大堆。
向下调整操作步骤:
- 找出要调整节点的子节点中的最大值;
- 父子节点进行比较,若父节点>子节点,则进行交换操作。
- 将父节点更新为此时的子节点,继续循环1、2步骤;
- 直到更新完变动的子树结束。
1.4 建堆代码
void swap(int* a, int* b)
{
int tmp = *a;
*a = *b;
*b = tmp;
}
//1.向下调整(建最大堆)
void ShiftDown(int* arr, int n, int parent)
{
//1.将左右子节点中较大的一个与父节点进行比较,若父节点小于他们两个中的较大值,则交换父子节点,更新父节点继续比较。
int child = parent * 2 + 1; //左孩子节点下标
while (child < n)
{
if (child + 1 < n - 1 && arr[child + 1] > arr[child])
child++;
if (arr[child] > arr[parent])
{
swap(&arr[child], &arr[parent]);
parent = child;
child = parent * 2 + 1;
}
else
break;
}
}
//1.向下调整(建最大堆)——递归
void ShiftDown1(int* arr, int n, int parent)
{
if (parent >= n) //递归出口
{
return;
}
int child = parent * 2 + 1; //左孩子
if (child + 1 < n && arr[child + 1] > arr[child])
child++;
if (child<n && arr[child]>arr[parent])
{
swap(&arr[child], &arr[parent]);
ShiftDown1(arr, n, child);
}
}
//2.建堆
void CreateHeap(int* arr, int n)
{
//从第一个非叶子节点开始建堆(最后一个非叶子节点,即为最后一个节点的父节点)
int end = n - 1;
int unleaf = (end - 1) / 2;
while (unleaf >= 0)
{
ShiftDown(arr, n, unleaf);
unleaf--;
}
}
2. 堆排序
2.1 基本思想
堆排序属于交换排序,每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的末尾位置(即后面得到了已排序序列),直到全部待排序的数据元素排完。
2.2 堆排序算法步骤
- 建堆(升序:建最大堆;降序:建最小堆);
- 交换堆顶元素与最后一个未排序元素(初始end=n-1)——(交换完毕后,说明区间(end,n-1]已为有序区间);
- 重新调整堆(原因:堆顶元素发生改变),时期仍然是最大堆或最小堆;
- 重复2、3步骤,直到所有数据均有序,即end==0。
2.3 堆排序代码
void HeapSort(int* arr, int n)
{
for (int i = n-1; i >0; --i)
{
swap(&arr[0], &arr[i]);
ShiftDown(arr,i,0); //调整函数——调整堆顶元素到合适位置
}
}
2.4 算法特性
- 时间复杂度:O(N*logN)
- 空间复杂度:O(1)
- 稳定性:不稳定