Ordered Set을 구현하는 방법을 살펴보자.
아래는 Ordered Set이 어떻게 동작하는지 보여주는 예제다.
let s = AppleOrderedSet<Int>()
s.add(1)
s.add(2)
s.add(-1)
s.add(0)
s.insert(4, at: 3)
print(s.all()) // [1, 2, -1, 4, 0]
s.set(-1, at: 0) // 이미 인덱스 2에 -1이 있으므로 아무 작업도 수행하지 않음
print(s.all()) // [1, 2, -1, 4, 0]
s.remove(-1)
print(s.all()) // [1, 2, 4, 0]
print(s.object(at: 1)) // 2
print(s.object(at: 2)) // 4
여기서 중요한 차이점은 배열이 정렬되지 않는다는 것이다. 배열에 삽입된 요소들은 그대로 유지된다. 중복 없이 O(logn)
또는 O(1)
검색 시간을 가진 배열을 상상해 보자.
이 아이디어는 O(1)
또는 O(logn)
시간 복잡도를 제공하는 데이터 구조를 사용하는 것이다. 따라서 해시 테이블을 떠올리기 쉽다.
var indexOfKey: [T: Int]
var objects: [T]
indexOfKey
는 요소의 인덱스를 추적하는 데 사용된다. objects
는 요소를 담는 배열이다.
몇 가지 주요 함수의 세부 사항을 살펴보자.
indexOfKey
를 업데이트하고 objects
의 끝에 엘리먼트를 삽입한다.
// O(1)
public func add(_ object: T) {
guard indexOfKey[object] == nil else {
return
}
objects.append(object)
indexOfKey[object] = objects.count - 1
}
배열의 임의 위치에 삽입할 때 O(n)
시간이 소요된다.
// O(n)
public func insert(_ object: T, at index: Int) {
assert(index < objects.count, "인덱스는 객체 개수보다 작아야 함")
assert(index >= 0, "인덱스는 0보다 커야 함")
guard indexOfKey[object] == nil else {
return
}
objects.insert(object, at: index)
indexOfKey[object] = index
for i in index+1..<objects.count {
indexOfKey[objects[i]] = i
}
}
// O(1)
public func set(_ object: T, at index: Int) {
assert(index < objects.count, "인덱스는 객체 개수보다 작아야 함")
assert(index >= 0, "인덱스는 0보다 커야 함")
guard indexOfKey[object] == nil else {
return
}
indexOfKey.removeValue(forKey: objects[index])
indexOfKey[object] = index
objects[index] = object
}
배열에서 엘리먼트를 제거하는 작업은 O(n)
의 시간 복잡도를 가진다. 이때, 제거된 엘리먼트 이후의 모든 엘리먼트의 인덱스를 업데이트해야 한다.
// O(n)
public func remove(_ object: T) {
guard let index = indexOfKey[object] else {
return
}
indexOfKey.removeValue(forKey: object)
objects.remove(at: index)
for i in index..<objects.count {
indexOfKey[objects[i]] = i
}
}
작성자: Kai Chen