Skip to content

Commit ca033c9

Browse files
committed
哈希集合
1 parent 7a3c740 commit ca033c9

File tree

4 files changed

+248
-3
lines changed

4 files changed

+248
-3
lines changed

Hash Set/README.markdown

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
# Hash Set
2+
# 哈希集合
23

34
A set is a collection of elements that is kind of like an array but with two important differences: the order of the elements in the set is unimportant and each element can appear only once.
5+
集合是元素的集合,有点像数组但有两个重要的区别:集合中元素的顺序不重要,每个元素只能出现一次。
46

57
If the following were arrays, they'd all be different. However, they all represent the same set:
8+
如果以下是数组,它们都会有所不同。 但是,它们都代表相同的集合:
69

710
```swift
811
[1 ,2, 3]
@@ -12,8 +15,10 @@ If the following were arrays, they'd all be different. However, they all represe
1215
```
1316

1417
Because each element can appear only once, it doesn't matter how often you write the element down -- only one of them counts.
18+
因为每个元素只能出现一次,所以将元素写下来的频率并不重要 - 只有其中一个元素有效。
1519

1620
> **Note:** I often prefer to use sets over arrays when I have a collection of objects but don't care what order they are in. Using a set communicates to the programmer that the order of the elements is unimportant. If you're using an array, then you can't assume the same thing.
21+
> **注意:**当我有一组对象但不关心它们的顺序时,我经常更喜欢使用数组上的集合。使用集合与程序员通信,元素的顺序并不重要。 如果你正在使用数组,那么你不能假设同样的事情。
1722
1823
Typical operations on a set are:
1924

@@ -24,17 +29,31 @@ Typical operations on a set are:
2429
- take the intersection with another set
2530
- calculate the difference with another set
2631

32+
一组的典型操作是:
33+
34+
- 插入元素
35+
- 删除元素
36+
- 检查集合是否包含元素
37+
- 与另一组结合
38+
- 与另一组交叉
39+
- 计算与另一组的差异
40+
2741
Union, intersection, and difference are ways to combine two sets into a single one:
42+
联合,交集和差异是将两个集合组合成一个集合的方法:
2843

2944
![Union, intersection, difference](Images/CombineSets.png)
3045

3146
As of Swift 1.2, the standard library includes a built-in `Set` type but here I'll show how you can make your own. You wouldn't use this in production code, but it's instructive to see how sets are implemented.
47+
从Swift 1.2开始,标准库包含一个内置的`Set`类型,但在这里我将展示如何制作自己的类型。 您不会在生产代码中使用它,但了解如何实现集合是有益的。
3248

3349
It's possible to implement a set using a simple array but that's not the most efficient way. Instead, we'll use a dictionary. Since `Swift`'s dictionary is built using a hash table, our own set will be a hash set.
50+
使用简单数组实现集合是可能的,但这不是最有效的方法。 相反,我们将使用字典。 由于`Swift`的字典是使用哈希表构建的,因此我们自己的集合将是一个哈希集。
3451

3552
## The code
53+
## 代码
3654

3755
Here are the beginnings of `HashSet` in Swift:
56+
以下是Swift中`HashSet`的开头:
3857

3958
```swift
4059
public struct HashSet<T: Hashable> {
@@ -71,12 +90,16 @@ public struct HashSet<T: Hashable> {
7190
```
7291

7392
The code is really very simple because we rely on Swift's built-in `Dictionary` to do all the hard work. The reason we use a dictionary is that dictionary keys must be unique, just like the elements from a set. In addition, a dictionary has **O(1)** time complexity for most of its operations, making this set implementation very fast.
93+
代码非常简单,因为我们依靠Swift的内置`Dictionary`来完成所有的艰苦工作。 我们使用字典的原因是字典键必须是唯一的,就像集合中的元素一样。 此外,字典在其大多数操作中具有**O(1)**时间复杂度,使得该集合实现非常快。
7494

7595
Because we're using a dictionary, the generic type `T` must conform to `Hashable`. You can put any type of object into our set, as long as it can be hashed. (This is true for Swift's own `Set` too.)
96+
因为我们使用的是字典,所以通用类型`T`必须符合`Hashable`。 您可以将任何类型的对象放入我们的集合中,只要它可以进行哈希处理即可。 (对于Swift自己的`Set`也是如此。)
7697

7798
Normally, you use a dictionary to associate keys with values, but for a set we only care about the keys. That's why we use `Bool` as the dictionary's value type, even though we only ever set it to `true`, never to `false`. (We could have picked anything here but booleans take up the least space.)
99+
通常,您使用字典将键与值关联,但对于一个集合,我们只关心键。 这就是为什么我们使用`Bool`作为字典的值类型,即使我们只将它设置为`true`,而不是`false`。 (我们本可以选择任何东西,但布尔占用的空间最小。)
78100

