Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Priority Queue in swift

I'm trying to implement a version of Dijkstra Algorithm to find the shortest route for a bus to take from start to end. Unfortunately, I cannot seem to find a library or other way that swift provides a type of priority queue so it seems I will have to code my own.

This being said, can anyone point me in the right direction to do this?

Currently my thinking is as follows:

Write a class which will hold the priority array. In this class there will be a method which receives a value, adds it to the priority array and then sorts it according to priority (In this case, distance). There will also be a get function which returns the highest priority item from the array.

I would like to know if I'm close or still way off in my understanding of a priority queue.

Thank you.

EDIT :

This is my code so far. Seems too short and brutal... I must be missing something in terms of the concept.

var priorityQueue = Dictionary<String, Int>()
var firstElement: String = ""

func push(name: String, distance: Int)
{
    priorityQueue[name] = distance
    var myArr = Array(priorityQueue.keys)
    var sortedKeys = sort(myArr) {
        var obj1 = self.priorityQueue[$0] // get obj associated w/ key 1
        var obj2 = self.priorityQueue[$1] // get obj associated w/ key 2
        return obj1 > obj2
    }

    firstElement = myArr[0]
    var tempPriorityQueue = Dictionary<String, Int>()
    for val in myArr
    {
        tempPriorityQueue[val] = priorityQueue[val]
    }

    priorityQueue = tempPriorityQueue
}

func pop() -> String
{
    priorityQueue.removeValueForKey(firstElement)
}
like image 362
Byron Coetsee Avatar asked Jul 01 '14 16:07

Byron Coetsee


2 Answers

You may be interested in looking at two open source projects that I've authored. The first is SwiftPriorityQueue: https://github.com/davecom/SwiftPriorityQueue

Your implementation of a priority queue sorts on push which is O(n lg n). Most implementations of a priority queue, including SwiftPriorityQueue, use a binary heap as the backing store. They have push operations that operate in O(lg n) and pops that operate in O(lg n) as well. Therefore your suspicions are right - your current implementation is unlikely to be very performant (although pops are technically faster).

The second is SwiftGraph: https://github.com/davecom/SwiftGraph

SwiftGraph includes an implementation of Dijkstra's Algorithm.

I'm surprised neither of these projects was easier to find since they have been out for over a year and moderately popular, but based on current answers to this question in the last year, it seems I need to work on discoverability.

like image 173
davecom Avatar answered Nov 08 '22 20:11

davecom


You should use heap sort for the priority. I think this is optimal! Try it out in playground!

import Foundation
typealias PriorityDefinition<P> = (_ p1: P, _ p2: P) -> (Bool)
class PriorityQueue<E, P: Hashable> {
    var priDef: PriorityDefinition<P>!
    var elemments = [P: [E]]()
    var priority = [P]()
    init(_ priDef: @escaping PriorityDefinition<P>) {
        self.priDef = priDef
    }
    func enqueue(_ element: E!, _ priorityValue: P!) {
        if let _ = elemments[priorityValue] {
            elemments[priorityValue]!.append(element)
        } else {
            elemments[priorityValue] = [element]
        }
        if !priority.contains(priorityValue) {
            priority.append(priorityValue)
            let lastIndex = priority.count - 1
            siftUp(0, lastIndex, lastIndex)
        }
    }
    func dequeue() -> E? {
        var result: E? = nil
        if priority.count > 0 {
            var p = priority.first!
            if elemments[p]!.count == 1 {
                if priority.count > 1 {
                    let _temp = priority[0]
                    priority[0] = priority[priority.count - 1]
                    priority[priority.count - 1] = _temp
                    p = priority.last!
                    siftDown(0, priority.count - 2)
                }
                result = elemments[p]!.removeFirst()
                elemments[p] = nil
                priority.remove(at: priority.index(of: p)!)
            } else {
                result = elemments[p]!.removeFirst()
            }
        }
        return result
    }
    func siftDown(_ start: Int, _ end: Int) {
        let iLeftChild = 2 * start + 1
        if iLeftChild <= end {
            var largestChild = priDef(priority[iLeftChild], priority[start]) ? iLeftChild : start
            let iRightChild = 2 * start + 2
            if iRightChild <= end {
                if priDef(priority[iRightChild], priority[iLeftChild]) {
                    largestChild = iRightChild
                }
            }
            if largestChild == start {
                return
            } else {
                let _temp = priority[start]
                priority[start] = priority[largestChild]
                priority[largestChild] = _temp
                siftDown(largestChild, end)
            }
        }
    }
    func siftUp(_ start: Int, _ end: Int, _ nodeIndex: Int) {
        let parent = (nodeIndex - 1) / 2
        if parent >= start {
            if priDef(priority[nodeIndex], priority[parent]) {
                let _temp = priority[nodeIndex]
                priority[nodeIndex] = priority[parent]
                priority[parent] = _temp
                siftUp(start, end, parent)
            } else {
                return
            }
        }
    }
    func isEmpty() -> Bool {
        return priority.count == 0
    }
}
let Q = PriorityQueue<Int, Int> { (p1: Int, p2: Int) -> (Bool) in
    return p1 > p2
}
let n = 999
for i in 0...n - 1 {
    let start = NSDate().timeIntervalSince1970
    Q.enqueue(i, i)
    let end = NSDate().timeIntervalSince1970
    print(end - start)
}
like image 30
Bao HQ Avatar answered Nov 08 '22 19:11

Bao HQ