目录

 

​一、基础堆​

​二、原地最大堆​


一、基础堆

基础堆的搭建,将传入的数组数组元素放入一个新的数组,并操作成最大堆

shift Down/shift Up函数,逐个插入元素,或者直接插入数组、终端中以树形结构方式的打印函数的实现

template <typename Item>
class MaxHeap
{
private:
Item* data;
//堆中已经存放了多少堆元素
int count;
//可以存放多少个堆元素
int capacity;
//私有属性,对象调用不了,用户调用Insert函数就可以了
void shiftUp(int k)
{
while (k>1&&data[k] > data[k / 2])
{
swap(data[k], data[k / 2]);
k /= 2;
}
}
//如果有左右孩子,则找出左右孩子的最大值索引,跟父节点比较,如果父节点比他们大,则跳出,反之则交换
void shiftDown(int k)
{
while (2 * k <= count)
{
//j存放孩子的最大值索引,默认为左孩子
int j = 2 * k;
//要考虑j边界问题
if (j + 1 <= count && data[j + 1] > data[j])
{
j += 1;
}
if (data[k] >= data[j])
break;
swap(data[k], data[j]);
k = j;
}


}
//在控制台中打印树型辅助函数
void putNumberInLine(int num, string &line, int index_cur_level, int cur_tree_width, bool isLeft) {

int sub_tree_width = (cur_tree_width - 1) / 2;
int offset = index_cur_level * (cur_tree_width + 1) + sub_tree_width;
assert(offset + 1 < line.size());
if (num >= 10) {
line[offset + 0] = '0' + num / 10;
line[offset + 1] = '0' + num % 10;
}
else {
if (isLeft)
line[offset + 0] = '0' + num;
else
line[offset + 1] = '0' + num;
}
}
//在控制台中打印树型辅助函数
void putBranchInLine(string &line, int index_cur_level, int cur_tree_width) {

int sub_tree_width = (cur_tree_width - 1) / 2;
int sub_sub_tree_width = (sub_tree_width - 1) / 2;
int offset_left = index_cur_level * (cur_tree_width + 1) + sub_sub_tree_width;
assert(offset_left + 1 < line.size());
int offset_right = index_cur_level * (cur_tree_width + 1) + sub_tree_width + 1 + sub_sub_tree_width;
assert(offset_right < line.size());

line[offset_left + 1] = '/';
line[offset_right + 0] = '\\';
}
public:
MaxHeap(int capacity)
{
//因为堆的根节点索引从1开始
data = new Item[capacity+1];
count = 0;
this->capacity = capacity;
}
//heapify的过程,算法复杂度为O(n),将n个元素逐个插到一个空堆中,算法复杂度是O(nlogn)
MaxHeap(Item arr[],int n)
{
data = new Item[n + 1];
for (int i = 0; i < n; i++)
{
data[i + 1] = arr[i];
}
count = n;
capacity = n;
for (int i = count / 2; i >= 1; i--)
shiftDown(i);

}
~MaxHeap()
{
delete[] data;
}
int size()
{
return count;
}
bool isEmpty()
{
return count == 0;
}
//给堆插入元素
void Insert(Item item)
{
assert(count + 1 <= capacity);
data[count + 1] = item;
count++;
shiftUp(count);
}
//从堆中提取元素,数组中堆以外的元素不是为空,而是有值得数,是从根节点替换下来的
Item extractMax()
{
assert(count >=1);
Item pop = data[1];

swap(data[1], data[count]);
count--;
shiftDown(1);

return pop;

}
//在控制台中打印树型函数
void testPrint() {

if (size() >= 100) {
cout << "Fancy print can only work for less than 100 int";
return;
}

if (typeid(Item) != typeid(int)) {
cout << "Fancy print can only work for int item";
return;
}

cout << "The Heap size is: " << size() << endl;
cout << "data in heap: ";
for (int i = 1; i <= size(); i++)
cout << data[i] << " ";
cout << endl;
cout << endl;

int n = size();
int max_level = 0;
int number_per_level = 1;
while (n > 0) {
max_level += 1;
n -= number_per_level;
number_per_level *= 2;
}

int max_level_number = int(pow(2, max_level - 1));
int cur_tree_max_level_number = max_level_number;
int index = 1;
for (int level = 0; level < max_level; level++) {
string line1 = string(max_level_number * 3 - 1, ' ');

int cur_level_number = min(count - int(pow(2, level)) + 1, int(pow(2, level)));
bool isLeft = true;
for (int index_cur_level = 0; index_cur_level < cur_level_number; index++, index_cur_level++) {
putNumberInLine(data[index], line1, index_cur_level, cur_tree_max_level_number * 3 - 1, isLeft);
isLeft = !isLeft;
}
cout << line1 << endl;

if (level == max_level - 1)
break;

string line2 = string(max_level_number * 3 - 1, ' ');
for (int index_cur_level = 0; index_cur_level < cur_level_number; index_cur_level++)
putBranchInLine(line2, index_cur_level, cur_tree_max_level_number * 3 - 1);
cout << line2 << endl;

cur_tree_max_level_number /= 2;
}
}
};
template<typename T>
void heapsort(T arr[],int n)
{
MaxHeap<int> maxheap = MaxHeap<int>(arr, n);
for (int i = n - 1; i >= 0; i--)
{
arr[i] = maxheap.extractMax();
}
}

