堆排序
堆:堆是具有以下性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;或者每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆
大顶堆:arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2]
小顶堆:arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2]
基本思想:
将待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点。将其与末尾元素进行交换,此时末尾就为最大值。然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列了。(将最大元素放在末尾)
a. 将无需序列构建成一个堆,一般升序采用大顶堆,降序采用小顶堆
1.无序数组
2.从最后一个非叶子结点开始(叶结点自然不用调整,第一个非叶子结点 arr.length/2-1=5/2-1=1,也就是下面的6结点),从左至右,从下至上进行调整
3.找到第二个非叶节点4,由于[4,9,8]中9元素最大,4和9交换
交换导致了子根[4,5,6]结构混乱,继续调整,[4,5,6]中6最大,交换4和6,构成大顶堆
b. 将堆顶元素与末尾元素交换,将最大元素”沉”到数组末端;
c. 重新调整结构,使其满足堆定义,然后继续交换堆顶元素与当前末尾元素,反复执行调整+交换步骤,直到整个序列有序。
1.将堆顶元素9和末尾元素4进行交换
2.重新调整结构,使其继续满足堆定义
3.再将堆顶元素8与末尾元素5进行交换,得到第二大元素8
最后得到有序数组
#include <iostream>
using namespace std;
void heapfy(int A[], int len);
void buildheap(int A[], int idx, int max);
int main() {
int A[] = { 4,6,8,5,9 };
int len = sizeof(A) / sizeof(A[0]);
heapfy(A, len);
//输出结果
for (int i = 0; i < len; ++i) {
cout << A[i] << " ";
}
cout << endl;
system("pause");
return 0;
}
void heapfy(int A[], int len) {
//从len / 2 - 1开始到0递减,对每一个节点执行heapfy,最后实现把最大的数字放在第0个的位置
for (int i = len / 2 - 1; i >= 0; --i) {
buildheap(A, i, len);
}
//把堆的第0个数字和第len-1个交换
for (int i = len - 1; i >= 1; --i) {//i = 0 时只剩一个数字不需要交换
int temp = A[i];
A[i] = A[0];
A[0] = temp;
buildheap(A, 0, i);//建立下一个最大堆,这次从0开始,最大是i(i在循环中从len-1开始减小)
}
}
void buildheap(int A[], int idx, int max) {
int left = 2 * idx + 1;//idx的左孩子标号
int right = left + 1;//idx的右孩子标号
int largest = idx;//初始设为idx节点自己
if (left < max && A[left] > A[idx])
largest = left;//这里只记录标号,后面才交换
//注意!下面这时候A[right]不能和idx比了,要跟largest比,因为可能在上一个if中被改变了,比如idx= 6,left= 8,right= 7
if (right < max && A[right] > A[largest])
largest = right;
if (largest != idx) {//如果largest不是初始值了的话
int temp = A[idx];
A[idx] = A[largest];
A[largest] = temp;
buildheap(A, largest, max);//注意这里的递归调用
//这里对第largest节点递归调用,因为刚交换了idx和largest的数字
//需要保证,如果largest有子节点的话,它的子节点也遵循最大堆原则(父节点最大)
}
}