堆:是一种数组对象,它可以被看成是一种二叉树结构。
我们把堆的二叉树存储方式分为两种:即大堆和小堆。那么问题来了,什么大堆?什么是小堆?
大堆:让每个父节点的值都大于孩子节点的值。
小堆:让每个父节点的值都小于孩子节点的值。
说完概念后,就来考虑一下如何来实现大堆和小堆吧。第一步肯定得有一个堆吧,所以我们首先得建堆,这是毋庸置疑的。由于堆实际上就是一个二叉树结构,所以先将数压进栈中,然后通过向下调整来建大堆。
第一例:堆的实现
具体实现代码如下:
#pragma once
#include<vector>
#include<iostream>
#include<assert.h>
using namespace std;
//仿函数用于下面的父节点与子节点的比较
template<class T>
struct Less
{
bool operator()(const T& left,const T& right)
{
return (left < right);
}
};
template<class T>
struct Greater
{
bool operator()(const T& left,const T& right)
{
return (left > right);
}
};
template<class T,class Compare = Greater<T>>
class Heap
{
public:
<span style="white-space:pre"> </span>//建堆
Heap(const T* a,size_t size)
{
assert(a);
for(size_t i = 0; i < size; i++)
{
_a.push_back(a[i]);
}
for(size_t i = (_a.size()-2)/2; i > 0; i--)
{
AdjustDown(i);
}
}
void push(const T& x)
{
_a.push_back(x);
AdjustUp(_a.size()-1);
}
void pop()
{
assert(!_a.empty());
swap(_a[0],_a[_a.size()-1]);
_a.pop_back();
AdjustDown(0);
}
bool Empty()
{
return _a.empty();
}
size_t Size()
{
return _a.size();
}
void print()
{
for(size_t i = 0; i < _a.size(); i++)
{
cout<<_a[i]<<" ";
}
cout<<endl;
}
protected:
<span style="white-space:pre"> </span>//向下调整
void AdjustDown(size_t parent)
{
size_t child = parent*2+1;
Compare com;
while(child < _a.size())
{
if(child+1 < _a.size() && com(_a[child+1],_a[child]))
{
child++;
}
if(com(_a[child],_a[parent])) //孩子节点大于父节点
{
swap(_a[child],_a[parent]);
parent = child;
child = parent*2+1;
}
else
{
break;
}
}
}
<span style="white-space:pre"> </span>//向上调整
void AdjustUp(size_t child)
{
size_t parent = (child-1)/2;
Compare com;
while(child > 0)
{
if(com(_a[child],_a[parent])) //父节点小于孩子节点
{
swap(_a[child],_a[parent]);
child = parent;
parent = (child-1)/2;
}
else
{
break;
}
}
}
protected:
vector<T> _a;
};
void testHeap()
{
int a[] = {10,11, 13, 12, 16, 18, 15, 17, 14, 19};
Heap<int,Greater<int>> hp(a,sizeof(a)/sizeof(a[0]));
hp.push(2);
hp.print();
}
第二例:
同样,我们也可以实现堆排序:
如图实现方式:
(1)
(2)
(3)
...
依此向下调整的方式进行排序。
void AdjustDown(int a[],int n, int parent)
{
int child = parent*2+1;
while(child < n)
{
if(child+1 < n && a[child+1] > a[child])
{
child++;
}
if(a[child] > a[parent])
{
swap(a[child],a[parent]);
parent = child;
child = parent*2+1;
}
else
{
break;
}
}
}
void HeapSort(int a[],size_t n)
{
assert(a);
for(int i = (n-2)/2; i >= 0; --i)
{
AdjustDown(a,n,i);
}
for(int i = 0; i < n; ++i)
{
swap(a[0],a[n-1-i]); //交换堆顶元素和最后一个元素
AdjustDown(a,n-1-i,0); //将最后一个元素固定,向下调整前n-i-1个
}
}
void print(int a[],size_t size)
{
for(size_t i = 0; i < size;i++)
{
cout<<a[i]<<" ";
}
cout<<endl;
}
void TestHeapSort()
{
int a[] = {2,1,4,5,0,6,3,7,8,9};
HeapSort(a, sizeof(a)/sizeof(a[0]));
print(a,sizeof(a)/sizeof(a[0]));
}
堆的实现及堆排序就说到这里啦。。堆排序将会在后期排序那部分再与大家见面哦。到时候会与其他排序一起比较,包括它的时间复杂度和空间复杂度,以及各个排序算法的优缺点。今天的就说到这里啦~~