二、堆排序
堆是一种特殊的完全二叉树,分为大根堆和小根堆。
大根堆的每个节点的值都大于或等于其左右子节点的值,小根堆则相反,每个节点的值都小于或等于其左右子节点的值。(大根堆小根堆只看父子关系) 
堆排序的基本思想是将待排序的序列构建成一个堆,然后依次取出堆顶元素并调整堆,直到整个序列有序。
1.算法思想
建堆:将给定的数组构建成一个大根堆(或小根堆)。从最后一个非叶子节点开始,依次向上调整每个节点,使其满足堆的性质。
交换元素:将堆顶元素与堆的最后一个元素交换位置,此时堆顶元素是当前堆中的最大(或最小)值,将其取出,放到已排序序列的末尾。
调整堆:交换元素后,堆的性质可能被破坏,需要对剩余的元素重新调整堆,使其再次满足堆的性质。重复步骤 2 和 3,直到堆中只剩下一个元素,此时整个数组已经有序。

1->调整成大根堆
(1)从最后一棵子树开始,从后往前调整
(2)每次调整,从上往下
(3)整体调整成大根堆
具体调整:
定义一个临时变量tmp,把根放到tmp里,找左右孩子的最大值,和tmp比较,如果比tmp大,则放到根的位置,继续递归比较新的左右孩子的最大值
调整顺序:
调整子树:
调整完成:
2->根和待排序的最后一个交换
根是最大的,交换后则视作有序
3->再次调整成大根堆
2.代码实现
//堆排序
void HeapAdjust(int* arr, int start, int end)
{
int tmp = arr[start];
for (int i = 2 * start + 1; i <= end;i=2*i+1)//从上往下
{
if (i < end && arr[i] < arr[i + 1])//如果有右孩子,且左孩子的值小于右孩子
{
i++;
}//i一定是左右孩子的最大值
if (arr[i] > tmp)
{
arr[start] = arr[i];
start = i;
}
else
{
break;
}
}
arr[start] = tmp;
}
void HeapSort(int* arr, int len)
{
//第一次建立大根堆(从后往前,多次调整)//根分别为4 3 2 1的子树
for (int i = (len-1-1)/2; i >= 0; i--)//i的初值与结点总数有关,i=总结点数len-根-
{
HeapAdjust(arr, i, len - 1);
}
//每次将根和待排序的最后一个交换,然后再次调整(注意是待排序部分)
int tmp;//用于交换
for (int i = 0; i < len - 1; i++)
{
tmp = arr[0];
arr[0] = arr[len - 1 - i];
arr[len - 1 - i] = tmp;
//再次调整
HeapAdjust(arr, 0, len - 1 - i - 1);
}
return;
}
3.复杂度分析
建立大根堆的时间复杂度:O(n)
每次调整大根堆时间复杂度:O(logn),调整次数n-1次,总时间复杂度O(nlogn)
综合建堆和排序两个阶段,堆排序的时间复杂度为O(n+nlogn),通常简化为O(nlogn)。这是堆排序的平均时间复杂度;
空间复杂度O(1);
不稳定