mirror of
https://github.com/SideStore/SideStore.git
synced 2026-02-09 14:53:25 +01:00
- DataStructures: Implemented LinkedHashMap and TreeMap (RB Tree) in swift
This commit is contained in:
226
SideStore/Utils/datastructures/LinkedHashMap.swift
Normal file
226
SideStore/Utils/datastructures/LinkedHashMap.swift
Normal file
@@ -0,0 +1,226 @@
|
||||
//
|
||||
// LinkedHashMap.swift
|
||||
// SideStore
|
||||
//
|
||||
// Created by Magesh K on 21/02/25.
|
||||
// Copyright © 2025 SideStore. All rights reserved.
|
||||
//
|
||||
|
||||
|
||||
/// A generic LinkedHashMap implementation in Swift.
|
||||
/// It provides constant-time lookup along with predictable (insertion) ordering.
|
||||
public final class LinkedHashMap<Key: Hashable, Value>: Sequence {
|
||||
|
||||
/// Internal doubly-linked list node
|
||||
fileprivate final class Node {
|
||||
let key: Key
|
||||
var value: Value
|
||||
var next: Node?
|
||||
weak var prev: Node? // weak to avoid strong reference cycle
|
||||
|
||||
init(key: Key, value: Value) {
|
||||
self.key = key
|
||||
self.value = value
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Storage
|
||||
|
||||
/// Dictionary for fast lookup from key to node.
|
||||
private var dict: [Key: Node] = [:]
|
||||
|
||||
/// Head and tail of the doubly-linked list to maintain order.
|
||||
private var head: Node?
|
||||
private var tail: Node?
|
||||
|
||||
// MARK: - Initialization
|
||||
|
||||
/// Creates an empty LinkedHashMap.
|
||||
public init() { }
|
||||
|
||||
/// Creates a LinkedHashMap from a standard dictionary.
|
||||
public init(_ dictionary: [Key: Value]) {
|
||||
for (key, value) in dictionary {
|
||||
_ = self.put(key: key, value: value)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Public API
|
||||
|
||||
/// The number of key-value pairs in the map.
|
||||
public var count: Int {
|
||||
return dict.count
|
||||
}
|
||||
|
||||
/// A Boolean value indicating whether the map is empty.
|
||||
public var isEmpty: Bool {
|
||||
return dict.isEmpty
|
||||
}
|
||||
|
||||
/// Returns the value for the given key, or `nil` if the key is not found.
|
||||
public func get(key: Key) -> Value? {
|
||||
return dict[key]?.value
|
||||
}
|
||||
|
||||
/// Inserts or updates the value for the given key.
|
||||
/// - Returns: The previous value for the key if it existed; otherwise, `nil`.
|
||||
@discardableResult
|
||||
public func put(key: Key, value: Value) -> Value? {
|
||||
if let node = dict[key] {
|
||||
let oldValue = node.value
|
||||
node.value = value
|
||||
return oldValue
|
||||
} else {
|
||||
let newNode = Node(key: key, value: value)
|
||||
dict[key] = newNode
|
||||
appendNode(newNode)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
/// Removes the value for the given key.
|
||||
/// - Returns: The removed value if it existed; otherwise, `nil`.
|
||||
@discardableResult
|
||||
public func remove(key: Key) -> Value? {
|
||||
guard let node = dict.removeValue(forKey: key) else { return nil }
|
||||
removeNode(node)
|
||||
return node.value
|
||||
}
|
||||
|
||||
/// Removes all key-value pairs from the map.
|
||||
public func clear() {
|
||||
dict.removeAll()
|
||||
head = nil
|
||||
tail = nil
|
||||
}
|
||||
|
||||
/// Determines whether the map contains the given key.
|
||||
public func containsKey(_ key: Key) -> Bool {
|
||||
return dict[key] != nil
|
||||
}
|
||||
|
||||
/// Determines whether the map contains the given value.
|
||||
/// Note: This method requires that Value conforms to Equatable.
|
||||
public func containsValue(_ value: Value) -> Bool where Value: Equatable {
|
||||
var current = head
|
||||
while let node = current {
|
||||
if node.value == value {
|
||||
return true
|
||||
}
|
||||
current = node.next
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/// Returns all keys in insertion order.
|
||||
public var keys: [Key] {
|
||||
var result = [Key]()
|
||||
var current = head
|
||||
while let node = current {
|
||||
result.append(node.key)
|
||||
current = node.next
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
/// Returns all values in insertion order.
|
||||
public var values: [Value] {
|
||||
var result = [Value]()
|
||||
var current = head
|
||||
while let node = current {
|
||||
result.append(node.value)
|
||||
current = node.next
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
/// Subscript for getting and setting values.
|
||||
public subscript(key: Key) -> Value? {
|
||||
get {
|
||||
return get(key: key)
|
||||
}
|
||||
set {
|
||||
if let newValue = newValue {
|
||||
_ = put(key: key, value: newValue)
|
||||
} else {
|
||||
_ = remove(key: key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Sequence Conformance
|
||||
|
||||
/// Iterator that yields key-value pairs in insertion order.
|
||||
public struct Iterator: IteratorProtocol {
|
||||
private var current: Node?
|
||||
|
||||
fileprivate init(start: Node?) {
|
||||
self.current = start
|
||||
}
|
||||
|
||||
public mutating func next() -> (key: Key, value: Value)? {
|
||||
guard let node = current else { return nil }
|
||||
current = node.next
|
||||
return (node.key, node.value)
|
||||
}
|
||||
}
|
||||
|
||||
public func makeIterator() -> Iterator {
|
||||
return Iterator(start: head)
|
||||
}
|
||||
|
||||
// MARK: - Private Helpers
|
||||
|
||||
/// Appends a new node to the end of the linked list.
|
||||
private func appendNode(_ node: Node) {
|
||||
if let tailNode = tail {
|
||||
tailNode.next = node
|
||||
node.prev = tailNode
|
||||
tail = node
|
||||
} else {
|
||||
head = node
|
||||
tail = node
|
||||
}
|
||||
}
|
||||
|
||||
/// Removes the given node from the linked list.
|
||||
private func removeNode(_ node: Node) {
|
||||
let prevNode = node.prev
|
||||
let nextNode = node.next
|
||||
|
||||
if let prevNode = prevNode {
|
||||
prevNode.next = nextNode
|
||||
} else {
|
||||
head = nextNode
|
||||
}
|
||||
|
||||
if let nextNode = nextNode {
|
||||
nextNode.prev = prevNode
|
||||
} else {
|
||||
tail = prevNode
|
||||
}
|
||||
|
||||
// Disconnect node's pointers.
|
||||
node.prev = nil
|
||||
node.next = nil
|
||||
}
|
||||
|
||||
public func removeValue(forKey key: Key) -> Value? {
|
||||
return remove(key: key)
|
||||
}
|
||||
}
|
||||
|
||||
extension LinkedHashMap {
|
||||
public subscript(key: Key, default defaultValue: @autoclosure () -> Value) -> Value {
|
||||
get {
|
||||
if let value = self[key] {
|
||||
return value
|
||||
} else {
|
||||
return defaultValue()
|
||||
}
|
||||
}
|
||||
set {
|
||||
self[key] = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
397
SideStore/Utils/datastructures/TreeMap.swift
Normal file
397
SideStore/Utils/datastructures/TreeMap.swift
Normal file
@@ -0,0 +1,397 @@
|
||||
//
|
||||
// TreeMap.swift
|
||||
// SideStore
|
||||
//
|
||||
// Created by Magesh K on 21/02/25.
|
||||
// Copyright © 2025 SideStore. All rights reserved.
|
||||
//
|
||||
|
||||
public class TreeMap<Key: Comparable, Value>: Sequence {
|
||||
|
||||
// MARK: - Node and Color Definitions
|
||||
|
||||
fileprivate enum Color {
|
||||
case red
|
||||
case black
|
||||
}
|
||||
|
||||
fileprivate class Node {
|
||||
var key: Key
|
||||
var value: Value
|
||||
var left: Node?
|
||||
var right: Node?
|
||||
weak var parent: Node?
|
||||
var color: Color
|
||||
|
||||
init(key: Key, value: Value, color: Color = .red, parent: Node? = nil) {
|
||||
self.key = key
|
||||
self.value = value
|
||||
self.color = color
|
||||
self.parent = parent
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - TreeMap Properties and Initializer
|
||||
|
||||
private var root: Node?
|
||||
public private(set) var count: Int = 0
|
||||
|
||||
public init() {}
|
||||
|
||||
// MARK: - Public Dictionary-like API
|
||||
|
||||
/// Subscript: Get or set value for a given key.
|
||||
public subscript(key: Key) -> Value? {
|
||||
get { return get(key: key) }
|
||||
set {
|
||||
if let newValue = newValue {
|
||||
_ = insert(key: key, value: newValue)
|
||||
} else {
|
||||
_ = remove(key: key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the value associated with the given key.
|
||||
public func get(key: Key) -> Value? {
|
||||
guard let node = getNode(forKey: key) else { return nil }
|
||||
return node.value
|
||||
}
|
||||
|
||||
/// Inserts (or updates) the key with the given value.
|
||||
/// Returns the old value if the key was already present.
|
||||
@discardableResult
|
||||
public func insert(key: Key, value: Value) -> Value? {
|
||||
if let node = getNode(forKey: key) {
|
||||
let oldValue = node.value
|
||||
node.value = value
|
||||
return oldValue
|
||||
}
|
||||
// Create new node
|
||||
let newNode = Node(key: key, value: value)
|
||||
var parent: Node? = nil
|
||||
var current = root
|
||||
while let cur = current {
|
||||
parent = cur
|
||||
if newNode.key < cur.key {
|
||||
current = cur.left
|
||||
} else {
|
||||
current = cur.right
|
||||
}
|
||||
}
|
||||
newNode.parent = parent
|
||||
if parent == nil {
|
||||
root = newNode
|
||||
} else if newNode.key < parent!.key {
|
||||
parent!.left = newNode
|
||||
} else {
|
||||
parent!.right = newNode
|
||||
}
|
||||
count += 1
|
||||
fixAfterInsertion(newNode)
|
||||
return nil
|
||||
}
|
||||
|
||||
/// Removes the node with the given key.
|
||||
/// Returns the removed value if it existed.
|
||||
@discardableResult
|
||||
public func remove(key: Key) -> Value? {
|
||||
guard let node = getNode(forKey: key) else { return nil }
|
||||
let removedValue = node.value
|
||||
deleteNode(node)
|
||||
count -= 1
|
||||
return removedValue
|
||||
}
|
||||
|
||||
/// Returns true if the map is empty.
|
||||
public var isEmpty: Bool {
|
||||
return count == 0
|
||||
}
|
||||
|
||||
/// Returns all keys in sorted order.
|
||||
public var keys: [Key] {
|
||||
var result = [Key]()
|
||||
for (k, _) in self { result.append(k) }
|
||||
return result
|
||||
}
|
||||
|
||||
/// Returns all values in order of their keys.
|
||||
public var values: [Value] {
|
||||
var result = [Value]()
|
||||
for (_, v) in self { result.append(v) }
|
||||
return result
|
||||
}
|
||||
|
||||
/// Removes all entries.
|
||||
public func removeAll() {
|
||||
root = nil
|
||||
count = 0
|
||||
}
|
||||
|
||||
// MARK: - Internal Helper Methods
|
||||
|
||||
/// Standard BST search for a node matching the key.
|
||||
private func getNode(forKey key: Key) -> Node? {
|
||||
var current = root
|
||||
while let node = current {
|
||||
if key == node.key {
|
||||
return node
|
||||
} else if key < node.key {
|
||||
current = node.left
|
||||
} else {
|
||||
current = node.right
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
/// Returns the minimum node in the subtree rooted at `node`.
|
||||
private func minimum(_ node: Node) -> Node {
|
||||
var current = node
|
||||
while let next = current.left {
|
||||
current = next
|
||||
}
|
||||
return current
|
||||
}
|
||||
|
||||
// MARK: - Rotation Methods
|
||||
|
||||
private func rotateLeft(_ x: Node) {
|
||||
guard let y = x.right else { return }
|
||||
x.right = y.left
|
||||
if let leftChild = y.left {
|
||||
leftChild.parent = x
|
||||
}
|
||||
y.parent = x.parent
|
||||
if x.parent == nil {
|
||||
root = y
|
||||
} else if x === x.parent?.left {
|
||||
x.parent?.left = y
|
||||
} else {
|
||||
x.parent?.right = y
|
||||
}
|
||||
y.left = x
|
||||
x.parent = y
|
||||
}
|
||||
|
||||
private func rotateRight(_ x: Node) {
|
||||
guard let y = x.left else { return }
|
||||
x.left = y.right
|
||||
if let rightChild = y.right {
|
||||
rightChild.parent = x
|
||||
}
|
||||
y.parent = x.parent
|
||||
if x.parent == nil {
|
||||
root = y
|
||||
} else if x === x.parent?.right {
|
||||
x.parent?.right = y
|
||||
} else {
|
||||
x.parent?.left = y
|
||||
}
|
||||
y.right = x
|
||||
x.parent = y
|
||||
}
|
||||
|
||||
// MARK: - Insertion Fix-Up
|
||||
|
||||
/// Restores red–black properties after insertion.
|
||||
private func fixAfterInsertion(_ x: Node) {
|
||||
var node = x
|
||||
node.color = .red
|
||||
while node !== root, let parent = node.parent, parent.color == .red {
|
||||
if parent === parent.parent?.left {
|
||||
if let uncle = parent.parent?.right, uncle.color == .red {
|
||||
parent.color = .black
|
||||
uncle.color = .black
|
||||
parent.parent?.color = .red
|
||||
if let grandparent = parent.parent {
|
||||
node = grandparent
|
||||
}
|
||||
} else {
|
||||
if node === parent.right {
|
||||
node = parent
|
||||
rotateLeft(node)
|
||||
}
|
||||
node.parent?.color = .black
|
||||
node.parent?.parent?.color = .red
|
||||
if let grandparent = node.parent?.parent {
|
||||
rotateRight(grandparent)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if let uncle = parent.parent?.left, uncle.color == .red {
|
||||
parent.color = .black
|
||||
uncle.color = .black
|
||||
parent.parent?.color = .red
|
||||
if let grandparent = parent.parent {
|
||||
node = grandparent
|
||||
}
|
||||
} else {
|
||||
if node === parent.left {
|
||||
node = parent
|
||||
rotateRight(node)
|
||||
}
|
||||
node.parent?.color = .black
|
||||
node.parent?.parent?.color = .red
|
||||
if let grandparent = node.parent?.parent {
|
||||
rotateLeft(grandparent)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
root?.color = .black
|
||||
}
|
||||
|
||||
// MARK: - Deletion Helpers
|
||||
|
||||
/// Replaces subtree rooted at u with subtree rooted at v.
|
||||
private func transplant(_ u: Node, _ v: Node?) {
|
||||
if u.parent == nil {
|
||||
root = v
|
||||
} else if u === u.parent?.left {
|
||||
u.parent?.left = v
|
||||
} else {
|
||||
u.parent?.right = v
|
||||
}
|
||||
if let vNode = v {
|
||||
vNode.parent = u.parent
|
||||
}
|
||||
}
|
||||
|
||||
/// Deletes node z and fixes red–black properties.
|
||||
private func deleteNode(_ z: Node) {
|
||||
var y = z
|
||||
let originalColor = y.color
|
||||
var x: Node?
|
||||
|
||||
if z.left == nil {
|
||||
x = z.right
|
||||
transplant(z, z.right)
|
||||
} else if z.right == nil {
|
||||
x = z.left
|
||||
transplant(z, z.left)
|
||||
} else {
|
||||
y = minimum(z.right!)
|
||||
let yOriginalColor = y.color
|
||||
x = y.right
|
||||
if y.parent === z {
|
||||
if x != nil { x!.parent = y }
|
||||
} else {
|
||||
transplant(y, y.right)
|
||||
y.right = z.right
|
||||
y.right?.parent = y
|
||||
}
|
||||
transplant(z, y)
|
||||
y.left = z.left
|
||||
y.left?.parent = y
|
||||
y.color = z.color
|
||||
if yOriginalColor == .black {
|
||||
fixAfterDeletion(x, parent: y.parent)
|
||||
}
|
||||
return
|
||||
}
|
||||
if originalColor == .black {
|
||||
fixAfterDeletion(x, parent: z.parent)
|
||||
}
|
||||
}
|
||||
|
||||
/// Restores red–black properties after deletion.
|
||||
private func fixAfterDeletion(_ x: Node?, parent: Node?) {
|
||||
var x = x
|
||||
var parent = parent
|
||||
while (x == nil || x!.color == .black) && (x !== root) {
|
||||
if x === parent?.left {
|
||||
var w = parent?.right
|
||||
if w?.color == .red {
|
||||
w?.color = .black
|
||||
parent?.color = .red
|
||||
rotateLeft(parent!)
|
||||
w = parent?.right
|
||||
}
|
||||
if (w?.left == nil || w?.left?.color == .black) &&
|
||||
(w?.right == nil || w?.right?.color == .black) {
|
||||
w?.color = .red
|
||||
x = parent
|
||||
parent = x?.parent
|
||||
} else {
|
||||
if w?.right == nil || w?.right?.color == .black {
|
||||
w?.left?.color = .black
|
||||
w?.color = .red
|
||||
if let wUnwrapped = w { rotateRight(wUnwrapped) }
|
||||
w = parent?.right
|
||||
}
|
||||
w?.color = parent?.color ?? .black
|
||||
parent?.color = .black
|
||||
w?.right?.color = .black
|
||||
rotateLeft(parent!)
|
||||
x = root
|
||||
parent = nil
|
||||
}
|
||||
} else {
|
||||
var w = parent?.left
|
||||
if w?.color == .red {
|
||||
w?.color = .black
|
||||
parent?.color = .red
|
||||
rotateRight(parent!)
|
||||
w = parent?.left
|
||||
}
|
||||
if (w?.left == nil || w?.left?.color == .black) &&
|
||||
(w?.right == nil || w?.right?.color == .black) {
|
||||
w?.color = .red
|
||||
x = parent
|
||||
parent = x?.parent
|
||||
} else {
|
||||
if w?.left == nil || w?.left?.color == .black {
|
||||
w?.right?.color = .black
|
||||
w?.color = .red
|
||||
if let wUnwrapped = w { rotateLeft(wUnwrapped) }
|
||||
w = parent?.left
|
||||
}
|
||||
w?.color = parent?.color ?? .black
|
||||
parent?.color = .black
|
||||
w?.left?.color = .black
|
||||
rotateRight(parent!)
|
||||
x = root
|
||||
parent = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
x?.color = .black
|
||||
}
|
||||
|
||||
// Convenience overload if parent is not separately tracked.
|
||||
private func fixAfterDeletion(_ x: Node?) {
|
||||
fixAfterDeletion(x, parent: x?.parent)
|
||||
}
|
||||
|
||||
// MARK: - Sequence Conformance (In-Order Traversal)
|
||||
|
||||
public struct Iterator: IteratorProtocol {
|
||||
private var stack: [Node] = []
|
||||
|
||||
// Marked as private because Node is a private type.
|
||||
fileprivate init(root: Node?) {
|
||||
var current = root
|
||||
while let node = current {
|
||||
stack.append(node)
|
||||
current = node.left
|
||||
}
|
||||
}
|
||||
|
||||
public mutating func next() -> (Key, Value)? {
|
||||
if stack.isEmpty { return nil }
|
||||
let node = stack.removeLast()
|
||||
let result = (node.key, node.value)
|
||||
var current = node.right
|
||||
while let n = current {
|
||||
stack.append(n)
|
||||
current = n.left
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
public func makeIterator() -> Iterator {
|
||||
return Iterator(root: root)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user