要求:
1、完成priority queue的基本功能
2、在至多logn的时间复杂度合并两个priority queue
基本功能(顺序存储)
#ifndef SJTU_PRIORITY_QUEUE_HPP
#define SJTU_PRIORITY_QUEUE_HPP
#include <cstddef>
#include <functional>
#include "exceptions.hpp"
#include<iostream>
namespace sjtu {
// a container like std::priority_queue which is a heap internal.
//用cmp!!!Compare函数!
template<typename T, class Compare = std::less<T>>
class priority_queue {
private:
T *data;
int siz,capacity;
void DoubleSpace(){
if(capacity==0) {
capacity = 5 ;
data = (T *) malloc(sizeof(T)*capacity);
return ;
}
capacity *= 2;
T *data2 = (T *)malloc(sizeof(T)*capacity);
for(int i=1;i<=siz;i++){//注意这里siz < capacity/2!
new(data2+i)T(data[i]);
}
for(int i=1;i<=siz;i++){
data[i].~T();
}
free(data);
data = data2;
}
public:
// TODO constructors
priority_queue(int maxsize = 5) {
siz = 0;
// capacity = 0;//不能不初始化!!!!capacity=0要写!!
// DoubleSpace();//或者采用未注释的写法
data = (T *)malloc (sizeof(T)*maxsize);
capacity = maxsize;
}
priority_queue(const priority_queue &other) {//copy constructor
data = (T *) malloc(sizeof(T)*other.capacity);
for(int i=1;i<=other.siz ;i++){
new(data+i)T(other.data[i]);
}
capacity = other.capacity ;
siz = other.siz ;
}
//TODO deconstructor
~priority_queue() {
for(int i=1;i<=siz;i++){
data[i].~T();
}
free(data);
siz = 0;
capacity = 0;
data = NULL;
}
//TODO Assignment operator
priority_queue &operator=(const priority_queue &other) {
if(this == &other) return *this;
for(int i=1;i<=siz;i++){
data[i].~T();
}
if(other.siz > capacity){
free(data);
data = (T *)malloc(sizeof(T)*other.capacity);
capacity = other.capacity;
}
for(int i=1;i<=other.siz;i++){
new(data+i)T(other.data[i]);
}
siz = other.siz;
return *this;
}
/* get the top of the queue.
* @return a reference of the top element.
* throw container_is_empty if empty() returns true;
*/
const T & top() const {
if(empty()) throw container_is_empty();
return data[1];
}
// push new element to the priority queue.
void push(const T &e) {//大顶堆
if(siz>=capacity-1)
DoubleSpace();
siz ++;
int hole = siz;
new(data+hole)T(e);//是这里,要写一个构造的而不是重载等于,因为本身没有T类型的变量
while(hole>1 && Compare()(data[hole>>1],e)){
data[hole] = data[hole>>1];//不需要重复析构,因为在T类型重载等号已经写了
hole = hole>>1;
}
data[hole] = e;
}
/* delete the top element.
* throw container_is_empty if empty() returns true;
*/
void pop() {
if(siz==0) throw container_is_empty();
int hole = 1;
while(hole*2 < siz){//至少有一个儿子在下面呀!
int child = hole<<1;
if(child+1<siz && Compare()(data[child],data[child+1])){
child++;
}
if(Compare()(data[child],data[siz])) break;
data[hole] = data[child];
hole = child;
}
data[hole] = data[siz];
data[siz].~T();
siz--;
}
// return the number of the elements.
size_t size() const {
return siz;
}
/* check if the container has at least an element.
* @return true if it is empty, false if it has at least an element.
*/
bool empty() const {
if(siz) return false;
return true;
}
};
}
#endif
- 用
Compare
函数来确定是大顶堆还是小顶堆!这里是大顶堆 - 没法实现logn的合并
- 在
doublespace
的时候特别注意siz
和capacity
的关系,因为进入doublespace
的时候siz = capacity-1
(申请5个空间,0不能用,只能放四个值),不管是析构还是复制都要小心,判别是否进入doublespace
时也要小心 - 顺序存储特别注意构造和析构,什么时候写构造什么时候写赋值(赋值是可能析构掉原来的,看具体的实现),析构是你做还是赋值的时候做,析构时要不要一起析构掉空间……都很重要
连接存储(左堆)
#ifndef SJTU_PRIORITY_QUEUE_HPP
#define SJTU_PRIORITY_QUEUE_HPP
#include <cstddef>
#include <functional>
#include "exceptions.hpp"
namespace sjtu {
/**
* a container like std::priority_queue which is a heap internal.
*/
template<typename T, class Compare = std::less<T>>
class priority_queue {
private:
struct Node{
Node *left,*right;
T *data;
int npl;
Node(const T &x,Node *l = NULL,Node *r = NULL){
data = new T(x);
left = l;
right = r;
npl = -1;
}
~Node(){
if(data) delete data;//易忘if
left = NULL;
right = NULL;
}
};
Node *head;
int siz;
Node *copy(Node *q){
Node *p = new Node(*(q->data));
if(q->left){
p->left = copy(q->left);
}
if(q->right){
p->right = copy(q->right);
}
return p;
}
void clear(Node *p){//有可能clear的时候本身就是p==NULL!!
if(p==NULL) return ;
if(p->left)
clear(p->left);
if(p->right)
clear(p->right);
delete p;
return ;
}
void getnpl(Node *u){
if(u==NULL) {//npl=-1
return ;
}
if(u->left==NULL || u->right==NULL) {
u->npl = 0;
return ;
}
int x,y;
if(u->left!=NULL && u->left->npl == -1)
getnpl(u->left);
if(u->right!=NULL && u->right->npl == -1)
getnpl(u->right);
if(u->left->npl < u->right->npl)
u->npl = u->left->npl+1;
else u->npl = u->right->npl+1;
return;
}
void check(Node *u){
getnpl(u);
if(u->left==NULL || u->left->npl < u->right->npl){
Node *tmp = u->left;
u->left = u->right;
u->right = tmp;
}
return ;
}
Node *merge(Node *u,Node *v,Node *fau,Node *fav){//考虑一下合并后u原先的father还指着它!
if(u==NULL) return v;
if(v==NULL) return u;
Node *fa = fau;
//85-86行先声明Compare类型变量再用重载的括号,和直接写if(Compare()(*(v->data),*(u->data)))效果一样
Compare cmp;
if(cmp(*(v->data),*(u->data))){
Node *tmp = u;
u = v;
v = tmp;
fa = fav;
}//u是小的 ,用u和v的右子树合并
if(v->right == NULL){
v->right = u;
check(v);
return v;
}
v->right = merge(u,v->right,fau,v);
check(v);
if(fa==NULL) return v;
if(fa->left == u) fa->left = NULL;
if(fa->right ==v) fa->right = NULL;
return v;
}
public:
priority_queue() {
siz = 0;
head = NULL;
}
priority_queue(const priority_queue &other) {
siz = other.siz ;
head = copy(other.head);
}
~priority_queue() {
clear(head);
head = NULL;
siz = 0;
}
priority_queue &operator=(const priority_queue &other) {
if(this == &other) return *this;//易忘
clear(head);
siz = other.siz ;
head = copy(other.head);
return *this; //易忘
}
const T & top() const {
if(empty()) throw container_is_empty();
return *(head->data);
}
void push(const T &e) {
Node *tmp = new Node(e);
head = merge(head,tmp,NULL,NULL);//特判放在merge里面做了
siz ++;
}
void pop() {
if(empty()) throw container_is_empty();
Node *tmp = head;
head = merge(head->left,head->right,NULL,NULL);
delete tmp;
siz--;
}
size_t size() const {
return siz;
}
bool empty() const {
return siz==0;
}
/**
* merge two priority_queues with at least O(logn) complexity.
* clear the other priority_queue.
*/
void merge(priority_queue &other) {//传的参数是堆,里面的函数传进去结点
head = merge(head,other.head,NULL,NULL);
siz += other.siz;
other.head = NULL;
other.siz = 0;
return ;
}
};
}
#endif
- 写好了
merge
和其他private
函数以后public
写起来很舒服哎! - 关于
Compare
的用法:Compare
在code
实现里面是一个结构体cmp
,使用的时候可以先声明一个Compare
实例,然后再用重载的调用运算符即括号,来使用cmp
操作;也可以写一个语句即if(Compare()(*(v->data),*(u->data)))
,第一个空括号表示构造一个Compare
类型的实例,第二个括号才表示使用这个重载的调用运算符。 - 注意
merge
里面的特判……如果传进去俩NULL
咋整? check
也记得特判,很有可能u->left
就是NULL
,你用->left->data
就会377npl
要用一定程度的记忆化,否则每次都搜会超时,虽然我用的记忆化方法也不是很快,但是好歹没有TLE- 其他特别特别容易写忘记的点:
delete
之前先判是不是NULL
,不是再delete
重载等号的时候,开头的判if(this==&other)
,尾巴的return *this;
- 以后做题前能不能先看看题,要用顺序还是链接,就不至于像这次一样两个都写……