79101
Copy the code to a playground and add some tests:
102+
将代码复制到 playground 并添加一些测试:
80103

81104
```swift
82105
var set = HashSet<String>()
@@ -95,13 +118,17 @@ set.contains("one") // false
95118
```
96119

97120
The `allElements()` function converts the contents of the set into an array. Note that the order of the elements in that array can be different than the order in which you added the items. As I said, a set doesn't care about the order of the elements (and neither does a dictionary).
121+
`allElements()`函数将集合的内容转换为数组。 请注意,该数组中元素的顺序可能与添加项目的顺序不同。 正如我所说,一个集合并不关心元素的顺序(也不是字典)。
98122

99123

100124
## Combining sets
125+
## 组合集
101126

102127
A lot of the usefulness of sets is in how you can combine them. (If you've ever used a vector drawing program like Sketch or Illustrator, you'll have seen the Union, Subtract, Intersect options to combine shapes. Same thing.)
128+
集合的很多用处在于如何组合它们。(如果你曾经使用像Sketch或Illustrator这样的矢量绘图程序,你会看到Union,Subtract,Intersect选项来组合形状。同样的事情。)
103129

104130
Here is the code for the union operation:
131+
这是union操作的代码:
105132

106133
```swift
107134
extension HashSet {
@@ -119,6 +146,7 @@ extension HashSet {
119146
```
120147

121148
The *union* of two sets creates a new set that consists of all the elements in set A plus all the elements in set B. Of course, if there are duplicate elements they count only once.
149+
两个集合的 *union* 创建一个新集合,它由集合A中的所有元素加上集合B中的所有元素组成。当然,如果存在重复元素,它们只计算一次。
122150

123151
Example:
124152

