一.结点和类
template<class T>
struct Node
{
T key;
int degree;
Node<T>* left;//左右兄弟
Node<T>* right;
Node<T>* child;//一个孩子
Node<T>* parent;
bool mark;//标记
Node(T k)
:key(k)
{
degree = 0;
mark = false;
left = this;//建立头尾相连的链表
right = this;
parent = nullptr;
child = nullptr;
}
};
template<class T>
class FiHeap
{
private:
int N;
Node<T>* min;
public:
FiHeap();
void Insert(Node<T>* n);
void Insert(T key);
void Union(FiHeap<T>& H);
T pop();
void SimpleList();
void Link(Node<T>* y, Node<T>* x);//y作为x孩子
void remove(Node<T>* n);//结点n从当前链表中移除 remove后n与左右节点失去联系,要判断n==n->right应提前保存n->right
void ADDNode(Node<T>* n, Node<T>* x);//n插在x左侧
void Cut(Node<T>* x, Node<T>* y);
void Cut(Node<T>* x);
void Decrease(Node<T>* x, T k);
void Decrease(T K, T k);
Node<T>* search(T key,Node<T>* root);
Node<T>* Search(T key);
void Delete(Node<T>* x);
void Delete(T k);
void out(Node<T>* root);
void Out();
};
//最大度数 D(n)<=low(loga n) a=(1+sqrt(5))/2
//D(n)=O(lgn)
//定义势函数 F(H)=t(H)+2m(H) t(H)为树的数目 m(H)为已标记结点数
//系数为2的原因:在关键字减值中Cut()操作时,当一个结点因为标记被切掉
//标记减一,势减小2。一个单位的势支付切断和标记位的清除,另一单位的
//补偿结点变为根结点增加的势
template<class T>
FiHeap<T>::FiHeap()
{
N = 0;
min = nullptr;
}
二.插入
//将新结点插入根链表,实际代价 O(1)
//势增加量 t(H)+1+2m(H)-( t(H)+2m(H) )=1
//摊还代价 O(1)+1=O(1)
template<class T>
void FiHeap<T>::Insert(Node<T>* n)//插在min左侧
{
if (N == 0)
min = n;
else
{
ADDNode(n, min);
if (n->key < min->key)
min = n;
}
N += 1;
}
template<class T>
void FiHeap<T>::Insert(T key)
{
Node<T>* n = new Node<T>(key);
Insert(n);
}
template<class T>
void FiHeap<T>::ADDNode(Node<T>* n, Node<T>* x)//n插在x左侧
{
n->right = x;
n->left = x->left;
x->left->right = n;
x->left = n;
}
三.合并
//将两个堆的根链表链接,再从两个min中找最小的作为min结点
//实际代价 O(1)
//势的变化: t(H)+2m(H)-( t(H1)+2m(H1) )- ( t(H2)+2m(H2) )
//且 t(H)=t(H1)+t(H2) m(H)=m(H1)+m(H2)
//势的变化 =0
//摊还代价为 O(1)
template<class T>
void FiHeap<T>::Union(FiHeap<T>& H)
{
if (H.N == 0)
return;
if (N == 0)
{
min = H.min;
N = H.N;
H.N = 0;
H.min = nullptr;
return;
}
//“双向且头的左指针指向尾、尾的右指针指向头”的链表的特性
Node<T>* t = min->right;
min->right = H.min->right;
H.min->right->left = min;
H.min->right = t;
t->left = H.min;
if (min->key > H.min->key)
min = H.min;
N += H.N;
//被合并的堆失效
H.N = 0;
H.min = nullptr;
}
四.提取最小结点
//提取最小关键字
//让z的所有孩子成为根结点,再移除z。再合并相同度数的树,减少树的数目。
//实际代价: O( D(n)+t(H) ) D(n)为最大度数
//由于合并相同度数树后最大度数为D(n),故最多有D(n)+1个根留下
//操作后势最大为 D(n)+1+2m(H) 势变化量最多为:D(n)+1-t(n)
//摊还代价最多为:O( D(n) )
template<class T>
T FiHeap<T>::pop()
{
Node<T>* z = min;
T res = z->key;
if (z)//非空堆
{
Node<T>* q = z->child, *x;
//遍历所有孩子
//z->child只指向一个孩子,但z的所有孩子构成一个“双向且头的左指针指向尾、尾的右指针指向头”的链表
if (q)
{
x = q->right;//ADDNode后q的右指针改变,提前保存
ADDNode(q, min);
q->parent = nullptr;
q = x;
while (q != z->child)//双向链表,回到原结点,遍历完
{
x = q->right;
ADDNode(q, min);
q->parent = nullptr;
q = x;
}
}
//将z从根链表移除(将z的孩子提升过程中z尚在根链表中)
Node<T>* pre = z->right;//remove 中会使z->right=z 提前保存便于下面比较是否为空堆
remove(z);
if (z == pre)//根链表只有z,移除z后为空堆
{
min = nullptr;
}
else
{
min = pre;//先假定一个min(不一定真的为最小)
SimpleList();//合并相同度数的子树
}
N -= 1;
}
delete z;
return res;
}
//对每一个根判断,先加入数组A并从根链表中移去,,若A[d]冲突则小的作为父结点
template<class T>
void FiHeap<T>::SimpleList()
{
double a = (sqrt(5) + 1) / 2.0;
int maxdegree = log(N) / log(a);//最带度数
Node<T>** A = new Node<T>*[maxdegree + 1];
for (int i = 0; i <= maxdegree; i++)
A[i] = nullptr;
Node<T>* x, *y;
int d;
while (min)//将根节点先提取到A数组中,当min为空指针,则所有节点都已提取到A数组中,不再存在相同度数的子树
{
x = min;
d = x->degree;
//将x从根链表提取出来
Node<T>* pre = min->right;
remove(min);
if (min == pre)
{
min = nullptr;//便于跳出循环
}
else
{
min = pre;
}
while (A[d])//出现重复度数
{
y = A[d];
if (x->key > y->key)//关键字小的作为孩子
{
Node<T>* t = x;
x = y;
y = t;
}
Link(y, x);
A[d] = nullptr;
d += 1;
}
A[d] = x;//当前A数组中已无重复度数
}
for (int i = 0; i <= maxdegree; i++)//组成根链表
{
if (A[i])
{
if (min == nullptr)
min = A[i];
else
{
ADDNode(A[i], min);
if (A[i]->key < min->key)
min = A[i];
}
}
}
}
template<class T>
void FiHeap<T>::remove(Node<T>* n)//结点n从当前链表中移除 remove后n与左右节点失去联系,要判断n==n->right应提前保存n->right
{
if (n == n->right)
return;
n->left->right = n->right;
n->right->left = n->left;
n->left = n; //保证任意单个结点(除孩子外)的左右指针均指向自己
n->right = n;
n->parent = nullptr;
}
template<class T>
void FiHeap<T>::Link(Node<T>* y, Node<T>* x)//y作为x的孩子
{
if (x->child == nullptr)
{
x->child = y;
y->parent = x;
x->degree += 1;
return;
}
ADDNode(y, x->child);
x->child = y;
y->parent = x;
x->degree += 1;
y->mark = false;
}
五.关键字减值
//关键字减值
//假定调用Cut(一个参数)共c次,(c-1次递归),则操作实际代价O(c)
//此后根链表包含 t(H)+c个结点
//c-1个结点被清除标记,最后一次调用可能再标记一个,后最多有m(H)-c+2个标记
//势变化最多:t(H)+c+2( m(H)+4-c )-( t(H)+2m(H) )=4-c
//摊还代价最多为:O(c)+4-c=O(1)
template<class T>
void FiHeap<T>::Decrease(Node<T>* x, T k)
{
x->key = k;
Node<T>* y = x->parent;
if (y&&x->key < y->key)
{
Cut(x, y);
Cut(y);
}
if (x->key < min->key)
min = x;
}
template<class T>
void FiHeap<T>::Decrease(T K, T k)
{
Decrease(Search(K), k);
}
template<class T>
void FiHeap<T>::Cut(Node<T>* x, Node<T>* y)
{
if (x == x->right)
y->child = nullptr;
else
y->child = x->right;
remove(x);
ADDNode(x, min);
x->mark = false;
}
template<class T>
void FiHeap<T>::Cut(Node<T>* x)
{
Node<T>* z = x->parent;
if (z)
{
if (!x->mark)
x->mark = true;
else //x失去了两个孩子,破坏了性质(推算最大度数时假定作为孩子的结点在作为一个结点的孩子期间最多失去一个孩子
{
Cut(x, z);
Cut(z);
}
}
}
六.删除
//删除结点
//摊还实践为Decrease和pop之和
//摊还代价:O( D(n) )
template<class T>
void FiHeap<T>::Delete(Node<T>* x)
{
Decrease(x, INT_MIN);
cout << pop() << endl;
}
template<class T>
void FiHeap<T>::Delete(T k)
{
Delete(Search(k));
}
七.其它
template<class T>
Node<T>* FiHeap<T>::search(T key,Node<T>* root)
{
if (!root||root->key == key)
return root;
Node<T>* q = root->child;
Node<T>* ans;
if (q)
{
if (ans = search(key, q))
return ans;
q = q->right;
while (q != root->child)
{
if (ans = search(key, q))
return ans;
q = q->right;
}
}
return nullptr;
}
template<class T>
Node<T>* FiHeap<T>::Search(T key)
{
Node<T>* ans;
if (ans = search(key,min))
return ans;
Node<T>* q = min->left;
while (q != min)
{
if (ans = search(key, q))
return ans;
q = q->left;
}
}
template<class T>
void FiHeap<T>::out(Node<T>* root)
{
if (!root)
return;
cout << root->key << " ->( ";
Node<T>* q = root->child;
if (q)
{
out(q);
q = q->right;
while (q != root->child)
{
out(q);
q = q->right;
}
}
cout << " )";
}
template<class T>
void FiHeap<T>::Out()
{
Node<T>* q = min;
out(min);
cout << endl;
q = q->right;
while (q != min)
{
out(q);
cout << endl;
q = q->right;
}
}