Skip to content

Commit 20ed097

Browse files
author
318236213
committed
2 parents 599fc0a + 564a7d5 commit 20ed097

File tree

1 file changed

+162
-1
lines changed

1 file changed

+162
-1
lines changed

Ordered Set/README.md

Lines changed: 162 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,166 @@
11
# Ordered Set
2+
An Ordered Set is a collection of unique items in sorted order. Items are usually sorted from least to greatest. The Ordered Set data type is a representation of a [Set in Mathematics](https://en.wikipedia.org/wiki/Set_(mathematics)). It's important to keep in mind that two items can have the same *value* but still may not be equal.
3+
For example, we could define "a" and "z" to have the same value (their lengths), but clearly "a" != "z".
4+
5+
### Examples of Ordered Sets
6+
```
7+
[1, 2, 3, 6, 8, 10, 1000]
8+
Where each item (Integers) has it's normal definition of value and equality
9+
```
10+
```
11+
["a", "is", "set", "this"]
12+
Where each item (String) has it's value equal to it's length
13+
```
14+
15+
### These are not Ordered Sets
16+
```
17+
[1, 1, 2, 3, 5, 8]
18+
This Set violates the property of uniqueness
19+
```
20+
```
21+
[1, 11, 2, 3]
22+
This Set violates the sorted property
23+
```
24+
25+
## The Code
26+
We'll start by creating our internal representation for the Ordered Set. Since the idea of a set is similar to that of an array, we will use an array to represent our set. Furthermore, since we'll need to keep our set sorted, we need to compare the individual elemants. Thus, any type must conform to the [Comparable Protocol](https://developer.apple.com/library/watchos/documentation/Swift/Reference/Swift_Comparable_Protocol/index.html).
27+
28+
``` swift
29+
public struct OrderedSet<T: Comparable> {
30+
private var internalSet: [T]! = nil
31+
32+
// returns size of Set
33+
public var count: Int {
34+
return internalSet!.count
35+
}
36+
37+
public init(){
38+
internalSet = [T]() // create the internal array on init
39+
}
40+
...
41+
```
42+
43+
Lets take a look at the insert function first. The insert function first checks if the item already exists, and if so returns and does not insert the item. Otherwise, it will insert the item through straight forward iteration. It starts from the first item, and checks to see if this item is larger than the item we want to insert. Once we find such an item, we insert the given item into it's place, and shift the array over to the right by 1.
44+
45+
``` swift
46+
// inserts an item
47+
public mutating func insert(item: T){
48+
if exists(item) {
49+
return // don't add an item if it already exists
50+
}
51+
// if the set is initially empty, we need to simply append the item to internalSet
52+
if count == 0 {
53+
internalSet.append(item)
54+
return
55+
}
56+
57+
for i in 0..<count {
58+
if internalSet[i] > item {
59+
internalSet.insert(item, atIndex: i)
60+
return
61+
}
62+
}
63+
64+
// if an item is larger than any item in the current set, append it to the back.
65+
internalSet.append(item)
66+
}
67+
```
68+
The first part of the function checks if the item is already in the set.As we'll see later on, this has an efficiency of **O(log(n) + k)** where k is the number of items with the same value as the item we are inserting. The second part iterates through the interal array so that it can find a spot for our given item. This is at worse **O(n)**. The insert function for arrays has an efficiency of **O(log(n))**, thus making the insert function for our Ordered Set **O(log(n) + k)**.
69+
70+
71+
Next we have the `remove` function. First check if the item exists. If not, then return and no nothing. If it does exist, remove it.
72+
73+
``` swift
74+
// removes an item if it exists
75+
public mutating func remove(item: T) {
76+
if !exists(item) {
77+
return
78+
}
79+
80+
internalSet.removeAtIndex(findIndex(item))
81+
}
82+
```
83+
Again, because of the `exists` function, the efficiency for remove is **O(log(n) + k)**
84+
85+
The next function is the `findIndex` function which takes in an item of type `T` and returns the index of the item if it is in the set, otherwise returns -1.
86+
87+
``` swift
88+
// returns the index of an item if it exists, otherwise returns -1.
89+
public func findIndex(item: T) -> Int {
90+
var leftBound = 0
91+
var rightBound = count - 1
92+
93+
while leftBound <= rightBound {
94+
let mid = leftBound + ((rightBound - leftBound) / 2)
95+
96+
if internalSet[mid] > item {
97+
rightBound = mid - 1
98+
} else if internalSet[mid] < item {
99+
leftBound = mid + 1
100+
} else {
101+
// check the mid value to see if it is the item we are looking for
102+
if internalSet[mid] == item {
103+
return mid
104+
}
105+
106+
var j = mid
107+
108+
// check right side of mid
109+
while j < internalSet.count - 1 && !(internalSet[j] < internalSet[j + 1]) {
110+
if internalSet[j + 1] == item {
111+
return j + 1
112+
}
113+
114+
j += 1
115+
}
116+
117+
j = mid
118+
119+
// check left side of mid
120+
while j > 0 && !(internalSet[j] < internalSet[j - 1]) {
121+
if internalSet[j - 1] == item {
122+
return j - 1
123+
}
124+
125+
j -= 1
126+
}
127+
return -1
128+
}
129+
}
130+
131+
return -1
132+
}
133+
```
134+
Since our set is sorted, we can use a binary search to quickly search for the item. If you are not familiar with the concept of binary search, we have an article all about it [here](../Binary\ Search).
135+
136+
Since a set can contain multiple items with the same *value*, it is important to check to see if we have the correct item.
137+
138+
For example, consider this Ordered Set
139+
```
140+
["a", "b", "c", "longer string", "even longer string"]
141+
Where the value of each String is equal to it's length.
142+
```
143+
The call `findIndex("a")` with the traditional implementation of Binary Search would give us the value of 2, however we know that "a" is located at index 0. Thus, we need to check the items with the same *value* to the right and left of the mid value.
144+
145+
The code to check the left and right side are similar so we will only look at the code that checks the left side.
146+
``` swift
147+
j = mid
148+
149+
// check left side of mid
150+
while j > 0 && !(internalSet[j] < internalSet[j - 1]) {
151+
if internalSet[j - 1] == item {
152+
return j - 1
153+
}
154+
155+
j -= 1
156+
}
157+
return -1
158+
```
159+
First, `j` starts at the mid value. Above, we've already checked to see that the item at index `j` is not equal to the item we are looking for. Then, we keep looping until we either reach the end of the array, or hit an element which has a lower value than the current item at index `j`. If the item at value `j - 1` is equal to the one we are looking for, we return that index, otherwise we keep decreasing `j`. Once the loop terminates, we were unable to find the item and so we return -1.
160+
161+
The combined runtime for this function is **O(log(n) + k)** where `n` is the length of the set, and `k` is the number of
162+
items with the same *value* as the one that is being searched for.
163+
2164

3-
Under Construction
4165

5166
*Written By Zain Humayun*

0 commit comments

Comments
 (0)