@@ -140,8 +168,10 @@ union.allElements() // [5, 6, 2, 3, 1, 4]
140168
```
141169

142170
As you can see, the union of the two sets contains all of the elements now. The values `3` and `4` still appear only once, even though they were in both sets.
171+
如您所见,两个集合的并集现在包含所有元素。 值`3``4`仍然只出现一次,即使它们都在两组中。
143172

144173
The *intersection* of two sets contains only the elements that they have in common. Here is the code:
174+
两个集合的*交集*仅包含它们共有的元素。 这是代码:
145175

146176
```swift
147177
extension HashSet {
@@ -165,8 +195,10 @@ intersection.allElements()
165195
```
166196

167197
This prints `[3, 4]` because those are the only objects from set A that are also in set B.
198+
这打印 `[3, 4]` 因为那些是集合A中也是集合B的唯一对象。
168199

169200
Finally, the *difference* between two sets removes the elements they have in common. The code is as follows:
201+
最后,两组之间的*差异*删除了它们共有的元素。 代码如下:
170202

171203
```swift
172204
extension HashSet {
@@ -183,6 +215,7 @@ extension HashSet {
183215
```
184216

185217
It's really the opposite of `intersect()`. Try it out:
218+
它实际上与`intersect()`相反。 试试看:
186219

187220
```swift
188221
let difference1 = setA.difference(setB)
@@ -193,13 +226,20 @@ difference2.allElements() // [5, 6]
193226
```
194227

195228
## Where to go from here?
229+
## 从这往哪儿走?
196230

197231
If you look at the [documentation](http://swiftdoc.org/v2.1/type/Set/) for Swift's own `Set`, you'll notice it has tons more functionality. An obvious extension would be to make `HashSet` conform to `SequenceType` so that you can iterate it with a `for`...`in` loop.
232+
如果你看一下Swift自己的`Set`[文档](http://swiftdoc.org/v2.1/type/Set/),你会发现它有更多的功能。 一个明显的扩展是使`HashSet`符合`SequenceType`,这样你就可以用`for` ...`in`循环迭代它。
198233

199234
Another thing you could do is replace the `Dictionary` with an actual [hash table](../Hash%20Table), but one that just stores the keys and doesn't associate them with anything. So you wouldn't need the `Bool` values anymore.
235+
您可以做的另一件事是将`Dictionary`替换为实际的[哈希表](../Hash%20Table),但是只存储键并且不将它们与任何东西相关联。 所以你不再需要`Bool`值了。
200236

201237
If you often need to look up whether an element belongs to a set and perform unions, then the [union-find](../Union-Find/) data structure may be more suitable. It uses a tree structure instead of a dictionary to make the find and union operations very efficient.
238+
如果您经常需要查找元素是否属于集合并执行联合,那么[union-find](../Union-Find/)数据结构可能更合适。它使用树结构而不是字典来使查找和联合操作非常有效。
202239

203240
> **Note:** I'd like to make `HashSet` conform to `ArrayLiteralConvertible` so you can write `let setA: HashSet<Int> = [1, 2, 3, 4]` but currently this crashes the compiler.
241+
> **注意:**我想让`HashSet`符合`ArrayLiteralConvertible`,这样你就可以编写`let setA: HashSet<Int> = [1, 2, 3, 4]`但是目前这会使编译器崩溃。
204242
205-
*Written for Swift Algorithm Club by Matthijs Hollemans*
243+
*Written for Swift Algorithm Club by Matthijs Hollemans*
244+
*作者:Matthijs Hollemans*
245+
*翻译:[Andy Ron](https://github.com/andyRon)*

Hash Set/README_en.markdown

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
# Hash Set
2+
3+
A set is a collection of elements that is kind of like an array but with two important differences: the order of the elements in the set is unimportant and each element can appear only once.
4+
5+
If the following were arrays, they'd all be different. However, they all represent the same set:
6+
7+
```swift
8+
[1 ,2, 3]
9+
[2, 1, 3]
10+
[3, 2, 1]
11+
[1, 2, 2, 3, 1]
12+
```
13+
14+
Because each element can appear only once, it doesn't matter how often you write the element down -- only one of them counts.
15+
16+
> **Note:** I often prefer to use sets over arrays when I have a collection of objects but don't care what order they are in. Using a set communicates to the programmer that the order of the elements is unimportant. If you're using an array, then you can't assume the same thing.
17+
18+
Typical operations on a set are:
19+
20+
- insert an element
21+
- remove an element
22+
- check whether the set contains an element
23+
- take the union with another set
24+
- take the intersection with another set
25+
- calculate the difference with another set
26+
27+
Union, intersection, and difference are ways to combine two sets into a single one:
28+
29+
![Union, intersection, difference](Images/CombineSets.png)
30+
31+
As of Swift 1.2, the standard library includes a built-in `Set` type but here I'll show how you can make your own. You wouldn't use this in production code, but it's instructive to see how sets are implemented.
32+
33+
It's possible to implement a set using a simple array but that's not the most efficient way. Instead, we'll use a dictionary. Since `Swift`'s dictionary is built using a hash table, our own set will be a hash set.
34+
35+
## The code
36+
37+
Here are the beginnings of `HashSet` in Swift:
38+
39+
```swift
40+
public struct HashSet<T: Hashable> {
41+
fileprivate var dictionary = Dictionary<T, Bool>()
42+
43+
public init() {
44+
45+
}
46+
47+
public mutating func insert(_ element: T) {
48+
dictionary[element] = true
49+
}
50+
51+
public mutating func remove(_ element: T) {
52+
dictionary[element] = nil
53+
}
54+
55+
public func contains(_ element: T) -> Bool {
56+
return dictionary[element] != nil
57+
}
58+
59+
public func allElements() -> [T] {
60+
return Array(dictionary.keys)
61+
}
62+
63+
public var count: Int {
64+
return dictionary.count
65+
}
66+
67+
public var isEmpty: Bool {
68+
return dictionary.isEmpty
69+
}
70+
}
71+
```
72+
73+
The code is really very simple because we rely on Swift's built-in `Dictionary` to do all the hard work. The reason we use a dictionary is that dictionary keys must be unique, just like the elements from a set. In addition, a dictionary has **O(1)** time complexity for most of its operations, making this set implementation very fast.
74+
75+
Because we're using a dictionary, the generic type `T` must conform to `Hashable`. You can put any type of object into our set, as long as it can be hashed. (This is true for Swift's own `Set` too.)
76+
77+
Normally, you use a dictionary to associate keys with values, but for a set we only care about the keys. That's why we use `Bool` as the dictionary's value type, even though we only ever set it to `true`, never to `false`. (We could have picked anything here but booleans take up the least space.)
78+
79+
Copy the code to a playground and add some tests:
80+
81+
```swift
82+
var set = HashSet<String>()
83+
84+
set.insert("one")
85+
set.insert("two")
86+
set.insert("three")
87+
set.allElements() // ["one, "three", "two"]
88+
89+
set.insert("two")
90+
set.allElements() // still ["one, "three", "two"]
91+
92+
set.contains("one") // true
93+
set.remove("one")
94+
set.contains("one") // false
95+
```
96+
97+
The `allElements()` function converts the contents of the set into an array. Note that the order of the elements in that array can be different than the order in which you added the items. As I said, a set doesn't care about the order of the elements (and neither does a dictionary).
98+
99+
100+
## Combining sets
101+
102+
A lot of the usefulness of sets is in how you can combine them. (If you've ever used a vector drawing program like Sketch or Illustrator, you'll have seen the Union, Subtract, Intersect options to combine shapes. Same thing.)
103+
104+
Here is the code for the union operation:
105+
106+
```swift
107+
extension HashSet {
108+
public func union(_ otherSet: HashSet<T>) -> HashSet<T> {
109+
var combined = HashSet<T>()
110+
for obj in self.dictionary.keys {
111+
combined.insert(obj)
112+
}
113+
for obj in otherSet.dictionary.keys {
114+
combined.insert(obj)
115+
}
116+
return combined
117+
}
118+
}
119+
```
120+
121+
The *union* of two sets creates a new set that consists of all the elements in set A plus all the elements in set B. Of course, if there are duplicate elements they count only once.
122+
123+
Example:
124+
125+
```swift
126+
var setA = HashSet<Int>()
127+
setA.insert(1)
128+
setA.insert(2)
129+
setA.insert(3)
130+
setA.insert(4)
131+
132+
var setB = HashSet<Int>()
133+
setB.insert(3)
134+
setB.insert(4)
135+
setB.insert(5)
136+
setB.insert(6)
137+
138+
let union = setA.union(setB)
139+
union.allElements() // [5, 6, 2, 3, 1, 4]
140+
```
141+
142+
As you can see, the union of the two sets contains all of the elements now. The values `3` and `4` still appear only once, even though they were in both sets.
143+
144+
The *intersection* of two sets contains only the elements that they have in common. Here is the code:
145+
146+
```swift
147+
extension HashSet {
148+
public func intersect(_ otherSet: HashSet<T>) -> HashSet<T> {
149+
var common = HashSet<T>()
150+
for obj in dictionary.keys {
151+
if otherSet.contains(obj) {
152+
common.insert(obj)
153+
}
154+
}
155+
return common
156+
}
157+
}
158+
```
159+
160+
To test it:
161+
162+
```swift
163+
let intersection = setA.intersect(setB)
164+
intersection.allElements()
165+
```
166+
167+
This prints `[3, 4]` because those are the only objects from set A that are also in set B.
168+
169+
Finally, the *difference* between two sets removes the elements they have in common. The code is as follows:
170+
171+
```swift
172+
extension HashSet {
173+
public func difference(_ otherSet: HashSet<T>) -> HashSet<T> {
174+
var diff = HashSet<T>()
175+
for obj in dictionary.keys {
176+
if !otherSet.contains(obj) {
177+
diff.insert(obj)
178+
}
179+
}
180+
return diff
181+
}
182+
}
183+
```
184+
185+
It's really the opposite of `intersect()`. Try it out:
186+
187+
```swift
188+
let difference1 = setA.difference(setB)
189+
difference1.allElements() // [2, 1]
190+
191+
let difference2 = setB.difference(setA)
192+
difference2.allElements() // [5, 6]
193+
```
194+
195+
## Where to go from here?
196+
197+
If you look at the [documentation](http://swiftdoc.org/v2.1/type/Set/) for Swift's own `Set`, you'll notice it has tons more functionality. An obvious extension would be to make `HashSet` conform to `SequenceType` so that you can iterate it with a `for`...`in` loop.
198+
199+
Another thing you could do is replace the `Dictionary` with an actual [hash table](../Hash%20Table), but one that just stores the keys and doesn't associate them with anything. So you wouldn't need the `Bool` values anymore.
200+
201+
If you often need to look up whether an element belongs to a set and perform unions, then the [union-find](../Union-Find/) data structure may be more suitable. It uses a tree structure instead of a dictionary to make the find and union operations very efficient.
202+
203+
> **Note:** I'd like to make `HashSet` conform to `ArrayLiteralConvertible` so you can write `let setA: HashSet<Int> = [1, 2, 3, 4]` but currently this crashes the compiler.
204+
205+
*Written for Swift Algorithm Club by Matthijs Hollemans*

README.markdown

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@
192192
### 集合
193193

194194
- [布隆过滤器](Bloom%20Filter/)-一个常量内存数据结构,用于概率性的检测某个元素是否在集合中。
195-
- [哈希集合](Hash%20Set/)-使用哈希表实现的集合。
195+
- [哈希集合](Hash%20Set/)-使用哈希表实现的集合。
196196
- [⏳多重集](Multiset/). A set where the number of times an element is added matters. (Also known as a bag.)
197197
- [⏳有序集](Ordered%20Set/)-很看重元素顺序的集合。
198198

Schedule.markdown

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@
157157
| 题目 | 翻译人员 | 翻译是否完成 | 校对人员 | 校对是否完成|
158158
|---------------------------|------------|:-------------:|----------|:---------:|
159159
| Bloom Filter | andyRon | Y | | |
160-
| Hash Set | | | | |
160+
| Hash Set | andyRon | Y | | |
161161
| Multiset | | | | |
162162
| Ordered Set | | | | |
163163

0 commit comments

Comments
 (0)