题目

通过使用c++的类 ,重载,vector容器等相关知识来实现集合间的:交 并 补 差 对称差这五种关系
这里不直接set使用有关集合关系的函数

相关数学知识

并集:设A与B是任意两个集合,由至少属于集合A与集合B之一的一切元素构成的集合成为A与B的并集,记作A∪B(或B∪A),读作“A并B”(或“B并A”),即A∪B={x|x∈A,或x∈B}。

交集:对于给定的两个集合A 和 集合B 的交集是指含有所有既属于 A 又属于 B 的元素所构成的集合。由属于A且属于B的相同元素组成的集合,记作A∩B(或B∩A)读作“A交B”(或“B交A”),即A∩B={x|x∈A,且x∈B}。

差集:设A与B是任意两个集合,由属于A但不属于B的一切元数构成的集合称为A与B的差集,记为A\B或A-B,即A\B={x|x∈A且x∉B}。

补集:设S是一个集合,A是S的一个真子集,由S中所有不属于A的元素组成的集合,叫做子集A在S中的补集,于集也称为补集,记作CsA或Ac或A’。即Ac=S\A。

对称差:两个集合的对称差是只属于其中一个集合,而不属于另一个集合的元素组成的集合。设A与B是任意两个集合,集合 A 和 B 的对称差可记为 AΔB,对称差相当于两个相对补集的并集,即AΔB=(A-B)∪(B-A)。也可以表示为两个集合的并集减去它们的交集,即AΔB=(A∪B)-(B∩A)。

功能实现

通过创建两个类,一个学生类,一个学生集合类,分别代表元素和集合,将学生类里面的数据放在一个vector的容器中可以在学生集合类里进行储存及使用。

头文件以及实现

学生类

创建一个学生类

class Student
{
private:
	int m_uiId;
public:
	Student();
	Student(int id);
	Student(const Student& student);
	Student& operator=(const Student& student);
	bool operator==(const Student& student) const;
	bool operator!=(const Student& student) const;
	int GetID() const { return m_uiId; }
};
学生类实现

默认构造:为其赋初值,在使用rand () 函数在使用时是已经固定的值,所以创建一个随机种子来改变其值,同时用srand(time(NULL))实现随机种子,根据时间变化而产生学号,这个可以避免学号创建出来是一样的情况

Student::Student(){
	srand((unsigned int)time(NULL));
 	m_uiId = rand() % 20 + 1;
}

随机产生学生学号范围在1-20之间

 rand() % 20 + 1;

有参构造
实现参数id保证传入的参数的准确性

Student::Student(int id){
  m_uiId=id;
}

拷贝构造函数
传入进来的是一个对象,通过引用的方式进行传入,并的将传进来的对象的m_uiId赋值给本身的m_uiId进而来实现拷贝效果

Student::Student(const Student & student){
  m_uiId=student.m_uiId;
}

重载赋值运算符
这个重载赋值和拷贝函数很像,在返回值上有区别,要实现一个链式编程的思想,为了将重载赋值运算符可以链式的使用下去,所以我们需要将其返回一个指针的形式

Student &Student::operator=(const Student &student){
   m_uiId=student.m_uiId;
   return *this;
}

重载关系运算符
是一个bool类型的函数,判断是否相等,相等则返回true,否则返回false类型

bool Student::operator==(const Student &student)const{
     if(student.m_uiId==m_uiId) return true;
     return false;
}
bool Student::operator!=(const Student &student)const{
     if(student.m_uiId!=m_uiId) return true;
     return false;
}

学生集合类

接着创建 学生集合类 在学生集合类里面实现相应函数
我们创建一个Student类型的容器去存放数据

在创建学生集合类时使用了vector容器,需要加入头文件#include<vector>

class StudentSet
{
private:
	vector<Student>m_vecData;
public:
	StudentSet();
	StudentSet(const StudentSet&studentset);
	StudentSet& operator=(const StudentSet&studentset);
	bool Insert(const Student student);
	bool Remove(const int id);
	StudentSet Union(const StudentSet&studentset) const;
	StudentSet InterSect(const StudentSet&studentset) const;
	StudentSet Difference(const StudentSet&studentset) const;
	StudentSet Complement() const;
	StudentSet SymDifference(const StudentSet&studentset) const;
	bool IsEmpty() const;
	void Clear();
	void Output() const;
};
学生集合类实现

