王曉東數據結構中集合一章,用位向量實現集合看的很懵記錄一下。
N是一個不大的固定整數時,{1,2...N}是N的子集 假如N=10000,可以用數組A[N]來表示這個集合的存在,此時數組大小為A[N],如A[1]=1表示集合中第一個元素存在。
位向量顧名思義就是用位來存儲元素。以書中unsigned short類型為例,下面用US表示。US占位2個字節,16位,那么一個US就可以表示16個數,那么這N個數只需要用N/16 個US類型數表示。此時A數組大小為A[N/16+1],
A[0]表示1-16個數(0-15)
A[1]表示2-32個數(16-31)
...
A[0]=13,可看成000000001011,A[0]的第一個位置、第二個位置、第四個位置的數存在,即0、1、3三個數存在。
假如輸入一個數30,首先先確定在數組哪個位置,30/16=1,此時應在數組A[1]里,計算在A[1]中的位置則只需要30%16+1=15,表示在第15個位置。
下面解析王曉東書中集合的實現方法:
集合的基本運算:
//並集運算:其運算結果為集合A和集合B的並集 Set SetUnion(Set A,Set B); //交集運算:其運算結果為集合A和集合B的交集 Set SetIntersection(Set A,Set B); //賦值運算:將集合B賦值給A void SetAssign(Set A,Set B); //判斷運算:相等返回1 否則返回0 int SetEqual(Set A,Set B); //成員運算:x與集合相同的類型,當x屬於S時,返回1 否則返回0 int SetMember(int x,Set set); //插入運算:將x與插入集合S,當x本身屬於S時,不改變集合 void SetInsert(int x,Set s); //刪除運算:將集合S中的x元素刪除,當x不屬於S時,不改變集合 void SetDelete(int x,Set s );
Set集合定義:
typedef struct set { int size;//能存儲元素規模 //數組大小應該看類型 如v為 ushort應該一個數可以存16個 大小為arr/16+1||(size+15) int arraysize; //向量組 每個向量能存儲16個 unsigned short *v; } Bitset, *Set;
SetInit函數用於創建新Set:
Set InitSet(int size) { Set _set = new Bitset; _set->size = size; //一個US類型可以表示16個 故數組大小為1+size/16 //書中這種向右移動4位的運算方式等同於/16 _set->arraysize = (size+15)>>4; //書中v大小為size 個人認為有錯,有不同意見的朋友歡迎探討 _set->v = new unsigned short[_set->arraysize]; for (int i = 0; i <_set->arraysize; i++)
{
_set->v[i] = 0;
}
return _set;
}
SetAssign函數用於賦值:
//把B賦值給A void SetAsign(Set A, Set B) { if (A->size != B->size) return; for (int i = 0; i < A->size; i++) { A->v [i] = B->v[i]; } }
ArrayIndex尋找數在數組中的位置:
//計算位置大小 除類型的大小 UShort為16 故要/16 按位的話/4即可 int ArrayIndex(int x){ return x>>4; }
BitMask函數尋找在US位置中第幾位:
//計算這個數在一個ushort里面所在的第幾個位置 除16取余+1即可 //輸入10二進制為1010為15 1111 &計算得1010表示10 之后右移 unsigned short BitMask(int x){ return 1<<(x&15); }
SetInsert插入函數:
//位插入運算 查找數組位置 找到x%16放置 用|運算 //如17插入,此時數組v[1]中為00100000,表示第七個位置有東西,17%16+1=2 |運算后v[1]為00100010 void SetInsert(int x,Set s){ if(x<0||s->size<x) return; s->v[ArrayIndex(x)]|=BitMask(x); }
SetDelete刪除函數:
//刪除元素運算 先取反再去取& //如刪除17,此時數組v[1]中為00100010,表示第2、7個位置有東西,17%16+1=2 ~2取反為11111101 &運算后v[1]為00100000 void SetDelete(int x,Set s ){ if(x<0||s->size<x) return; s->v[ArrayIndex(x)]&=~BitMask(x); }
SetMember函數查看元素是否存在:
//檢測元素是否存在 int SetMember(int x,Set set){ if(x<0||x>set->size) return 0; //首先查找數組對應位置,與x相對的位置 //如3 此時在數組第一個位置 BitMask值為1000 表示第4數,第1個為0 return set->v[ArrayIndex(x)]&BitMask(x); }
SetEqual函數判斷集合A和B是否相等:
int SetEqual(Set A,Set B){ if(A->size! B->size) return 0; for (int i = 0; i < A->arraysize; i++) { if(A->v[i]! B->v[i]) return 0; } return 1; }
SetUnion並集運算:
//取並集 如A=1100 B=0110 此時第2、3、4個為並集 Set SetUnion(Set A,Set B){ Set s=InitSet(A->size); for (int i = 0; i < s->arraysize; i++) { //按位| s->v[i]=A->v[i] B->v[i]; } }
SetIntersection交集運算:
//取交集 如A=1100 B=0100=>0100 此時第3個為交集 Set SetIntersection(Set A,Set B){ Set s=InitSet(A->size); for (int i = 0; i < s->arraysize; i++) { //按位& s->v[i]=A->v[i] B->v[i]; } }
SetDifference差集運算:
//取差集 如A=111000 B=001000=>取&為001000 再異或110000 此時第5.6個為差集 Set SetDifference(Set A,Set B){ Set s=InitSet(A->size); for (int i = 0; i < s->arraysize; i++) { //按位& 再^ s->v[i]=A->v[i]^(A->v[i]&B->v[i]); } }