本篇不是非常全面的教程,主要是堆排序的思路,希望能帮助到你。
堆排序给你一个数组,对这个数组进行排序。一个数组可以看成一个完全二叉树。
数组显然不是一个二叉树。那为什么说这玩意是二叉树,因为我们可以通过下标操作方式按照二叉树对其进行操作。
也就是让这个数组变成逻辑上的二叉树(蓝色是数组下标)。
堆排序步骤(注意这个不是最终版本,还要优化)
1.构建完全二叉树
把数组看成一个完全二叉树,计算时坐标按照下边:
父==>子:左孩子:2*i+1 右孩子:2*i+2;
子==>父:(i-1)/2
注意这里的i都是数组下标。
2.从数组最后一个叶子的父节点开始,依次进行堆调整heapify(图中红色顺序)
最后一个叶子下标就是数组长度-1 ,这个元素就是最后一个叶子。它的父节点对应数组就是(数组长度-1-1)/2 。从这个节点开始,把这颗小树调整为大根树,比较其与左右子节点的值,选择最大的节点作为父节点,并确保父节点大于或等于子节点。若发生交换,继续对被交换的子节点进行堆调整,以保持堆的性质。然后不断往前移动。挨个调整,等到遍历到整个树的根节点,那么这就是一个大根堆。(按照红色顺序)而这个树的根节点一定是最大的元素。
3.把最大的挑出来
把整棵树的根节点拿出来和数组最后一个下标的元素交换,而且交换后最后一个元素不参与这个排序过程了。就当组数末尾少了一个元素。这个元素不跟前边的玩了。
4.把剩余的再挑出一个最大的元素,再拿出来。
然后前边数组长度-1个元素再次进行步骤2,走一遍之后又挑出来一个最大的,再与新的数组末尾,也就是下标为(数组长度-1-1)交换。交换后这个元素也不参与前边剩余元素竞争了。
5.重复2,3,4操作,得到的就是一个大根堆。
就是不断从这一堆东西里挑出一个最大的按顺序从后往前放。得到的就是一个递增的数组。
聪明的你一定会发现:
在整棵树的根节点与最后一个元素交换后。此时整个树,只有以根节点为根的树是不一定满足二叉堆的性质的,其余的所有子树都满足二叉堆的性质,所以只需要对根节点再做一次堆化操作即可。比较跟节点与两个叶子节点谁最大,然后交换,让最大的上来。交换后,根节点成为了一个子节点,然后以这个子节点为根节点的树就不一定满足大根堆的性质了(最大元素一定是跟节点),所以要再次把以这个子节点为根节点的树调整。直到不发生交换,交换必定要调整。
优化后的堆排序步骤
1.构建大根堆:
将给定的数组转换为逻辑上的完全二叉树,通过比较父节点和子节点的大小关系,使得每个父节点的值都大于或等于其子节点的值。从最后一个非叶子节点开始,向前遍历每个非叶子节点,对其进行堆调整操作。
2.堆调整(heapify):
对于当前的非叶子节点,比较其与左右子节点的值,选择最大的节点作为父节点,并确保父节点大于或等于子节点。若发生交换,继续对被交换的子节点进行堆调整,以保持堆的性质。
3.交换元素:
将堆顶元素(最大值)与数组中最后一个元素交换位置,即将最大值放到数组的末尾。
4.缩小堆范围:
将数组长度减一,剔除已经排好序的最大值,此时堆的范围缩小。
5.重复对整棵树的根节点进行对调整,直到堆的范围缩小为1,排序完成。
这个就是优化,不用从下往上依次调整了。
OK理论结束,上代码:
c++堆排序
//arr:排序的数组
//n:数组长度
//i:要操作的节点
void heapify(vector<int>& arr, int n, int i)
{
int largest = i; // 初始化当前节点为最大值
int left = 2 * i + 1; // 左子节点
int right = 2 * i + 2; // 右子节点
// 如果左子节点大于根节点,则更新最大值
if (left < n && arr[left] > arr[largest])
largest = left;
// 如果右子节点大于当前最大值,则更新最大值
if (right < n && arr[right] > arr[largest])
largest = right;
// 如果最大值不是当前节点,则交换最大值和当前节点,并继续向下调整
if (largest != i)
{
swap(arr[i], arr[largest]);
heapify(arr, n, largest);
}
}
void heapSort(vector<int>& arr)
{
int n = arr.size();
// 构建最大堆,从最后一个非叶子节点(就是最后一个叶子结点的根节点)开始向上调整
for (int i = n-2 / 2 ; i >= 0; i--)
heapify(arr, n, i);
// 依次将最大值交换到数组末尾,并重新调整堆
for (int i = n - 1; i > 0; i--)
{
swap(arr[0], arr[i]); // 将最大值交换到末尾
heapify(arr, i, 0); // 重新调整堆,i也对应现在参与排序的数组长度,会不断减小
}
}