1.父结点的键值总是大于或等于(小于或等于)任何一个子节点的键值。
2.每个结点的左子树和右子树都是一个二叉堆(都是最大堆或最小堆)。
当父结点的键值总是大于或等于任何一个子节点的键值时为最大堆。
当父结点的键值总是小于或等于任何一个子节点的键值时为最小堆。
这个堆是不是很有趣呢~ 现在开始我们开始了解它,首先我们应该知道堆是如何创建的,这里我有首先了解一个概念:向下调整
举个例子这里我建的是一个大堆,我需要一个函数来交换子节点大于父节点的节点们,现在我附上代码:
(注意今天的方法都是用数组模仿堆的实现的)
template<class T>
struct Less
{
bool operator()(const T& l, const T& r)const
{
return l < r;
}
};
template<class T>
struct Greator
{
bool operator()(const T& l, const T& r)const
{
return l > r;
}
};
void _AdjustDown(int parent)
{
Compare ptr;
int child = parent * 2 + 1;
while (child < _a.size())
{
if (child + 1 < _a.size() && ptr(_a[child + 1], _a[child]))
{
++child;
}
if (ptr(_a[child] , _a[parent]))
{
swap(_a[parent], _a[child]);
parent = child;
child = parent * 2 + 1;
}
else
{
break;
}
}
}
这里我用到仿函数,对代码的复用性提升,小堆的向下调整和大堆神似(99%相似),所以我用到仿函数对他们进行复用。我们看看调用过程:
现在我们来看向下调整的代码,它的核心思想就是如果如果孩子节点里面较大的那个结点的值大于父节点那么就让他们交换,然后
顺着一条路径往下走,显然调用一次不足以创建一个堆,所以建堆是一个过程.
Heap(T* a, size_t n) { _a.reserve(n); for (size_t i = 0; i < n; ++i) { _a.push_back(a[i]); } for (int i = (_a.size() - 2) / 2; i>=0; --i) { _AdjustDown(i); } }
//向上调整 void _AdjustUp(int child) { Compare fun; int parent = (child - 1) >> 1; while (child > 0) { if (fun(_a[child], _a[parent])) { swap(_a[child], _a[parent]); child = parent; parent = (child - 1) >> 1; } else { break; } } } //插入数据 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); }
void _AdjustDown(int* heap, int k, size_t parent) { size_t child = parent * 2 + 1; while (child < k) { if (child + 1 < k && (heap[child + 1] < heap[child])) { ++child; } if (heap[parent] > heap[child]) { swap(heap[parent], heap[child]); parent = child; child = parent * 2 + 1; } else { break; } } } void Topk(int* a, int k, int n) { assert(k < n); int* heap = new int[k]; for (size_t i = 0; i < k; ++i) { heap[i] = a[i]; } //topk建小堆 for (int i = (k - 2) / 2; i>0; --i) { _AdjustDown(heap,k,i); } //然后从头遍历n或者传进来的数组。 for (int i = 0; i < n; i++) { if (a[i]>heap[0]) { heap[0] = i; _AdjustDown(heap, k, 0); } } cout <<n<<"以内最大的" << k << "个数字分别为:" << " "; for (int i = 0; i < k; i++) { cout << heap[i] << " "; } cout << endl; delete[] heap; }
#include<iostream> #include<Windows.h> #include<vector> #include<assert.h> #include<memory> using namespace std; //**********************************************************************************************// //*******************************创建一个堆(大小堆)*********************************************// template<class T> struct Less { bool operator()(const T& l, const T& r)const { return l < r; } }; template<class T> struct Greator { bool operator()(const T& l, const T& r)const { return l > r; } }; template<class T,class Compare> class Heap { public: Heap() {} Heap(T* a, size_t n) { _a.reserve(n); for (size_t i = 0; i < n; ++i) { _a.push_back(a[i]); } for (int 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); } const T& Top() const { return _a[0]; } protected: void _AdjustDown(int parent) { Compare ptr; int child = parent * 2 + 1; while (child < _a.size()) { if (child + 1 < _a.size() && ptr(_a[child + 1], _a[child])) { ++child; } if (ptr(_a[child] , _a[parent])) { swap(_a[parent], _a[child]); parent = child; child = parent * 2 + 1; } else { break; } } } //向上调整 void _AdjustUp(int child) { Compare fun; int parent = (child - 1) >> 1; while (child > 0) { if (fun(_a[child], _a[parent])) { swap(_a[child], _a[parent]); child = parent; parent = (child - 1) >> 1; } else { break; } } } protected: vector<T> _a; }; //**********************************************************************************************// //**********************************************************************************************// //**********************************************************************************************// //******************************* top K 问题 *********************************************// //top K 问题 //找出n中最大的k个数 //传进来的数组 //这里的参数,如果传的是一个数组的话就是int* a 如果是数字的话直接就是int n,和int k 就够了。 void _AdjustDown(int* heap, int k, size_t parent) { size_t child = parent * 2 + 1; while (child < k) { if (child + 1 < k && (heap[child + 1] < heap[child])) { ++child; } if (heap[parent] > heap[child]) { swap(heap[parent], heap[child]); parent = child; child = parent * 2 + 1; } else { break; } } } void Topk(int* a, int k, int n) { assert(k < n); int* heap = new int[k]; for (size_t i = 0; i < k; ++i) { heap[i] = a[i]; } //topk建小堆 for (int i = (k - 2) / 2; i>0; --i) { _AdjustDown(heap,k,i); } //然后从头遍历n或者传进来的数组。 for (int i = 0; i < n; i++) { if (a[i]>heap[0]) { heap[0] = i; _AdjustDown(heap, k, 0); } } cout <<n<<"以内最大的" << k << "个数字分别为:" << " "; for (int i = 0; i < k; i++) { cout << heap[i] << " "; } cout << endl; delete[] heap; } //**********************************************************************************************// //**********************************************************************************************//