学生集合类的三个构造函数和学生类的一样,在学生集合类中,默认构造函数让其清空
注意在重载赋值函数中,要清空函数,否则会保留上一次的数据

StudentSet::StudentSet(){
   m_vecData.clear();
}
StudentSet::StudentSet(const StudentSet &studentset){
   int len=studentset.m_vecData.size();
   for(int i=0;i<len;i++){
    m_vecData.push_back(studentset.m_vecData[i]);
   }
}
StudentSet& StudentSet::operator=(const StudentSet & studentset){
	m_vecData.clear();//清空防止在下一次的使用时仍含有上一次的值
   int len=studentset.m_vecData.size();
   for(int i=0;i<len;i++){
    m_vecData.push_back(studentset.m_vecData[i]);
   }
   return *this;
}

插入和删除函数,是bool类型的函数,需要判断该集合中是否有与我们插入的id重复,如果有,则返回false,没有则返回true,同理删除函数一样
插入和删除都用到了vector容器里面的push_back函数和erase函数

判断是否有重复的思路
所使用的方法:我使用了map容器,通过map容器的两个参数值key和value,是键值对,他们之间有着一一对应的关系,同时在map容器中key 值是没有重复的。将m_vecData[i].GetID()全标记为1,此时m_vecData[i].GetID()就相当于map容器里的key值,标记的1就是相当于value值,这样在通过判断传进来的id的value值是否是1,如果是1,代表有重复的,之没有,没有重复的我们将其插入并返回true。

删除函数:就是在本身的范围内开始遍历,如果有和传入的id相同的,记录一下当前的遍历的位置,使用m_vecData.erase(m_vecData.begin()+i);的方法删除重复的那个

注:m_vecData[i].GetID()==id的要注意私有成员函数只能由该类内部成员函数调用

bool StudentSet::Insert(const Student student){//插入
	int len =m_vecData.size();
	map<int ,int >mp;
	
	for(int i=0;i<len;i++){
		mp[m_vecData[i].GetID()]=1;
	}
	if(!mp[student.GetID()]){
		m_vecData.push_back(student);
     	return true;
	}else {
		return false;
	}
}
bool StudentSet::Remove(const int id){//删除
     int len=m_vecData.size();
     for(int i=0;i<len;i++){
     if(m_vecData[i].GetID()==id){   ///私有成员函数只能被该类内部成员函数调用
         m_vecData.erase(m_vecData.begin()+i);
         return true;
        }
     }
     return false;
}
并 交 差 补 对称差

实现方式和插入函数的逻辑类似都是通过map函数的key值和value值进行判断的

并集:在本身这个集合内进行遍历将其全部插入到我们创建的的新的集合st里面,并且我们将map函数的key值为那个本身的学号,并将其的value值标记为1
int len=m_vecData.size(); for(int i=0;i<len;i++){ st.Insert(m_vecData[i]); mp[m_vecData[i].GetID()]=1; }再在传入进来的集合中进行遍历,如果在这个传入的集合中的id(key值)没有等于1的(value值),就意味着传入集合中的元素和我们本身的元素之间没有重复的,那么我们就将其插入进来 ,这样就可以得到我们这个并集了,


StudentSet StudentSet::Union(const StudentSet&studentset) const{//并集
     StudentSet st;
     map<int,int>mp;
     int len=m_vecData.size();
     for(int i=0;i<len;i++){
        st.Insert(m_vecData[i]);
        mp[m_vecData[i].GetID()]=1;
     }
     len=studentset.m_vecData.size();
     for(int i=0;i<len;i++){
        if(!mp[studentset.m_vecData[i].GetID()]){
            st.Insert(studentset.m_vecData[i]);
        }
     }
     return st;
}

交集: 同样在本身这个集合中进行遍历并将其标记为1,,然后在传入的集合中进行遍历,如果有一样被标记为1的我们就将其插入到我们所新建的那个集合中

StudentSet StudentSet::InterSect(const StudentSet&studentset) const{//交集
     StudentSet st;
     map<int,int>mp;
     for(int i=0;i<m_vecData.size();i++) {
     	mp[m_vecData[i].GetID()]=1;
	 }
     for(int i=0;i<studentset.m_vecData.size();i++){
        if(mp[studentset.m_vecData[i].GetID()]) {
        	st.m_vecData.push_back(studentset.m_vecData[i]);
		}
     }
     return st;
}

