完成一个之前堆数据结构的模板类,关于类的创建需要注意的:
- 默认构造函数,没有声明编译器会自动创建,创建类的时候调用默认构造函数。默认构造函数可以使带默认参数的。
- 析构函数,没有声明编译器会自动创建,对动态分配来说会造成内存泄漏等问题
- 复制构造函数,类作为函数参数(按值传递)的时候会新建临时副本,就需要调用复制构造函数,之后还会调用析构函数,就会产生问题,复制构造会单纯复制非静态成员(也就是动态分配的指针,而不是带内容的指针)。所以应该自己定义复制构造函数完成内存申请等操作。同时应该函数用引用传递类对象,节约调用构造函数与空间
- 赋值运算符(=)重载一下赋值运算符,否则无法进行类赋值,用重载的函数完成数据成员的内存申请,赋值等,然后返回一个当前对象的引用即可
这个堆的类:类内一个struct数组,一个模板data,动态分配内存。一个struct里面存1权值,用来排序,2索引,用来找模板data,因为考虑到可能数据比较复杂的话,移动数据的开销比较大了,而改变一个位置里面存的索引是比较简单。同时后来加了一个valid的数据,用来记录哪些位置的data是有效的,因为后面增加了删除元素的操作,如果把后面元素前移的话就比较麻烦了,每个位置的索引都要移动,所以就加了这个valid。
#include <iostream>
#include <string.h>
using namespace std;
struct heap_struct
{
int value;//用来排序的键值,可能叫key好点
int index;//存相应数据的索引
};
template <class Type>
class heap_class
{
private:
heap_struct *p_struct;
Type *data;//数据的指针
unsigned char *data_valid;//记录哪些位置的数据是有效的
int len;//元素个数
int size;// 有效对数目 ,是在最大化堆中的一个中间变量heap_size
int max_len;//这个定义成分配的数组大小,类似vector的处理方法,避免频繁的内存操作
public:
heap_class();
heap_class(heap_class<Type> & arg);
~heap_class();
int Parent(int i);
int Left(int i);
int Right(int i);
void Swap(int & a,int & b);
void Swap(heap_struct & a,heap_struct & b);
void MaxHeap(int i);
void BuildMaxHeap();
//插入元素与相应键值
void Insert(const Type & x,const int &value);
void ShowAll();
Type MaxEle();//返回最大键值的元素,但不从类内删除
Type DelMaxEle();//删除最大键值的元素,并返回
bool IncreaseKey(const Type x,const int k);//把元素x的键值增加到k,就需要从元素反找键值了,可惜数据里面没存键值的信息,没办法很快反找到。只能遍历找了
};
template <class Type>//默认构造函数
heap_class<Type>::heap_class()
{
len = 0;
size = 0;
max_len = 1024;//默认大小1024
p_struct = new heap_struct[max_len];
data = new Type[max_len];
data_valid = new unsigned char[max_len];
memset(data_valid,0,sizeof(unsigned char)*max_len);
cout<<"default"<<endl;
}
template <class Type>//构造函数
heap_class<Type>::heap_class(heap_class<Type> & arg)
{
len = arg.len;
size = arg.size;
max_len = arg.max_len;
delete [] p_struct;
p_struct = new heap_class[max_len];
delete [] data;
data = new Type[max_len];
data_valid = new unsigned char[max_len];
memcpy(p_struct,arg.p_struct,sizeof(heap_struct)*len);
memcpy(data,arg.data,sizeof(Type)*len);
memcpy(data_valid,arg.data_valid,sizeof(unsigned char)*len);
}
template <class Type>//默认析构函数
heap_class<Type>::~heap_class()
{
len = 0;
size = 0;
delete [] p_struct;
delete [] data;
delete [] data_valid;
}
template <class Type>
inline int heap_class<Type>::Parent(int i)//返回父节点的坐标索引,数组都是索引从0开始,
{
return (i+1)/2-1;
}
template <class Type>
inline int heap_class<Type>::Left(int i)//返回左子节点的坐标索引,数组都是索引从0开始,
{
return 2*(i+1)-1;
}
template <class Type>
inline int heap_class<Type>::Right(int i)//返回右子节点的坐标索引,数组都是索引从0开始,
{
return 2*(i+1);
}
template <class Type>
inline void heap_class<Type>::Swap(int & a,int & b)
{
int tmp;
tmp = a;
a = b;
b = tmp;
return;
}
template <class Type>
inline void heap_class<Type>::Swap(heap_struct & a,heap_struct & b)
{
heap_struct tmp;
tmp = a;
a = b;
b = tmp;
return;
}
template <class Type>
void heap_class<Type>::MaxHeap(int i)//i是索引,对键值结构的处理,不涉及到数据
{
int l = Left(i);//9
int r = Right(i);//10
int largest = i;//4
if(l<size && p_struct[l].value>p_struct[i].value)//这里size是数目,第一次是n,而l,r是索引,所以比较的 边界是size-1
largest = l;
if(r<size && p_struct[r].value>p_struct[largest].value)
largest = r;
cout<<"heap i="<<i<<"size="<<size<<"largest="<<largest<<endl;
if(largest!=i)
{
Swap(p_struct[i],p_struct[largest]);
MaxHeap(largest);
}
return;
}
template <class Type>
void heap_class<Type>::BuildMaxHeap()//对键值结构的处理,不涉及到数据
{
size = len;//这步假设全都没排序,然后用于调用MaxHeap
for(int i=len/2-1;i>=0;i--)
MaxHeap(i);
cout<<"after build"<<endl;
for(int i=0;i<len;i++)
cout<<"i="<<i<<" value[i]="<<p_struct[i].value<<" index="<<p_struct[i].index<<endl;
}
template <class Type>
void heap_class<Type>::Insert(const Type &x,const int &value)
{
len +=1;
size +=1;
heap_struct s;
//s.index = len-1;一开始没valid的index处理
for(int i=0;i<len;i++)
{
if(data_valid[i] == 0)
{
s.index = i;
data_valid[i] = 1; //这个位置用过
break;
}
}
s.value = value;
if(max_len<len)//看看加入新元素以后 有没有超过原来max_len,超过了就开辟空间再复制元素过去
{
max_len += 1024;
heap_struct *tmp_struct = new heap_struct[max_len];
if(p_struct!=NULL)
{
cout<<"memcpy"<<endl;
cout<<"memcpy len="<<sizeof(heap_struct)*(len-1)<<endl;
memcpy(tmp_struct,p_struct,sizeof(heap_struct)*(len-1));//这里len加了一,所以把原来的数据复制过来的时候长度减个1
}
tmp_struct[len-1] = s;
if(p_struct!=NULL)
{
cout<<"delete"<<endl;
delete [] p_struct;
}
p_struct = tmp_struct;
Type *tmp_data = new Type[max_len];
if(data!=NULL) memcpy(tmp_data,data,sizeof(Type)*(len-1));
tmp_data[s.index] = x;
if(data!=NULL) delete [] data;
data = tmp_data;
}
else//没超过的话,直接加入新元素
{
p_struct[len-1] = s;
data[s.index] = x;
}
cout<<"cpy finished"<<endl;
cout<<"size="<<size<<"len="<<len<<endl;
BuildMaxHeap();
return;
}
template <class Type>
void heap_class<Type>::ShowAll()
{
for(int i=0;i<len;i++)
{
cout<<"value="<<p_struct[i].value<<endl;
}
for(int i=0;i<len;i++)
{
cout<<"index="<<p_struct[i].index<<endl;
}
for(int i=0;i<len;i++)
{
cout<<"data="<<data[i]<<endl;
}
}
template <class Type>
Type heap_class<Type>::MaxEle()
{
int index_tmp;
index_tmp = p_struct[0].index;
return data[index_tmp];
}
template <class Type>
Type heap_class<Type>::DelMaxEle()//感觉可以不考虑效率的话,直接删掉元素然后调用build即可,但会对除了顶层根节点以外的其他根节点进行一次判断,但是单纯只是判断,还可以接受
{
if(len<0)
{
Type tmp;
cout<<"no element"<<endl;
return tmp;
}
heap_struct tmp = p_struct[0];//最大键值
Type max_element = data[tmp.index];//最大键值对应的元素
data_valid[tmp.index] = 0;//这个位置又空了下来
p_struct[0] = p_struct[len-1];//最后一个位置的键值拿过来
size = size-1;//这一步可有可无,因为排序的时候先假设了size=len
len = len-1;
MaxHeap(0);
//这里也可以调 BuildMaxHeap(); 里面会从其他根节点判断一次,一直判断到顶层根节点才会出现不符合的情况,再调用这个Maxheap方法,
return max_element;
}
template <class Type>
bool heap_class<Type>::IncreaseKey(const Type x,const int k)
{
int index,flag=0;
for(index=0;index<len;index++)
{
if(data[index]==x)
{
flag=1;
break;
}
}
if(flag==0)
{
cout<<"can't find "<<x<<endl;
return false;
}
for(int i=0;i<len;i++)
{
if(p_struct[i].index==index)
{
p_struct[i].value = k;
return true;
}
}
}
int main()
{
class heap_class<char> test;
char c[11] = "abcdefghij";
test.Insert(c[0],16);
test.Insert(c[1],14);
test.Insert(c[2],10);
test.Insert(c[3],4);
test.Insert(c[4],7);
test.Insert(c[5],9);
test.Insert(c[6],3);
test.Insert(c[7],2);
test.Insert(c[8],8);
test.Insert(c[9],1);
test.ShowAll();
char c_max = test.DelMaxEle();
cout<<"c_max="<<c_max<<endl;
test.ShowAll();
test.Insert('A',15);
test.ShowAll();
test.IncreaseKey('A',18);
test.ShowAll();
return 0;
}
半个月过去再回头看写出来的时候发现有很多改进的地方,以后有空再看