斜堆(二)之 C++的实现
概要
上一章介绍了斜堆的基本概念,并通过C语言实现了斜堆。本章是斜堆的C++实现。
目录
1. 斜堆的介绍
2. 斜堆的基本操作
3. 斜堆的C++实现(完整源码)
4. 斜堆的C++测试程序
转载请注明出处:http://www.cnblogs.com/skywang12345/p/3638524.html
更多内容:数据结构与算法系列 目录
斜堆的介绍
斜堆(Skew heap)也叫自适应堆(self-adjusting heap),它是左倾堆的一个变种。和左倾堆一样,它通常也用于实现优先队列;作为一种自适应的左倾堆,它的合并操作的时间复杂度也是O(lg n)。
它与左倾堆的差别是:
(01) 斜堆的节点没有"零距离"这个属性,而左倾堆则有。
(02) 斜堆的合并操作和左倾堆的合并操作算法不同。
斜堆的合并操作
(01) 如果一个空斜堆与一个非空斜堆合并,返回非空斜堆。
(02) 如果两个斜堆都非空,那么比较两个根节点,取较小堆的根节点为新的根节点。将"较小堆的根节点的右孩子"和"较大堆"进行合并。
(03) 合并后,交换新堆根节点的左孩子和右孩子。
第(03)步是斜堆和左倾堆的合并操作差别的关键所在,如果是左倾堆,则合并后要比较左右孩子的零距离大小,若右孩子的零距离 > 左孩子的零距离,则交换左右孩子;最后,在设置根的零距离。
斜堆的基本操作
1. 基本定义
template <class T>
class SkewNode{
public:
T key; // 关键字(键值)
SkewNode *left; // 左孩子
SkewNode *right; // 右孩子 SkewNode(T value, SkewNode *l, SkewNode *r):
key(value), left(l),right(r) {}
};
SkewNode是斜堆对应的节点类。
template <class T>
class SkewHeap {
private:
SkewNode<T> *mRoot; // 根结点 public:
SkewHeap();
~SkewHeap(); // 前序遍历"斜堆"
void preOrder();
// 中序遍历"斜堆"
void inOrder();
// 后序遍历"斜堆"
void postOrder(); // 将other的斜堆合并到this中。
void merge(SkewHeap<T>* other);
// 将结点(key为节点键值)插入到斜堆中
void insert(T key);
// 删除结点(key为节点键值)
void remove(); // 销毁斜堆
void destroy(); // 打印斜堆
void print();
private: // 前序遍历"斜堆"
void preOrder(SkewNode<T>* heap) const;
// 中序遍历"斜堆"
void inOrder(SkewNode<T>* heap) const;
// 后序遍历"斜堆"
void postOrder(SkewNode<T>* heap) const; // 交换节点x和节点y
void swapNode(SkewNode<T> *&x, SkewNode<T> *&y);
// 合并"斜堆x"和"斜堆y"
SkewNode<T>* merge(SkewNode<T>* &x, SkewNode<T>* &y); // 销毁斜堆
void destroy(SkewNode<T>* &heap); // 打印斜堆
void print(SkewNode<T>* heap, T key, int direction);
};
SkewHeap是斜堆类,它包含了斜堆的根节点,以及斜堆的操作。
2. 合并
/*
* 合并"斜堆x"和"斜堆y"
*/
template <class T>
SkewNode<T>* SkewHeap<T>::merge(SkewNode<T>* &x, SkewNode<T>* &y)
{
if(x == NULL)
return y;
if(y == NULL)
return x; // 合并x和y时,将x作为合并后的树的根;
// 这里的操作是保证: x的key < y的key
if(x->key > y->key)
swapNode(x, y); // 将x的右孩子和y合并,
// 合并后直接交换x的左右孩子,而不需要像左倾堆一样考虑它们的npl。
SkewNode<T> *tmp = merge(x->right, y);
x->right = x->left;
x->left = tmp; return x;
} /*
* 将other的斜堆合并到this中。
*/
template <class T>
void SkewHeap<T>::merge(SkewHeap<T>* other)
{
mRoot = merge(mRoot, other->mRoot);
}
merge(x, y)是内部接口,作用是合并x和y这两个斜堆,并返回得到的新堆的根节点。
merge(other)是外部接口,作用是将other合并到当前堆中。
3. 添加
/*
* 新建键值为key的结点并将其插入到斜堆中
*
* 参数说明:
* heap 斜堆的根结点
* key 插入的结点的键值
* 返回值:
* 根节点
*/
template <class T>
void SkewHeap<T>::insert(T key)
{
SkewNode<T> *node; // 新建结点 // 新建节点
node = new SkewNode<T>(key, NULL, NULL);
if (node==NULL)
{
cout << "ERROR: create node failed!" << endl;
return ;
} mRoot = merge(mRoot, node);
}
insert(key)的作用是新建键值为key的节点,并将其加入到当前斜堆中。
4. 删除
/*
* 删除结点
*/
template <class T>
void SkewHeap<T>::remove()
{
if (mRoot == NULL)
return NULL; SkewNode<T> *l = mRoot->left;
SkewNode<T> *r = mRoot->right; // 删除根节点
delete mRoot;
// 左右子树合并后的新树
mRoot = merge(l, r);
}
remove()的作用是删除斜堆的最小节点。
注意:关于斜堆的"前序遍历"、"中序遍历"、"后序遍历"、"打印"、"销毁"等接口就不再单独介绍了。后文的源码中有给出它们的实现代码,Please RTFSC(Read The Fucking Source Code)!
斜堆的C++实现(完整源码)
斜堆的实现文件(SkewHeap.h)
/**
* C++: 斜堆
*
* @author skywang
* @date 2014/03/31
*/ #ifndef _SKEW_HEAP_HPP_
#define _SKEW_HEAP_HPP_ #include <iomanip>
#include <iostream>
using namespace std; template <class T>
class SkewNode{
public:
T key; // 关键字(键值)
SkewNode *left; // 左孩子
SkewNode *right; // 右孩子 SkewNode(T value, SkewNode *l, SkewNode *r):
key(value), left(l),right(r) {}
}; template <class T>
class SkewHeap {
private:
SkewNode<T> *mRoot; // 根结点 public:
SkewHeap();
~SkewHeap(); // 前序遍历"斜堆"
void preOrder();
// 中序遍历"斜堆"
void inOrder();
// 后序遍历"斜堆"
void postOrder(); // 将other的斜堆合并到this中。
void merge(SkewHeap<T>* other);
// 将结点(key为节点键值)插入到斜堆中
void insert(T key);
// 删除结点(key为节点键值)
void remove(); // 销毁斜堆
void destroy(); // 打印斜堆
void print();
private: // 前序遍历"斜堆"
void preOrder(SkewNode<T>* heap) const;
// 中序遍历"斜堆"
void inOrder(SkewNode<T>* heap) const;
// 后序遍历"斜堆"
void postOrder(SkewNode<T>* heap) const; // 交换节点x和节点y
void swapNode(SkewNode<T> *&x, SkewNode<T> *&y);
// 合并"斜堆x"和"斜堆y"
SkewNode<T>* merge(SkewNode<T>* &x, SkewNode<T>* &y); // 销毁斜堆
void destroy(SkewNode<T>* &heap); // 打印斜堆
void print(SkewNode<T>* heap, T key, int direction);
}; /*
* 构造函数
*/
template <class T>
SkewHeap<T>::SkewHeap():mRoot(NULL)
{
} /*
* 析构函数
*/
template <class T>
SkewHeap<T>::~SkewHeap()
{
destroy(mRoot);
} /*
* 前序遍历"斜堆"
*/
template <class T>
void SkewHeap<T>::preOrder(SkewNode<T>* heap) const
{
if(heap != NULL)
{
cout<< heap->key << " " ;
preOrder(heap->left);
preOrder(heap->right);
}
} template <class T>
void SkewHeap<T>::preOrder()
{
preOrder(mRoot);
} /*
* 中序遍历"斜堆"
*/
template <class T>
void SkewHeap<T>::inOrder(SkewNode<T>* heap) const
{
if(heap != NULL)
{
inOrder(heap->left);
cout<< heap->key << " " ;
inOrder(heap->right);
}
} template <class T>
void SkewHeap<T>::inOrder()
{
inOrder(mRoot);
} /*
* 后序遍历"斜堆"
*/
template <class T>
void SkewHeap<T>::postOrder(SkewNode<T>* heap) const
{
if(heap != NULL)
{
postOrder(heap->left);
postOrder(heap->right);
cout<< heap->key << " " ;
}
} template <class T>
void SkewHeap<T>::postOrder()
{
postOrder(mRoot);
} /*
* 交换两个节点的内容
*/
template <class T>
void SkewHeap<T>::swapNode(SkewNode<T> *&x, SkewNode<T> *&y)
{
SkewNode<T> *tmp = x;
x = y;
y = tmp;
} /*
* 合并"斜堆x"和"斜堆y"
*/
template <class T>
SkewNode<T>* SkewHeap<T>::merge(SkewNode<T>* &x, SkewNode<T>* &y)
{
if(x == NULL)
return y;
if(y == NULL)
return x; // 合并x和y时,将x作为合并后的树的根;
// 这里的操作是保证: x的key < y的key
if(x->key > y->key)
swapNode(x, y); // 将x的右孩子和y合并,
// 合并后直接交换x的左右孩子,而不需要像左倾堆一样考虑它们的npl。
SkewNode<T> *tmp = merge(x->right, y);
x->right = x->left;
x->left = tmp; return x;
} /*
* 将other的斜堆合并到this中。
*/
template <class T>
void SkewHeap<T>::merge(SkewHeap<T>* other)
{
mRoot = merge(mRoot, other->mRoot);
} /*
* 新建键值为key的结点并将其插入到斜堆中
*
* 参数说明:
* heap 斜堆的根结点
* key 插入的结点的键值
* 返回值:
* 根节点
*/
template <class T>
void SkewHeap<T>::insert(T key)
{
SkewNode<T> *node; // 新建结点 // 新建节点
node = new SkewNode<T>(key, NULL, NULL);
if (node==NULL)
{
cout << "ERROR: create node failed!" << endl;
return ;
} mRoot = merge(mRoot, node);
} /*
* 删除结点
*/
template <class T>
void SkewHeap<T>::remove()
{
if (mRoot == NULL)
return NULL; SkewNode<T> *l = mRoot->left;
SkewNode<T> *r = mRoot->right; // 删除根节点
delete mRoot;
// 左右子树合并后的新树
mRoot = merge(l, r);
} /*
* 销毁斜堆
*/
template <class T>
void SkewHeap<T>::destroy(SkewNode<T>* &heap)
{
if (heap==NULL)
return ; if (heap->left != NULL)
destroy(heap->left);
if (heap->right != NULL)
destroy(heap->right); delete heap;
} template <class T>
void SkewHeap<T>::destroy()
{
destroy(mRoot);
} /*
* 打印"斜堆"
*
* key -- 节点的键值
* direction -- 0,表示该节点是根节点;
* -1,表示该节点是它的父结点的左孩子;
* 1,表示该节点是它的父结点的右孩子。
*/
template <class T>
void SkewHeap<T>::print(SkewNode<T>* heap, T key, int direction)
{
if(heap != NULL)
{
if(direction==) // heap是根节点
cout << setw() << heap->key << " is root" << endl;
else // heap是分支节点
cout << setw() << heap->key << " is " << setw() << key << "'s " << setw() << (direction==?"right child" : "left child") << endl; print(heap->left, heap->key, -);
print(heap->right,heap->key, );
}
} template <class T>
void SkewHeap<T>::print()
{
if (mRoot != NULL)
print(mRoot, mRoot->key, );
}
#endif
斜堆的测试程序(SkewHeapTest.cpp)
/**
* C 语言: 斜堆
*
* @author skywang
* @date 2014/03/31
*/ #include <iostream>
#include "SkewHeap.h"
using namespace std; int main()
{
int i;
int a[]= {,,,,,,,};
int b[]= {,,,,,,};
int alen=sizeof(a)/sizeof(a[]);
int blen=sizeof(b)/sizeof(b[]);
SkewHeap<int>* ha=new SkewHeap<int>();
SkewHeap<int>* hb=new SkewHeap<int>(); cout << "== 斜堆(ha)中依次添加: ";
for(i=; i<alen; i++)
{
cout << a[i] <<" ";
ha->insert(a[i]);
}
cout << "\n== 斜堆(ha)的详细信息: " << endl;
ha->print(); cout << "\n== 斜堆(hb)中依次添加: ";
for(i=; i<blen; i++)
{
cout << b[i] <<" ";
hb->insert(b[i]);
}
cout << "\n== 斜堆(hb)的详细信息: " << endl;
hb->print(); // 将"斜堆hb"合并到"斜堆ha"中。
ha->merge(hb);
cout << "\n== 合并ha和hb后的详细信息: " << endl;
ha->print(); // 销毁
ha->destroy(); return ;
}
斜堆的C++测试程序
斜堆的测试程序已经包含在它的实现文件(SkewHeapTest.cpp)中了,这里仅给出它的运行结果:
== 斜堆(ha)中依次添加:
== 斜堆(ha)的详细信息:
is root
is 's left child
is 's left child
is 's left child
is 's left child
is 's right child
is 's left child
is 's left child == 斜堆(hb)中依次添加:
== 斜堆(hb)的详细信息:
is root
is 's left child
is 's left child
is 's left child
is 's right child
is 's right child
is 's left child == 合并ha和hb后的详细信息:
is root
is 's left child
is 's left child
is 's left child
is 's left child
is 's right child
is 's left child
is 's right child
is 's left child
is 's left child
is 's right child
is 's right child
is 's left child
is 's left child
is 's left child
斜堆(二)之 C++的实现的更多相关文章
- 结构之美——优先队列基本结构(四)——二叉堆、d堆、左式堆、斜堆
实现优先队列结构主要是通过堆完成,主要有:二叉堆.d堆.左式堆.斜堆.二项堆.斐波那契堆.pairing 堆等. 1. 二叉堆 1.1. 定义 完全二叉树,根最小. 存储时使用层序. 1.2. 操作 ...
- bzoj1078【SCOI2008】斜堆
题意: 斜堆(skew heap)是一种常用的数据结构.它也是二叉树,且满足与二叉堆相同的堆性质:每个非根结点的值都比它父亲大.因此在整棵斜堆中,根的值最小.但斜堆不必是平衡的,每个结点的左右儿子的大 ...
- BZOJ 1078: [SCOI2008]斜堆
1078: [SCOI2008]斜堆 Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 770 Solved: 422[Submit][Status][ ...
- 【bzoj1078】[SCOI2008]斜堆
2016-05-31 16:34:09 题目:http://www.lydsy.com/JudgeOnline/problem.php?id=1078 挖掘斜堆的性质233 http://www.cp ...
- 二项堆(二)之 C++的实现
概要 上一章介绍了二项堆的基本概念,并通过C语言实现了二项堆.本章是二项堆的C++实现. 目录1. 二项树的介绍2. 二项堆的介绍3. 二项堆的基本操作4. 二项堆的C++实现(完整源码)5. 二项堆 ...
- 斜堆,非旋转treap,替罪羊树
一.斜堆 斜堆是一种可以合并的堆 节点信息: struct Node { int v; Node *ch[]; }; 主要利用merge函数 Node *merge(Node *x, Node *y) ...
- [SCOI2008]斜堆
题目大意 1.题目描述 斜堆(skew heap)是一种常用的数据结构. 它也是二叉树,且满足与二叉堆相同的堆性质: 每个非根结点的值都比它父亲大.因此在整棵斜堆中,根的值最小. . 但斜堆不必是平衡 ...
- BZOJ1078 [SCOI2008]斜堆 堆
欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - BZOJ1078 题意概括 斜堆(skew heap)是一种常用的数据结构.它也是二叉树,且满足与二叉堆相同的 ...
- P2475 [SCOI2008]斜堆
题目背景 四川2008NOI省选 题目描述 斜堆(skew heap)是一种常用的数据结构.它也是二叉树,且满足与二叉堆相 同的堆性质:每个非根结点的值都比它父亲大.因此在整棵斜堆中,根的值最小. 但 ...
随机推荐
- C# 串口操作 ---- 系列文章
C# 串口操作系列(5)--通讯库雏形 通讯库雏形的建立. 串口通讯介绍的高级篇,介绍更高级的抽象,为扩展为通用的客户端通讯库做铺垫,扩展性的考虑,能支持任意类型的流设备. ... 2010-08-0 ...
- 转载:Cellebrite发布新版手机取证软件,增强调查能力
2012-5-24 7:57:51 文章来源:文传商讯 作者:文传商讯 UFED 1.1.9.7版本为移动取证数据提取.编码和分析提供了先进的技术突破 新闻事实: Cellebrite发布其旗舰产 ...
- libev代码
就是贴上来: ev.c: /* * libev event processing core, watcher management */ /* this big block deduces confi ...
- android 透明度颜色值
100% — FF99% — FC98% — FA97% — F796% — F595% — F294% — F093% — ED92% — EB91% — E890% — E689% — E388% ...
- 如何在使用MAMP环境下安装MySQLdb
我的电脑上没有安装XAMPP,而是安装了MAMP PRO,其实两者都差不多,都是PHP+MySQL+Apache的集成环境,只是MAMP的GUI界面更华丽一些,但是也更复杂一些. 好了不说这些,说说问 ...
- 转:windows下多线程通信方法
多线程知识简介 同一进程中可以包含多个线程,由于进程中的多个线程可以共享进程中的资源,所以使同一进程中的多个线程之间通信相对比较简单. 当需要有多个线程来访问一个全局变量时,通常我们会在这个全局变量前 ...
- CentOS 7.2 安装配置 Percona Server
个人比较喜欢 MYSQL 的轻量,今天花了一点时间把阿里云上的 MYSQL5.7 换成了 Percona-Server ,Percona 是一个开源的 MySQL 衍生版.InnoDB的数据库引擎使得 ...
- MAC 磁盘清理工具 ncdu
下载命令:brew install ncdu 使用命令:ncdu . 它会将当前目录下的所有文件.文件夹大小安倒叙排列,方便清除
- 替换GitBlit的证书为域证书
GitBlit(当前版本1.6.2,http://gitblit.org/) 是一个Git版本控制的服务端,使用java编写,功能上足够满足基本的版本控制要求,而且部署很简单方便,比如windows上 ...
- 安装vim中文帮助vimcdoc
1. 下载: 下载页面:http://vimcdoc.sourceforge.net/ 选择“Latest platform independent tarball, including an Lin ...