十三.集合
集合的定义
集合成员是无序的、并且都是只出现一次。
空集合:是不包含任何成员的集合。
全域:是所有可能成员的集合。
集合的操作
联合:两个集合的并集
交叉:两个集合的交集
差异:存在于第一个集合,但不存在于第二个集合,也就是相对补集,A相对B的补集。
集合的属性(性质)
集合与空集合的交叉是空集合。集合与空集合的联合是集合本身。
集合与自身的交叉是自身,与自身的联合还是联合。
联合、交叉满足交换律、结合律、分配律。
吸收定律:
集合A联合另一集合B再交叉自身集合A,结果还是集合A。
德摩根定律:
集合A差异(集合B交叉集合C)等于(集合A差异集合B)联合(集合A差异集合B)。
集合A差异(集合B联合集合C)等于(集合A差异集合B) 交叉 (集合A差异集合B)。
用散列表实现一个Set集合类
用散列表可以存储任何类型的数据项。
内部实现
HashSet类:
public class HashCSet
{
private Hashtabledata;
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 (inti = 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 { returndata.Count; }
}
///<summary>
///联合
///</summary>
///<param name="aSet"></param>
///<returns></returns>
public HashCSetUnion(HashCSet aSet)
{
HashCSet tempSet = new HashCSet();
foreach (ObjecthashObjectin data.Keys)
tempSet.Add(this.data[hashObject]);
foreach (ObjecthashObjectin aSet.data.Keys)
if (!(this.data.ContainsKey(hashObject)))
tempSet.Add(aSet.data[hashObject]);
return tempSet;
}
///<summary>
///返回一个和指定集合交叉的集合
///</summary>
///<param name="aSet"></param>
///<returns></returns>
public HashCSetIntersection(HashCSet aSet)
{
HashCSet tempSet = new HashCSet();
foreach (ObjecthashObjectin 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 (Objectkeyin this.data.Keys)
if(!(aSet.data.Contains(key)))
return false;
return true;
}
///<summary>
///差异。返回一个和指定集合的差异集合
///</summary>
///<param name="aSet"></param>
///<returns></returns>
public HashCSetDifference(HashCSet aSet)
{
HashCSet tempSet = new HashCSet();
foreach (ObjecthashObjectin data.Keys)
if (!(aSet.data.Contains(hashObject)))
tempSet.Add(data[hashObject]);
return tempSet;
}
public override string ToString()
{
string s="";
foreach (Objectkeyin data.Keys)
s += data[key] + " ";
return s;
}
}
使用实现
调用:
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
用BitArray实现一个Set集合类
当集合存储的数据是整数时,使用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补集的交集为空)
内部实现
BitArraySet类:
public class BitArraySet
{
private BitArraydata;
public BitArraySet()
{
data = new BitArray(5);
}
public void Add(int item)
{
data[item] = true;
}
public bool IsMember(int item)
{
returndata[item];
}
public void Remove(int item)
{
data[item] = false;
}
public BitArraySetUnion(BitArraySet aSet)
{
BitArraySet tempSet =new BitArraySet();
for (inti = 0; i <= data.Count - 1; i++)
tempSet.data[i] = (this.data[i] ||
aSet.data[i]);
return tempSet;
}
public BitArraySetIntersection(BitArraySet aSet)
{
BitArraySet tempSet =new BitArraySet();
for (inti = 0; i <= data.Count - 1; i++)
tempSet.data[i] = (this.data[i]&&
aSet.data[i]);
return tempSet;
}
public BitArraySetDifference(BitArraySet aSet)
{
BitArraySet tempSet =new BitArraySet();
for (inti = 0; i <= data.Count - 1; i++)
tempSet.data[i] = (this.data[i]&&
(!(aSet.data[i])));
return tempSet;
}
public bool IsSubset(BitArraySetaSet)
{
BitArraySet tempSet =new BitArraySet();
for (inti = 0; i <= data.Count - 1; i++)
if (this.data[i]&& (!(aSet.data[i])))
returnfalse;
return true;
}
public override string ToString()
{
string s = "";
for (inti = 0; i <= data.Count - 1; i++)
if (data[i])
s += i+" ";
return s;
}
}
使用实现
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.Union(BsetB);
Console.WriteLine();
Console.WriteLine("A:" + BsetA.ToString());
Console.WriteLine("B:" + BsetB.ToString());
Console.WriteLine("A Union B:" + BsetC.ToString());
BsetC = BsetA.Intersection(BsetB);
Console.WriteLine("A Intersection B:" + BsetC.ToString());
BsetC = BsetA.Difference(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
HashSet<T>类和SortedSet<T>类
HashSet<T>类是.Net Framework3.5中新增。表示值的集合。SortedSet<T>类是.Net Framework4.0中新增,表示按排序顺序保持的对象的集合。两者都实现了ISet<T>接口。
ISet<T>接口提供的方法可以创建合集、交集,或者给出一个集是另一个集的超集或子集的信息。
namespace System.Collections.Generic
public class HashSet<T> : ISerializable,IDeserializationCallback,ISet<T>, ICollection<T>,IEnumerable<T>,IEnumerable
public class SortedSet<T> : ISet<T>,ICollection<T>, IEnumerable<T>, ICollection,IEnumerable,ISerializable,IDeserializationCallback
HashSet<T>类
构造器
HashSet<T>()
初始化HashSet<T> 类的一个新实例,该实例为空并使用集类型的默认相等比较器。
HashSet<T>(IEnumerable<T>)
初始化HashSet<T> 类的一个新实例,该实例使用集类型的默认相等比较器,包含从指定的集合复制的元素,并且有足够的容量容纳所复制的这些元素。
HashSet<T>(IEqualityComparer<T>)
初始化HashSet<T> 类的一个新实例,该实例为空并使用集类型的指定相等比较器。
HashSet<T>(IEnumerable<T>,IEqualityComparer<T>)
初始化HashSet<T> 类的一个新实例,该实例使用集类型的指定相等比较器,包含从指定的集合复制的元素,并且有足够的容量容纳所复制的这些元素。
HashSet<T>(SerializationInfo,StreamingContext)
用序列化数据初始化 HashSet<T> 类的新实例。
主要属性和方法
S1是S2的超集,则S2是S1的真子集
Count属性:获取集中包含的元素数。
Add:将指定的元素添加到集中。
Clear:从HashSet<T> 对象中移除所有元素。
Contains:确定HashSet<T> 对象是否包含指定的元素。
CopyTo(T[]):将HashSet<T> 对象的元素复制到数组中。
CopyTo(T[], Int32):从指定数组索引处开始,将 HashSet<T> 对象的元素复制到数组中。
CopyTo(T[], Int32, Int32):从指定数组索引处开始,将 HashSet<T> 对象的指定数目的元素复制到数组中。
CreateSetComparer:返回IEqualityComparer 对象,该对象可用于对HashSet<T> 对象进行相等测试。
ExceptWith:从当前HashSet<T> 对象中移除指定集合中的所有元素。
IntersectWith:修改当前的HashSet<T> 对象,以仅包含该对象和指定集合中存在的元素。
IsProperSubsetOf:确定HashSet<T> 对象是否为指定集合的真子集。
IsProperSupersetOf:确定HashSet<T> 对象是否为指定集合的真超集。
IsSubsetOf:确定HashSet<T> 对象是否为指定集合的子集。
IsSupersetOf:确定HashSet<T> 对象是否为指定集合的超集。
Remove:从HashSet<T> 对象中移除指定的元素。
RemoveWhere:从HashSet<T> 集合中移除与指定的谓词所定义的条件相匹配的所有元素。
SetEquals:确定HashSet<T> 对象与指定的集合中是否包含相同的元素。
SymmetricExceptWith:修改当前的HashSet<T> 对象,以仅包含该对象或指定集合中存在的元素(但不可包含同时在该对象和指定集合中都存在的元素)。
TrimExcess:将HashSet<T> 对象的容量设置为它所包含的元素的实际个数,向上舍入为接近的特定于实现的值。
UnionWith:修改当前的 HashSet<T> 对象,以包含该对象本身、指定集合中存在的所有元素或两者。
实例
HashSet<int>evenNumbers =new HashSet<int>();
HashSet<int>oddNumbers =new HashSet<int>();
for (int i = 0; i< 5; i++)
{
evenNumbers.Add(i* 2);
oddNumbers.Add((i * 2) + 1);
}
Console.Write("evenNumberscontains {0} elements: ", evenNumbers.Count);
DisplaySet(evenNumbers);
///evenNumbers contains 5elements: { 0 2 4 6 8 }
Console.Write("oddNumberscontains {0} elements: ", oddNumbers.Count);
DisplaySet(oddNumbers);
///oddNumbers contains 5elements: { 1 3 5 7 9 }
HashSet<int>numbers =new HashSet<int>(evenNumbers);
numbers.UnionWith(oddNumbers);//1.联合,并集
Console.WriteLine("numbers(evenNumbers)UnionWith oddNumbers...change numbers");
Console.Write("numberscontains {0} elements: ", numbers.Count);
DisplaySet(numbers);
///numbers(evenNumbers)UnionWith oddNumbers...change numbers
///numbers contains 10elements: { 0 2 4 6 8 1 3 5 7 9 }
HashSet<int>numbers0 =new HashSet<int>(numbers);
numbers0.Remove(5);
numbers0.Remove(6);
Console.Write("numbers0contains {0} elements: ", numbers0.Count);
DisplaySet(numbers0);
///numbers0 contains 8elements: { 0 2 4 8 1 3 7 9 }
HashSet<int>numbers1 =new HashSet<int>(numbers0);
numbers1.ExceptWith(oddNumbers);//2.移除,差异,相对另外一个集合的补集
Console.WriteLine("numbers1(numbers0)ExceptWith oddNumbers...change numbers1");
Console.Write("numbers1contains {0} elements: ", numbers1.Count);
DisplaySet(numbers1);
///numbers1(numbers0)ExceptWith oddNumbers...change numbers1
///numbers1 contains 4elements: { 0 2 4 8 }
HashSet<int>numbers2 =new HashSet<int>(numbers); ;
numbers2.IntersectWith(oddNumbers);//3.交叉,交集
Console.WriteLine("numbers2(numbers)IntersectWith oddNumbers...return numbers2");
Console.Write("numbers2contains {0} elements: ", numbers2.Count);
DisplaySet(numbers2);
///numbers2(numbers)IntersectWith oddNumbers...change numbers2
///numbers2 contains 5elements: { 1 3 5 7 9 }
SortedSet<T>类
构造器
SortedSet<T>()
初始化SortedSet<T> 类的新实例。
SortedSet<T>(IComparer<T>)
初始化使用指定比较器的 SortedSet<T> 类的新实例。
SortedSet<T>(IEnumerable<T>)
初始化SortedSet<T> 类的新实例,该实例包含从指定的可枚举集合中复制的元素。
SortedSet<T>(IEnumerable<T>,IComparer<T>)
初始化SortedSet<T> 类的新实例,该实例包含从指定的可枚举集合中复制的元素并使用指定的比较器。
SortedSet<T>(SerializationInfo,StreamingContext)
初始化包含序列化数据的 SortedSet<T> 类的新实例。
主要属性和方法
Count: 获取 SortedSet<T> 中元素的数目。
Max:按照比较器的定义,获取SortedSet<T> 中的最大值。
Min:按照比较器的定义,获取SortedSet<T> 中的最小值。
Add:向集内添加元素,并返回一个指示是否已成功添加元素的值。
Clear:从集内移除所有元素。
Contains:确定集是否包含特定元素。
CopyTo(T[]):将整个SortedSet<T> 复制到兼容的一维数组中(从目标数组的开头开始复制)。
CopyTo(T[],Int32):将整个 SortedSet<T> 复制到兼容的一维目标数组(从指定的数组索引处开始复制)。
CopyTo(T[],Int32, Int32):将指定数量的元素从 SortedSet<T> 复制到兼容的一维数组中(从指定的数组索引处开始复制)。
ExceptWith:从当前SortedSet<T> 对象中移除指定集合中的所有元素。
IntersectWith:修改当前的SortedSet<T> 对象,使该对象仅包含指定集合中也存在的元素。
IsProperSubsetOf:确定SortedSet<T> 对象是否为指定集合的真子集。
IsProperSupersetOf:确定SortedSet<T> 对象是否为指定集合的真超集。
IsSubsetOf:确定SortedSet<T> 对象是否为指定集合的子集。
IsSupersetOf:确定SortedSet<T> 对象是否为指定集合的超集。
Remove:从SortedSet<T> 中移除指定的项。
RemoveWhere:从SortedSet<T> 中移除与指定的谓词所定义的条件相匹配的所有元素。
Reverse:返回一个IEnumerable<T>,它按相反的顺序循环访问 SortedSet<T>。
SetEquals:确定当前的SortedSet<T> 对象与指定的集合中是否包含相同的元素。
SymmetricExceptWith:修改当前的SortedSet<T> 对象,使该对象仅包含当前对象或指定集合中存在的元素(但不可包含两者共有的元素)。
UnionWith:修改当前SortedSet<T> 对象,使其包含存在于当前对象或指定集合的所有元素。
实例
SortedSet<int>ssi =new System.Collections.Generic.SortedSet<int>();
ssi.Add(3);
ssi.Add(1);
ssi.Add(42);
ssi.Add(2);
DisplaySSet<int>(ssi);
///{ 1 2 3 42 }
SortedSet<string>sss =new System.Collections.Generic.SortedSet<string>();
sss.Add("Mike");
sss.Add("John");
sss.Add("Ben");
sss.Add("Terry");
sss.Add("null");
DisplaySSet<string>(sss);
///{ Ben John Mike nullTerry }