shiftDown传统的比较三个值大小的写法

void shiftDown(int k)
{
if (2 * k <= count)
{
Item max = data[k];
Item maxindex = k;
if (data[2 * k] > max)
{
max = data[2 * k];
maxindex = 2 * k;
}
//注意j的边界取值
if (data[2 * k + 1] > max && 2 * k + 1 <= count)
{
max = data[2 * k + 1];
maxindex = 2 * k + 1;
}

//如果父节点比子节点大则不需要交换
if (max != data[k])
{
swap(data[k], data[maxindex]);
shiftDown(maxindex);
}
}
}

二、原地最大堆

根的索引为0,有n个元素,最后一个非叶子节点索引为 (n-1-1)/2

最大堆排序_父节点

//T arr[] 数组,n为数组arr中元素的个数,k是需要执行shiftDown操作的索引
template<typename T>
void __shiftDown(T arr[], int n, int k) {

while (2 * k + 1 < n)
{
//堆的根索引为0时,左孩子为 2 * k + 1,默认左孩子为最大值
int j = 2 * k + 1;
if (j + 1 < n &&arr[j + 1] > arr[j])
j += 1;

if (arr[k] >= arr[j])break;

swap(arr[k], arr[j]);
k = j;
}
}

//T arr[] 传递的数组,n为数组的元素大小
template<typename T>
void heapSort(T arr[], int n) {
//根的索引为0,有n个元素,最后一个非叶子节点索引为 (n-1-1)/2
for (int i = (n-2) / 2; i >= 0; i--)
__shiftDown(arr, n, i);

for (int i = n - 1; i > 0; i--) {
swap(arr[0], arr[i]);
__shiftDown(arr, i, 0);
}
}

 C++堆排序代码

#include<iostream>
#include<vector>
using namespace std;//
//T arr[] 数组,n为数组arr中元素的个数,k是需要执行shiftDown操作的索引

void __shiftDown(vector<int>& arr, int n, int k) {

while (2 * k + 1 < n)
{
//堆的根索引为0时,左孩子为 2 * k + 1,默认左孩子为最大值
int j = 2 * k + 1;
if (j + 1 < n && arr[j + 1] > arr[j])
j += 1;
//本身父节点就是大于 子节点 所以什么都不做,直接跳出
if (arr[k] >= arr[j])break;

swap(arr[k], arr[j]);
//交换的是j,将j赋值给k,一直shift_down下去,维持最大堆
k = j;
}
}

//T arr[] 传递的数组,n为数组的元素大小
template<typename T>
void heapSort(vector<int>& arr, int n) {
//根的索引为0,有n个元素,最后一个非叶子节点索引为 (n-1-1)/2
for (int i = (n - 2) / 2; i >= 0; i--)
__shiftDown(arr, n, i);

for (int i = n - 1; i > 0; i--) {
cout << arr[0] << " ";
swap(arr[0], arr[i]);
__shiftDown(arr, i, 0);
}
}

int main(void) {
vector<int> arr = { 4,3,6,45,3,2,7,8,5,4 };
heapSort<int>(arr, 10);
return 0;

}