490 likes | 652 Views
集合 等价类与并查集. 第七章 集合. 一、集合. A={a,b,c}, B={b,d}. 若干个 同一类型 互不相同 的以 一定次序排 列 的元素 整形 集合 (整数,字符,枚举,记录) 若干 离散 有序元素组成的集合 每个元素可以 对应 唯一一个整数 int(a) ; //a 对应的整数. 集合的运算 a∈A A∪B={a,b,c,d} A∩B={b} A-B={a,c}. 集合的实现. 用 数组 实现 用 指针 实现 用 链表 实现 用 位运算 实现 整形集合.
E N D
集合 • 等价类与并查集 第七章 集合
一、集合 A={a,b,c}, B={b,d} 若干个同一类型互不相同的以一定次序排 列的元素 整形集合(整数,字符,枚举,记录) 若干离散有序元素组成的集合 每个元素可以对应唯一一个整数 int(a); //a对应的整数
集合的运算 a∈A A∪B={a,b,c,d} A∩B={b} A-B={a,c}
集合的实现 用数组实现 用指针实现 用链表实现 用位运算实现整形集合
位运算 或 | 与& 非~ 异或^右移位>> 左移位<< —————————————————— x y ~x x|y x&y x^y —————————————————— 0 0 1 0 0 0 0 1 1 1 0 1 1 0 0 1 0 1 1 1 0 1 1 0
x=11100011 y=01110110 x|y 11100011 01110110 11110111 x&y 11100011 01110110 01100010 ~x = 00011100 x^y 11100011 01110110 10010101
unsigned short x=10, y=13,z;//2字节16位//x=1010, y=1101 z=x|y; //z=15 z=x&y; //z=8 z=x^y; //z=7 z=~0<<2; //z=65532 z=~0&(y>>2); //z=3
用01数组存储n个整形元素的集合 将n个元素固定成一列,每个下标代表一个元素 每个元素elt的下标可以由int(elt)或用一个公式算出 用一个01数组存储集合 每一位写0或1 0代表某个元素不在集合中, 1代表某个元素在集合中, n个元素最少用多长的数组呢?
一个unsigned short 整数占两个字节16位k个unsigned short 整数占两个字节k*16位 只要一个二进制位就可以存储0,1 k*16=n, k=(n+15)/16, k=(n+15)>>4. 存储n个元素只要k个unsigned short 整数 unsigned short *member; member=new unsigned short[k];
15 31 1 1 1 1 1 A={a1,a7,a13,a18,a25} 确定一个元素在哪一个字节第几位 第18号元素a18, 在member[1] 2号位 设第s号元素,在member[i] j号位 i=s/16=s>>4; j=s%16=1 << (s & 15);
两集合A,B的并 按对应字节求或| for(i=0;i<k;i++) C.member[i]= A.member[i] | B.member[i]; 两集合的交 按对应字节求与& for(i=0;i<k;i++) C.member[i]= A.member[i] & B.member[i]; x∈A 只要 A∩{x}非空 A.member[i] & (1<<j); //x在member[i] j号位
#ifndef SET_CLASS #define SET_CLASS #include <iostream.h> #include <stdlib.h> enumErrorType { InvalidMember, ExpectRightBrace, MissingValue, MissingComma, InvalidChar, MissingLeftBrace, InvalidInputData, EndOfFile, OutOfMemory, InvalidMemberRef, SetsDifferentSize };
template <classT> class Set { private: int setrange; // max number of elements in the set int arraysize; unsigned short *member; void Error(ErrorType n) const; int ArrayIndex(constT& elt) const; unsigned short BitMask(constT& elt) const; public: Set(int sz); Set(const Set<T>& x);
~Set(void); Set<T>& operator= (const Set<T>& rhs); int IsMember(constT& elt); intoperator== (const Set<T>& x) const; Set operator+ (const Set<T>& x) const; // union Set operator* (const Set<T>& x) const; void Insert(constT& elt); // set insertion void Delete(constT& elt); //deletion operations friend istream& operator>> (istream& istr, Set<T>& x); friend ostream& operator<<(ostream&ostr, const Set<T>& x); };
template <classT> void Set<T>::Error (ErrorType n) const { cout << endl; switch(n) { case InvalidMember: cerr << "Invalid set member"; break; case ExpectRightBrace: cerr << "Expect right brace '}'"; break; case MissingValue: cerr<< "Missing a value after a comma"; break; case MissingComma: cerr << "Separate members with a comma";break; case InvalidChar: cerr << "Invalid set character"; break;
case MissingLeftBrace: cerr << "Missing left brace '{'"; break; case InvalidInputData: cerr << "Invalid input data element"; break; case EndOfFile: cerr << "Premature end of file"; break; case OutOfMemory: cerr << "Memory allocation failure"; break; case InvalidMemberRef: cerr << "Invalid member reference"; break; case SetsDifferentSize: cerr << "Sets are not the same size"; break; } cout << endl; exit(1); }
template <classT> int Set<T>::ArrayIndex(const T& elt) const { // convert elt to int and shift returnint(elt) >> 4; } template <classT> unsigned short Set<T>::BitMask(constT& elt) const { // use & to find remainder after dividing by // 16. 0 stays in right-most bit, 15 goes on far left return 1 << (int(elt) & 15); }
// constructor. create an empty set template <classT> Set<T>::Set(int sz): setrange(sz) {// number of unsigned shorts needed to hold set elements arraysize = (setrange+15) >> 4; // allocate the array member = newunsigned short [arraysize]; if (member == NULL) Error(OutOfMemory); // create an empty set by setting all bits to 0 for (int i = 0; i < arraysize; i++) member[i] = 0; }
template <classT> Set<T>::Set(const Set<T>& x) { setrange = x.setrange; arraysize = x.arraysize; member = new unsigned short [arraysize]; if (member == NULL) Error(OutOfMemory); // copy set elements from x for (int i = 0; i < arraysize; i++) member[i] = x.member[i]; }
template <classT> Set<T>::~Set(void) { delete [] member; } template <classT> Set<T>& Set<T>::operator= (const Set<T>& rhs) { if (setrange != rhs.setrange) Error(SetsDifferentSize); // copy set elements from rhs for (int i = 0; i < arraysize; i++) member[i] = rhs.member[i]; return *this; }
// determine whether elt is in the set template <classT> int Set<T>::IsMember(const T& elt) { // is int(elt) in range 0 to setrange-1 ? if (int(elt) < 0 || int(elt) >= setrange) Error(InvalidMemberRef); // return the bit corresponding to elt return member[ArrayIndex(elt)] & BitMask(elt); }
template <classT> int Set<T>::operator= = (const Set<T>& x) const { int retval = 1; // the sets must have the same range if (setrange != x.setrange) Error(SetsDifferentSize); for(int i=0;i < arraysize;i++) if (member[i] != x.member[i]) { retval = 0; break; } return retval; }
template <classT> Set<T> Set<T>::operator+ (const Set<T>& x) const { // the sets must have the same range if (setrange != x.setrange) Error(SetsDifferentSize); // form the union in tmp Set<T> tmp(setrange); // each array element of tmp is the bitwise // OR of the current object and x for (int i = 0; i < arraysize; i++) tmp.member[i] = member[i] | x.member[i]; // return the union return tmp; }
template <classT> Set<T> Set<T>::operator* (const Set<T>& x) const { // the sets must have the same range if (setrange != x.setrange) Error(SetsDifferentSize); // form the intersection in tmp Set<T> tmp(setrange); // each array element of tmp is the bitwise // AND of the current object and x for (int i = 0; i < arraysize; i++) tmp.member[i] = member[i] & x.member[i]; // return the intersection return tmp; }
// insert elt into the set template <classT> void Set<T>::Insert(const T& elt) { // is int(elt) in range 0 to setrange-1 ? if (int(elt) < 0 || int(elt) >= setrange) Error(InvalidMemberRef); // set bit corresponding to elt member[ArrayIndex(elt)] |= BitMask(elt); }
// delete elt from the set template <classT> void Set<T>::Delete(constT& elt) { // is int(elt) in range 0 to setrange-1 ? if (int(elt) < 0 || int(elt) >= setrange) Error(InvalidMemberRef); // clear the bit corresponding to elt. note // that ~BitMask(elt) has a 0 in the bit // we are interested in an 1 in all others member[ArrayIndex(elt)] &= ~BitMask(elt); }
template <classT> istream& operator>> (istream& istr, Set<T>& x) { char c; int haveComma = 0, needComma = 0; T elt; int i; for (i = 0; i < x.arraysize; i++) x.member[i] = 0; c = ' '; // skip leading white space while (c == ' ' || c == '\t' || c == '\n') if (istr.get(c) == 0) x.Error(EndOfFile); if (c != '{') x.Error(MissingLeftBrace); if (istr.get(c) == 0) x.Error(EndOfFile);
while (c != '}') { switch(c) { case' ': case'\t': case'\n': break; case '{': x.Error(ExpectRightBrace); break; case',': if (haveComma == 1) x.Error(MissingValue); else { haveComma = 1; needComma = 0; } break;
default: if (needComma) x.Error(MissingComma); istr.putback(c); if(istr >> elt == 0) x.Error(InvalidInputData); if (int(elt) < 0 || int(elt) >= x.setrange) x.Error(InvalidInputData); x.member[x.ArrayIndex(elt)] |= x.BitMask(elt); needComma = 1; haveComma = 0; break; }
if (istr.get(c) == 0) x.Error(EndOfFile); } if (haveComma == 1) x.Error(MissingValue); return istr; }
template <classT> ostream& operator<< (ostream& ostr, const Set<T>& x) { int i, j, setElt; int needComma = 0; T elt; ostr << "{"; for (setElt = 0; setElt < x.setrange; setElt++) { if (x.member[x.ArrayIndex(T(setElt))] & x.BitMask(T(setElt))) { elt = T(setElt); if (needComma == 1) ostr << ", " << elt; else { ostr << elt; needComma = 1; } } } ostr << "} "; return ostr; }
打印素数 #include <iostream.h> #include <iomanip.h> #pragma hdrstop #include "set.h" // use the Set class
void PrintPrimes(int n) { Set<int> S(n+1); int m, k, count; for(m=2; m <= n; m++) S.Insert(m); for( m=2;m*m <= n; m++) if( S.IsMember(m)) for( k=m+m; k<= n; k += m) if (S.IsMember(k)) S.Delete(k); count = 1; for(m=2;m <= n;m++) if (S.IsMember(m)) { cout << setw(3) << m << " "; if (count++ % 10 == 0) cout << endl;} cout << endl; }
voidmain(void) { int n; cout << "Enter n: "; cin >> n; cout << endl; PrintPrimes(n); }
二、等价类和并查集 一个等价关系把一个集合划分成互不相交的等价类 设 S={0,1,2,3,4,5,6,7,8,9,10,11} 0≡4,3≡1,6≡10,8≡9,7≡4,6≡8,3≡5,2≡11,11≡0 则S={0,2,4,7,11}∪{1,3,5}∪{6,8,9,10} 怎样用一个算法实现
用集合运算 先把S的每个元素都做成单点集 再把有关系的集合做并: 每读入一对等价元素a≡b 查找a,b所在的集合,如不同则做并。
用双亲表示法的树结构表示集合 双亲表示法树表示集合 树根同时作集合名 A D B C E F
不交集合的并只要连接两颗树 G A H I J D B C E F G H I J
双亲表示法结点定义 #define MAX_TREE_SIZE 100 template <class T> class PNode { T data; int Parent; public: PNode(T item, int pr); T GetData( ); int GetParent( ); } }
const int defaultsize=15; template <class T> class UFSet //并查集的类 { PNode<T> *nodes; int size; int Find(int i);///找结点i所在集合的根 public: UFset(int sz=defaultsize ); ~UFSet( ){delete[ ] nodes;} void Union(Titem1, Titem2); int Find(Titem);//找item所在集合的根 void WeightedUnion(Titem1, Titem2); int CollapsingFind(Titem); };
template <class T> int UFSet<T>::Find(int i) {if(i<0||i>=size)return -1; for(int j=i; nodes[j].Parent>=0;j=nodes[j].Parent) ; return j; } template <class T> UFSet<T>::UFSet( int sz) { nodes=new PNode<T>[sz]; size=sz; for(int i=0;i<size;i++) nodes[i].Parent=-1; }
template <class T> int UFSet<T>::Find(Titem) { int i=0; while(nodes[i].Getdata( )!=item)i++; return Find(i) ; } template <class T> void UFSet<T>:: Union( T item1, Titem2) { int root1, root2; if( root1=Find(item1)!=( root2=Find(item2)) nodes[root1].Parent=root2;}
Union 的性能分析 设 S={0,1,2,…,n}, 0≡1≡2≡…≡n-1≡n 作Union(0,1), Union(1,2), …,Union(n-1,n) 得到 n 每个元查找复杂度O(n) 总复杂度O(n2) n-1 … 2 1 0
改进Union为WeightUnion 改根结点的权值为集合中元素个数的 相反数-t 两树结合 元素少的树联到元素多的树的根 再将两树根的权相加(两树元素个数相加)
template <class T> void UFSet<T>::WeightedUnion(Titem1, Titem2) {int root1=Find(item1), root2=Find( item2); int temp= nodes[root1].Parent+nodes[root2].Parent; if(nodes[root2].Parent<nodes[root1].Parent) {nodes[root1].Parent=root2; nodes[root2].Parent =temp;} else{nodes[root2].Parent=root1; nodes[root1].Parent =temp;} }
WeightUnion 的性能分析 设 S={0,1,2,…,n}, 0≡1≡2≡…≡n-1≡n 作WeightedUnion(0,1), WeightedUnion(1,2), …,WeightedUnion(n-1,n) 变为 n 1 … 0 n-1 2 n-1 n … 2 每个元查找复杂度O(log2n) 总复杂度O(nlog2n) 1 0
压缩路径 template<class T> int UFSet<T>::CollapsingFind(int i) {int j=Find(i); while(i!=j) { int temp=nodes[i].Parent; nodes[i].Parent=j; i=temp; } return j; }
压缩路径UFSet<char> u;…………… u.CollapsingFind(H); A A D H D B C F B C E G E F G H