理论:
集合的定义
集合成员是无序的、并且都是只出现一次。
空集合:是不包含任何成员的集合。
全域:是所有可能成员的集合。
集合的操作
联合:两个集合的并集
交叉:两个集合的交集
差异:存在于第一个集合,但不存在于第二个集合,也就是相对补集,A相对B的补集。
集合的属性(性质)
集合与空集合的交叉是空集合。集合与空集合的联合是集合本身。
集合与自身的交叉是自身,与自身的联合还是联合。
联合、交叉满足交换律、结合律、分配律。
吸收定律:
集合A联合另一集合B再交叉自身集合A,结果还是集合A。
德摩根定律:
集合A差异(集合B交叉集合C)等于(集合A差异集合B)联合(集合A差异集合B)。
集合A差异(集合B联合集合C)等于(集合A差异集合B) 交叉 (集合A差异集合B)。
实例1:
用散列表实现一个Set集合类
用散列表可以存储任何类型的数据项。
散列表/哈希表: 它使用键来访问集合中的元素。
当您使用键访问元素时,则使用哈希表,而且您可以识别一个有用的键值。哈希表中的每一项都有一个键/值对。键用于访问集合中的项目。
内部实现
using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace HashSet { public class HashCSet { private Hashtable data; public HashCSet() { data = new Hashtable(); } ///<summary> ///添加一个数据项。Hashtable的值为item,键为item根据内部散列函数计算的散列值。 ///</summary> ///<param name="item"></param> public void Add(Object item) { if (!data.ContainsValue(item)) { data.Add(item, item); } } ///<summary> ///散列函数,数据项字符的ASCII码值来计算散列值 ///</summary> ///<param name="item"></param> ///<returns></returns> private string Hash(object item) { char[] chars; string s = item.ToString(); int hashvalue = 0; chars = s.ToCharArray(); for (int i = 0; i <= chars.GetUpperBound(0); i++) { hashvalue += (int)chars[i]; } return hashvalue.ToString(); } public void Remove(object item) { data.Remove(Hash(item)); } public int Size { get { return data.Count; } } ///<summary> ///联合 ///</summary> ///<param name="aSet"></param> ///<returns></returns> public HashCSet Union(HashCSet aSet) { HashCSet tempSet = new HashCSet(); foreach (Object hashObject in data.Keys) tempSet.Add(this.data[hashObject]); foreach (Object hashObject in aSet.data.Keys) if (!(this.data.ContainsKey(hashObject))) tempSet.Add(aSet.data[hashObject]); return tempSet; } ///<summary> ///返回一个和指定集合交叉的集合 ///</summary> ///<param name="aSet"></param> ///<returns></returns> public HashCSet Intersection(HashCSet aSet) { HashCSet tempSet = new HashCSet(); foreach (Object hashObject in data.Keys) { if (aSet.data.Contains(hashObject)) tempSet.Add(aSet.data[hashObject]); } return tempSet; } ///<summary> ///是否全部包含在一个集合内。判断当前集合是否是指定集合的一个子集 ///</summary> ///<param name="aSet"></param> ///<returns></returns> public bool Subset(HashCSet aSet) { if (this.Size > aSet.Size) return false; else foreach (Object key in this.data.Keys) if (!(aSet.data.Contains(key))) return false; return true; } ///<summary> ///差异。返回一个和指定集合的差异集合 ///</summary> ///<param name="aSet"></param> ///<returns></returns> public HashCSet Difference(HashCSet aSet) { HashCSet tempSet = new HashCSet(); foreach (Object hashObject in data.Keys) if (!(aSet.data.Contains(hashObject))) tempSet.Add(data[hashObject]); return tempSet; } public override string ToString() { string s = ""; foreach (Object key in data.Keys) s += data[key] + " "; return s; } } class Program { static void Main(string[] args) { //使用实现 //调用: HashCSet setA = new HashCSet(); HashCSet setB = new HashCSet(); //A setA.Add("milk"); setA.Add("eggs"); setA.Add("bacon"); setA.Add("cereal"); //B setB.Add("bacon"); setB.Add("eggs"); setB.Add("bread"); HashCSet setC = new HashCSet(); setC = setA.Union(setB); Console.WriteLine("A:" + setA.ToString()); Console.WriteLine("B:" + setB.ToString()); Console.WriteLine("A union B:" + setC.ToString()); setC = setA.Intersection(setB); Console.WriteLine("A intersect B: " + setC.ToString()); setC = setA.Difference(setB); Console.WriteLine("A diff B:" + setC.ToString()); setC = setB.Difference(setA); Console.WriteLine("B diff A:" + setC.ToString()); if (setB.Subset(setA)) Console.WriteLine("b is a subset of a"); else Console.WriteLine("b is not a subset of a"); ///A:eggs milkcereal bacon ///B:eggsbacon bread ///A unionB:eggs milk cereal bread bacon ///A intersectB: eggs bacon ///A diffB:cereal milk ///B diffA:bread ///b is not asubset of a Console.ReadLine(); } } }
实例2:
用BitArray实现一个Set集合类
BitArray : 它代表了一个使用值 1 和 0 来表示的二进制数组。
当您需要存储位,但是事先不知道位数时,则使用点阵列。您可以使用整型索引从点阵列集合中访问各项,索引从零开始。
当集合存储的数据是整数时,使用BitArray内部实现的Set类,能有更好的性能。
BitArray只存储布尔数值,并且集合的四个操作(联合、交叉、差异、求子集)都可以利用布尔运算符(And、Or和Not)来实现。这些实现比散列表要快许多。
策略
用BitArray来创建整数集合的存储策略如下:
1. 添加数据1到集合中,即为索引位置为1的数组元素为true。
2. 判断数据1是否在集合内,即判断索引位置为1的数组元素是否为true。
3. 两个集合的联合,即两个BitArray的对应索引位置的两个布尔值的”a||b”。(A与B的并集)
4. 两个集合的交叉,即两个BitArray的对应索引位置的两个布尔值的”a&&b”。(A与B的交集)
5. 两个集合的差异,即两个BitArray的对应索引位置的两个布尔值的”a && !b”。(A与B补集的交集)
6. 判断集合A是否是集合B的子集,即两个BitArray的对应索引位置的两个布尔值的”a && !b”有存在ture。(A与B补集的交集为空)
内部实现:
using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace BitArraySet { public class BitArraySet { private BitArray data; public BitArraySet() { data = new BitArray(5); } public void Add(int item) { data[item] = true; } public bool IsMember(int item) { return data[item]; } public void Remove(int item) { data[item] = false; } public BitArraySet SetUnion(BitArraySet aSet) { BitArraySet tempSet = new BitArraySet(); for (int i = 0; i <= data.Count - 1; i++) tempSet.data[i] = (this.data[i] ||aSet.data[i]); return tempSet; } public BitArraySet SetIntersection(BitArraySet aSet) { BitArraySet tempSet = new BitArraySet(); for (int i = 0; i <= data.Count - 1; i++) tempSet.data[i] = (this.data[i] &&aSet.data[i]); return tempSet; } public BitArraySet SetDifference(BitArraySet aSet) { BitArraySet tempSet = new BitArraySet(); for (int i = 0; i <= data.Count - 1; i++) tempSet.data[i] = (this.data[i] &&(!(aSet.data[i]))); return tempSet; } public bool IsSubset(BitArraySet aSet) { BitArraySet tempSet = new BitArraySet(); for (int i = 0; i <= data.Count - 1; i++) if (this.data[i] && (!(aSet.data[i]))) return false; return true; } public override string ToString() { string s = ""; for (int i = 0; i <= data.Count - 1; i++) if (data[i]) s += i + " "; return s; } } class Program { static void Main(string[] args) { //使用实现: //调用: BitArraySet BsetA = new BitArraySet(); BitArraySet BsetB = new BitArraySet(); ///A BsetA.Add(1); BsetA.Add(2); BsetA.Add(3); ///B BsetB.Add(2); BsetB.Add(3); BitArraySet BsetC = new BitArraySet(); BsetC = BsetA.SetUnion(BsetB); Console.WriteLine(); Console.WriteLine("A:" + BsetA.ToString()); Console.WriteLine("B:" + BsetB.ToString()); Console.WriteLine("A Union B:" + BsetC.ToString()); BsetC = BsetA.SetIntersection(BsetB); Console.WriteLine("A Intersection B:" + BsetC.ToString()); BsetC = BsetA.SetDifference(BsetB); Console.WriteLine("A Difference B:" + BsetC.ToString()); bool flag = BsetB.IsSubset(BsetA); if (flag) Console.WriteLine("b is a subset of a"); else Console.WriteLine("b is not a subset of a"); ////A:1 2 3 ////B:2 3 ////A Union B:1 2 3 ////A Intersection B:2 3 ////A Difference B:1 ////b is a subset of a Console.ReadLine(); } } }
参考:
https://msdn.microsoft.com/zh-cn/library/7y3x785f
http://blog.csdn.net/maths_bai/article/details/8046590#t16
http://blog.csdn.net/maths_bai/article/details/8046493#_Toc337377703
http://outofmemory.cn/csharp/tutorial/csharp-collection.html