差集:在一个集合中把含有另一个集合的元素去除,我们这里是如果没有一样的元素我就插入进来
在传入的集合中进行遍历并将其标记为1,再在本身这个集合中进行遍历,如果这里面没有等于1的我们就将其插入到我们创建的新的集合中来

StudentSet StudentSet::Difference(const StudentSet&studentset) const{//差集
     StudentSet st;
     map<int,int>mp;
     int len=studentset.m_vecData.size();
     for(int i=0;i<len;i++) 
     mp[studentset.m_vecData[i].GetID()]=1;
     len=m_vecData.size();
     for(int i=0;i<len;i++){
        if(!mp[m_vecData[i].GetID()])
		st.m_vecData.push_back(m_vecData[i]);
     }
     return st;
}

补集:在全集是:IDMAX是0-20之间,求补集,我们在本身这个集合中遍历,并将其标记为1,再在全集中遍历,如果有不等于1的我们就将其插入到我们创建的新集合中来

StudentSet StudentSet::Complement() const{//补集
     StudentSet st;
     map<int ,int >mp;
     int len=m_vecData.size();
    for(int i=0;i<len;i++) 
	mp[m_vecData[i].GetID()]=1;
    for(int i=0;i<=IDMAX;i++){
		if(!mp[i]){
			Student a(i);
			st.m_vecData.push_back(a);
    	}
	}
     return st;
}

对称差实际上是两个集合的并集和交集再做一下差集
实现:创建一个新的集合a存放st和传入参数的并集,集合b存放的是st和传入参数的交集,完了再让a和b做差集,这样我们就得到了对称差

StudentSet StudentSet::SymDifference(const StudentSet&studentset) const{//对称差
     StudentSet st;
     int len=m_vecData.size();
     for(int i=0;i<len;i++) st.m_vecData.push_back(m_vecData[i]);
     StudentSet a=st.Union(studentset);
     StudentSet b=st.InterSect(studentset);
     StudentSet res=a.Difference(b);
     return res;
}

判断是否为空,清空函数以及输出函数
是否为空就是看vector.size()==0是否成立

bool StudentSet::IsEmpty() const{
     if(m_vecData.size()==0) return true;
     return false;
}
void StudentSet::Clear(){
     m_vecData.clear();
}
void StudentSet::Output() const{
     int len=m_vecData.size();
     for(int i=0;i<len;i++)
		cout<<m_vecData[i].GetID()<<endl;
}

主函数

int main()
{
	srand((unsigned int)time(NULL));
	StudentSet set1, set2, set3;
	set1.Clear();
	set2.Clear();
	set3.Clear();
	for (unsigned int i = 0; i < 5; i++)
	{
		Student tmpstudent(i);
		set1.Insert(tmpstudent);
	}
	set1.Output(); 
	system("pause");
	Student tmpstudent1(3);
	cout << set1.Insert(tmpstudent1) << endl;
	system("pause");
	Student tmpstudent2(10);
	cout << set1.Insert(tmpstudent2) << endl;
	system("pause");
	for (unsigned int i = 7; i < 12; i++)
	{
		Student tmpstudent(i);
		set2.Insert(tmpstudent);
	}
	set2.Output();
	system("pause");
	set3 = set1.Union(set2);
	set3.Output();
	system("pause");
	set3 = set1.InterSect(set2);
	set3.Output();
	system("pause");
	set3 = set1.Difference(set2);
	set3.Output();
	system("pause");
	set3 = set3.Complement();
	set3.Output();
	system("pause");
	set3 = set1.SymDifference(set2);
	set3.Output();
	system("pause");
	return 0;
}

总结

这个实验中主要使用的就是vector容器 插入、删除、清空、容量等一系列函数,运算符重载,以及map函数间的key值和value值之间的一一对应关系,以及我们对集合之间关系的掌握与理解.

注意注意
map里面有一种插入方法:(这里所有map的插入方式全都是这种)

map<int ,int>mp;
mp[1]=10;//key=1;value=10,

map中输出元素:如果是输出该容器内含有的元素,可以正常输出其value值,但是如果输出其中没有的,不会进行报错,会输出是0

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