C++模板类实现数据结构堆
2017-04-06 11:07
281 查看
堆 是一颗被完全填满的二叉树 堆分大堆小堆 性质:http://baike.baidu.com/link?url=7fnAEA2vNRAskkWEWae4gmg3mfzAWAtxueXn2-gKX05N-jvbOqiuu7urw1K70bQAh876IL6XpT4jGBrpPMUX-xWHwWG53hzFLij6XxQaQuS#4
下面的代码展示了通过一个数组如何构建一个堆,以及堆的pop与push操作 (此处为大堆,小堆类似)
调整堆:首先我们要明确调整堆的目的就是让整棵树中的双亲节点都大于孩子节点(这里以最大堆为例),所以我们要从叶子结点开始调整,直到调整到根节点结束,可能调整好这棵树后,子树又不符合最大堆规则,转而调整子树,所以我们把这种方式叫下调(AdjustDown)
我们通过向下调整算法来构建一个堆 从 最后一个非叶子节点开始调整(最后一个结点的父亲)依次向上,调整结束后完成一个堆。
Push 和 Pop操作通过两种方式来是实现
1.push与pop操作利用了 hole为空洞 进行调整 的想法
2.利用向下和向上调整算法 插入时只调整插入位置到根节点位置 删除时向下调整使之重新成为一个堆
.h:
.cpp:
[cpp] view
plain copy
#include <iostream>
#include <windows.h>
using namespace std; //注意顺序 , 因为 ybb.h 使用了 swap 这个名字 所以如果 #include "ybb.h" 在 using namespace std 上面 则程序就出错(因为使用swap时还未包含 std这个 命名空间, 所以自己写的头文件一定要在 using namespace std;语句下面)
#include "ybb.h"
void test( );
int main( )
{
test( );
system( "pause" );
return 0;
}
void test( )
{
int a [] = {10,11, 13, 12, 16, 18, 15, 17, 14, 19};
Heap<int> t1( a, (sizeof(a) / sizeof(a[0])) );
t1.Print( );
cout << endl;
t1.Push( 38 );
t1.Print( );
cout << endl;
t1.Push( 100 );
t1.Print( );
cout << endl;
t1.Pop( );
t1.Print( );
cout << endl;
t1.Pop( );
t1.Print( );
cout << endl;
}
下面的代码展示了通过一个数组如何构建一个堆,以及堆的pop与push操作 (此处为大堆,小堆类似)
调整堆:首先我们要明确调整堆的目的就是让整棵树中的双亲节点都大于孩子节点(这里以最大堆为例),所以我们要从叶子结点开始调整,直到调整到根节点结束,可能调整好这棵树后,子树又不符合最大堆规则,转而调整子树,所以我们把这种方式叫下调(AdjustDown)
我们通过向下调整算法来构建一个堆 从 最后一个非叶子节点开始调整(最后一个结点的父亲)依次向上,调整结束后完成一个堆。
Push 和 Pop操作通过两种方式来是实现
1.push与pop操作利用了 hole为空洞 进行调整 的想法
2.利用向下和向上调整算法 插入时只调整插入位置到根节点位置 删除时向下调整使之重新成为一个堆
.h:
//数组坐标 //左孩子坐标 * 2 + 1 //右孩子坐标 * 2 + 2 //找父亲 -1 / 2 //完全二叉树:底层的元素从左到右填充 //resize 开空间还赋值(先初始化为默认值) //reserve 只开空间 //最后一个结点父亲 是 最后一个非叶子节点 从它开始调整 //注意 如果 某个值在小于0 时退出循环 不要用 size_t 否则会死循环 //此处构建的是大堆 #include <vector> template<class T> class Heap { public: Heap( ) {} Heap( T* a, size_t n ) { _a.reserve( n ); //resize 开空间还赋值 reserve 只开空间 for ( size_t i = 0; i < n; ++i ) { _a.push_back( a[i] ); } AdjustDown( ); } void AdjustDown( ) //成员函数嘛,这里参数也不好写, 不如在里面调用子函数(给它传参),在外面通过对象调用这个函数就好,让它调用子函数 (but 外界对象不能直接调用保护的子Fun) { for ( int i = (_a.size( ) - 2) / 2; i >= 0; --i )//必须用int 否则死循环. 条件 >= 0; { _AdjustDown( i ); } } //这两个函数忽略,用书上的方法 void AdjustUp( ) { _AdjustUp( _a.size( ) - 1 ); } //void Push( const T& x ) //1 //{ // _a.push_back( x ); // T tmp = x; // int hole = _a.size( ) - 1; // for ( ; (hole > 0) && (tmp > _a[(hole - 1) / 2]); (hole = (hole - 1) / 2) )//条件不能是>=0. 如果等于0则出错! // { // _a[hole] = _a[(hole - 1) / 2]; // } // _a[hole] = tmp; //} void Push( const T& x ) //插入元素x后, 只影响插入元素到根节点处再到第一个元素的路径上的元素, 所以利用向上调整算法重新建堆 { _a.push_back( x ); AdjustUp( ); } const T& Top( ) //返回的值 ,不可改变 { return _a[0]; } //void Pop( ) //{ // swap( _a[0], _a[_a.size( ) - 1] ); // _a.pop_back( ); // int child; // int hole; // T tmp = _a[0]; // for ( hole = 0; (hole * 2 + 1<= _a.size( ) - 1); hole = child )//默认为左孩子 // { // child = hole * 2 + 1; // if ( (_a.size( ) != child + 1) && (_a[child + 1] > _a[child]) ) //右孩子存在 // { // ++child; // } //此时 child 为左右(如果右孩子存在)孩子中最大值下标 // if ( _a[child] > tmp ) // { // _a[hole] = _a[child]; // } // else // { // break; // } // } // _a[hole] = tmp; //} void Pop( ) { swap( _a[0], _a[_a.size( ) - 1] ); _a.pop_back( ); _AdjustDown( 0 ); //通过向下调整算法重新构建堆 } void Print( ) { for ( size_t i = 0; i < _a.size( ); ++i ) { cout << _a[i] << " "; } } protected: vector<T> _a; void _AdjustDown( size_t parent ) //不合适 { size_t child; child = parent * 2 + 1; //此处为左孩子 while ( child < _a.size( ) ) //循环内就继续调整,因为一次调整后,以前已经调整好的堆可能被破坏了 { if ( (child + 1 < _a.size( )) && (_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 _AdjustUp( size_t child ) { size_t parent = (child - 1) / 2; while ( child > 0 ) { if ( _a[child] > _a[parent] ) { swap( _a[child], _a[parent] ); child = parent; //此处 最后一个根节点 0 赋值给child 结束 所以child > 0时循环继续 parent = (child - 1) / 2; } else { break; } } } };
.cpp:
[cpp] view
plain copy
#include <iostream>
#include <windows.h>
using namespace std; //注意顺序 , 因为 ybb.h 使用了 swap 这个名字 所以如果 #include "ybb.h" 在 using namespace std 上面 则程序就出错(因为使用swap时还未包含 std这个 命名空间, 所以自己写的头文件一定要在 using namespace std;语句下面)
#include "ybb.h"
void test( );
int main( )
{
test( );
system( "pause" );
return 0;
}
void test( )
{
int a [] = {10,11, 13, 12, 16, 18, 15, 17, 14, 19};
Heap<int> t1( a, (sizeof(a) / sizeof(a[0])) );
t1.Print( );
cout << endl;
t1.Push( 38 );
t1.Print( );
cout << endl;
t1.Push( 100 );
t1.Print( );
cout << endl;
t1.Pop( );
t1.Print( );
cout << endl;
t1.Pop( );
t1.Print( );
cout << endl;
}
相关文章推荐
- (转)C++ 实现内存自动托管的模板类
- 在VC9.0中实现C++模板类头文件和实现文件分离的方法
- C++之STL(二):实现简单容器模板类Vec(vector capacity 增长问题、allocator 内存分配器)
- 实现C++模板类头文件和实现文件分离的方法
- C++模板类实现文件的操作
- 链表的顺序表示和实现(C++模板类实现)
- 用c++实现 c++单链表的实现(采用模板类)
- C/C++--模板类实现.h和.cpp分离
- C++模板类头文件和实现文件分离的方法(vs2012测试)
- C++单例模板类的实现和使用
- 在VC9.0中实现C++模板类头文件和实现文件分离的方法
- 【算法学习】B-Tree编程实现(C++模板类封装)
- C++顺序栈模板类实现之模板类友元函数实现
- 单链表的C++实现(采用模板类)
- [转贴]从零开始学C++之STL(二):实现一个简单容器模板类Vec(模仿VC6.0 中 vector 的实现、vector 的容量capacity 增长问题)
- C++中用模板类(结点类,链表类)实现的单链表的合并操作!
- C++中模板类的输入输出流重载函数实现
- 在VC9.0中实现C++模板类头文件和实现文件分离的方法
- C++ 实现自己queue模板类
- C++数据结构 顺序表的实现(模板类的实现)