刚刚从lwm老师处验收回来,打算恶补下C++......
题目:设计实现集合类,元素类型为整型, 集合采用带头结点单链表表示。该集合类成员函数须支持集合元素增加、删除、查询、显示,并支持集合并、交、差运算,运算结果要求返回集合对象;利用你设计的集合类,实现本题要求。为保证结果唯一,集合元素递增排列。要求实现拷贝构造和复制赋值重载、移动构造和移动赋值,不可有内存泄漏。
输入描述
开始为两个正整数m,n;后续m个整数构成集合A,再后续n个整数构成集合B
输出描述
样例输入
3 5
1 2 3
3 5 8 2 1样例输出
{1,2,3}
{1,2,3,5,8}
{1,2,3,5,8}
{1,2,3}
{}
直接上代码:
#include <iostream> using namespace std; class Node { public: int data; Node *next; }; class Set { private: Node *la;//私有成员为一个指向节点的指针,可被其他所有函数共用 public: Set();//构造函数 Set(const Set &Copy);//拷贝构造,参数为待被拷贝的集合类,且实现过程中不改变它,传引用提高效率 Set& operator=(const Set &Copy);//赋值运算符重载 Set(Set &&Copy);//移动构造 Set operator=(Set &&Copy);//移动赋值 ~Set();//有动态分配内存,需要析构函数 void Add(int x);//对当前集合增加元素x,传入一个需要增加的整型x,无返回值 void Delete(int x);//同上删除 bool Search(int x) const;//查找元素x,有则true,无则false,const保证查找过程中当前链表不被改变 void Display() const;//显示集合形式,const保证显示过程中当前链表不被改变 Set Union(const Set &) const;//并集,实现过程中传入的集合和当前集合均不被改变并且返回一个新的集合 //注1 Set Inters(const Set &) const;//交集,同上 Set Compl(const Set &) const;//差集,同上 }; Set::Set() { la=new Node; la->next=nullptr; }//申请头节点并置空next,构造了空链表 Set::Set(const Set &Copy) { la=new Node; Node *s=Copy.la->next; Node *last=la; while(s) { Node *p=new Node; p->data=s->data; last->next=p; last=p; s=s->next; } last->next=nullptr; }//在实现拷贝构造函数前不会执行其他函数,故需建立链表 Set& Set::operator=(const Set &Copy) { if(this!=&Copy) //此处&为取地址,即判断主函数中等式左右两边指的不是同一内存单元 { Set tmp(Copy);//拷贝构造 Node *t=tmp.la; tmp.la=la; la=t; } return *this; } //通过交换头结点的指针达到赋值的目的,且当前对象的原内存空间随tmp集合的析构而释放,此过程中对参数集合没有任何影响 //此处为何返回引用尚不明白 //注2 Set::Set(Set &&Copy) { la=Copy.la; Copy.la=nullptr;//将原链表的头节点置空,防止同一内存空间被释放两次造成异常 } //移动构造,用于临时对象,提高运行效率,不需要拷贝构造的申请和释放空间(此处需要编译器支持C++11标准) Set Set::operator=(Set &&Copy) { Node *p=Copy.la; Copy.la=la; la=p; return *this; } //移动赋值 //深一步原理还不是很明白 bool Set::Search(int x) const { Node *p=la->next; while(p) { if(p->data==x) { return true; } else { p=p->next; } } return false; } void Set::Add(int x) { if(!Search(x)) { Node *p=new Node; p->data=x; Node *q=la->next; Node *last=la;//一定注意指针变量的初始化 while(q&&x>q->data) { last=q; q=q->next; }//找到x应该插入的位置 p->next=q; last->next=p;//插入x } } //Add函数具有查重和排序的功能 void Set::Delete(int x) { Node *p=la->next; Node *last=la->next; while(p) { if(x==p->data) { last->next=p->next; delete p; } else { last=p; p=p->next; } } } void Set::Display() const { Node *p=la->next; cout<<"{"; if(p) { cout<<p->data; while(p->next) //在p存在的前提下,p->next有意义 { cout<<","<<p->next->data; p=p->next; } } cout<<"}"<<endl; } Set Set::Union(const Set &s1) const { Set s2(s1); Node *p=la->next; while(p) { s2.Add(p->data); p=p->next; } return s2; } Set Set::Inters(const Set &s1) const { Set s2; Node *p=la->next; while(p) { if(s1.Search(p->data)) { s2.Add(p->data); } p=p->next; } return s2; } Set Set::Compl(const Set &s1) const { Set s2; Node *p=la->next; while(p) { if(!s1.Search(p->data)) { s2.Add(p->data); } p=p->next; } return s2; } Set::~Set() { Node *p=la; while(p) { Node *q=p->next;//在删除节点前把节点的后一个节点记下,否则无法访问 delete p; p=q; } } int main() { int x,y; Set s1,s2,s3; cin>>x>>y; int i; for(i=0; i<x; i++) { int t; cin>>t; s1.Add(t); } for(i=0; i<y; i++) { int t; cin>>t; s2.Add(t); } s1.Display(); s2.Display(); s3=s1.Union(s2); s3.Display(); s3=s1.Inters(s2); s3.Display(); s3=s1.Compl(s2); s3.Display(); return 0; }
注1:并、交、差函数应返回集合类,不能返回引用(函数中的集合类是局部变量);并且,上述的代码中实现并交差的算法效率为M*N,在数据较大时就不好跑了,M+N效率的实现先挖个坑,下次补上...
注2:这种拷贝构造的写法具有异常安全的特点,当拷贝构造申请内存空间失败后,可以抛出异常,当什么都没有发生过一样不进行赋值;同时,若此处传参数值而不是引用则会引起重载二义性。