Git Product home page Git Product logo

swiftpriorityqueue's Introduction

SwiftPriorityQueue

Swift Versions CocoaPods Version SPM Compatible CocoaPods Platforms Linux Compatible Twitter Contact

SwiftPriorityQueue is a pure Swift (no Cocoa) implementation of a generic priority queue data structure, appropriate for use on all platforms (macOS, iOS, Linux, etc.) where Swift is supported. It features a straightforward interface and can be used with any type that implements Comparable. It utilizes comparisons between elements rather than separate numeric priorities to determine order.

Internally, SwiftPriorityQueue uses a classic binary heap, resulting in O(lg n) pushes and pops. It includes in-source documentation, an A* based example maze solving program (for macOS), and unit tests (pull requests are welcome for additional unit tests in particular).

Features

  • Easy-to-use method interface
  • Small, self contained, pure Swift code base
  • Classic binary heap implementation with O(lg n) pushes and pops
  • Iterable through standard Swift for...in loops (implements Sequence and IteratorProtocol)
  • In-source documentation
  • A fun maze solving A* based example program

Installation

Release 1.3.0 and above supports Swift 5. Use release 1.2.1 for Swift 4. Use release 1.1.2 for Swift 3 and Swift 2 support. Use release 1.0 for Swift 1.2 support.

CocoaPods

Use the CocoaPod SwiftPriorityQueue.

Swift Package Manager (SPM)

Add this repository as a dependency.

Manual

Copy SwiftPriorityQueue.swift into your project.

Documentation

There is a large amount of documentation in the source code using the standard Swift documentation technique (compatible with Jazzy). Essentially though, SwiftPriorityQueue has the three critical methods you'd expect - push(), pop(), and peek().

Initialization

When you create a new PriorityQueue you can optionally specify whether the priority queue is ascending or descending. What does this mean? If the priority queue is ascending, its smallest values (as determined by their implementation of Comparable aka <) will be popped first, and if it's descending, its largest values will be popped first.

var pq: PriorityQueue<String> = PriorityQueue<String>(ascending: true)

You can also provide an array of starting values to be pushed sequentially immediately into the priority queue.

var pq: PriorityQueue<Int> = PriorityQueue<Int>(startingValues: [6, 2, 3, 235, 4, 500])

Or you can specify both.

var pq: PriorityQueue<Int> = PriorityQueue<Int>(ascending: false, startingValues: [6, 2, 3, 235, 4, 500])

Or you can specify neither. By default a PriorityQueue is descending and empty. As you've probably noticed, a PriorityQueue takes a generic type. This type must be Comparable, as its comparison will be used for determining priority. This means that your custom types must implement Comparable and utilize the overridden < to determine priority.

Methods

PriorityQueue has all of the standard methods you'd expect a priority queue data structure to have.

  • push(element: T) - Puts an element into the priority queue. O(lg n)
  • push(element: T, maxCount: Int) -> T? - Adds an element while limiting the size of the priority queue to maxCount. If more than maxCount elements are in the priority queue after the addition, the lowest priority element will be discarded and returned. Note this is inefficient because this is a binary heap, so only the highet priority item is efficient to retrieve. O(n)
  • pop() -> T? - Returns and removes the element with the highest (or lowest if ascending) priority or nil if the priority queue is empty. O(lg n)
  • peek() -> T? - Returns the element with the highest (or lowest if ascending) priority or nil if the priority queue is empty. O(1)
  • clear() - Removes all elements from the priority queue.
  • remove(item: T) - Removes the first found instance of item in the priority queue. Silently returns if not found. O(n)
  • removeAll(item: T) - Removes all instances of item in the priority queue through repeated calls to remove(). Silently returns if not found.

Properties

  • count: Int - The number of elements in the priority queue.
  • isEmpty: Bool - true if the priority queue has zero elements, and false otherwise.

Standard Swift Protocols

PriorityQueue implements IteratorProtocol, Sequence and Collection so you can treat PriorityQueue like any other Swift sequence/collection. This means you can use Swift standard library fucntions on a PriorityQueue and iterate through a PriorityQueue like this:

for item in pq {  // pq is a PriorityQueue<String>
    print(item)
}

When you do this, every item from the PriorityQueue is popped in order. PriorityQueue also implements CustomStringConvertible and CustomDebugStringConvertible.

print(pq)

Note: PriorityQueue is not thread-safe (do not manipulate it from multiple threads at once).

Limited Heap Size Example

Suppose you want to only keep the maxCount highest priority items in the priority queue.

For example, say you only want the priority queue to only ever have 4 elements:

var pq: PriorityQueue<Int> = PriorityQueue<Int>()
let maxCount = 4         

pq.push(4, maxCount: maxCount)
pq.push(5, maxCount: maxCount)
pq.push(0, maxCount: maxCount)
pq.push(3, maxCount: maxCount)  
pq.push(6, maxCount: maxCount)     
pq.push(1, maxCount: maxCount)     

In this case, after 4 elements were pushed, only the smallest elements were kept (because the order was ascending). So, the final priority queue has the elements 0, 1, 3, 4 in it.

Just for Fun - A* (astar.swift)

A* is a pathfinding algorithm that uses a priority queue. The sample program that comes with SwiftPriorityQueue is a maze solver that uses A*. You can find some in-source documentation if you want to reuse this algorithm inside astar.swift.

Authorship & License

SwiftPriorityQueue is written by David Kopec (@davecom on GitHub) and released under the MIT License (see LICENSE). You can find my contact information on my GitHub profile page. I encourage you to submit pull requests and open issues here on GitHub. Thank you to all of the contributors over the years.

swiftpriorityqueue's People

Contributors

azare avatar davecom avatar emlai avatar oisdk avatar peteraisher avatar seb-bl avatar shnhrrsn avatar vale-cocoa avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

swiftpriorityqueue's Issues

crashes if the element which is the last in the heap gets removed

For silly reasons I unfortunately can't contribute code to most open-source projects. But here's a test case that crashes 100% for me:

    func testRemoveLastInHeap() {
        var pq: PriorityQueue<Int> = PriorityQueue<Int>()
        pq.push(1)
        pq.push(2)
        
        pq.remove(1)
        
        let expected: [Int] = [2]
        var actual: [Int] = []
        for j in pq {
            actual.append(j)
        }
        
        XCTAssertEqual(expected, actual)
    }

with

Exception Type:        EXC_BAD_INSTRUCTION (SIGILL)
Exception Codes:       0x0000000000000001, 0x0000000000000000
Exception Note:        EXC_CORPSE_NOTIFY

Termination Signal:    Illegal instruction: 4
Termination Reason:    Namespace SIGNAL, Code 0x4
Terminating Process:   exc handler [0]

Application Specific Information:
fatal error: Index out of range
 

Thread 0 Crashed:: Dispatch queue: com.apple.main-thread
0   libswiftCore.dylib            	0x000000010bf41de0 specialized _fatalErrorMessage(_:_:file:line:flags:) + 96
1   libswiftCore.dylib            	0x000000010bd6e1b5 _ArrayBuffer._checkInoutAndNativeTypeCheckedBounds(_:wasNativeTypeChecked:) + 261
2   libswiftCore.dylib            	0x000000010bd86067 Array.subscript.getter + 103
3   com.oaksnow.SwiftPriorityQueueTests	0x000000010cebc68c PriorityQueue.swim(_:) + 348 (SwiftPriorityQueue.swift:144)
4   com.oaksnow.SwiftPriorityQueueTests	0x000000010cebcdb1 PriorityQueue.remove(_:) + 657 (SwiftPriorityQueue.swift:97)
5   com.oaksnow.SwiftPriorityQueueTests	0x000000010ceba858 SwiftPriorityQueueTests.testRemoveLastInHeap() + 216 (SwiftPriorityQueueTests.swift:116)
6   com.oaksnow.SwiftPriorityQueueTests	0x000000010cebad04 @objc SwiftPriorityQueueTests.testRemoveLastInHeap() + 36

The problem is that swim(index) will access heap[index] but if in

    public mutating func remove(_ item: T) {
        if let index = heap.index(of: item) {
            heap.swapAt(index, heap.count - 1)
            heap.removeLast()
            swim(index)
            sink(index)
        }
    }

index turns out to be the last element (which gets removed in heap.removeLast()), then swim(index) will lead to a array OOB crash.

As I said I can't contribute the code to fix it but the only thing that's needed is not to call swapAt, swim or sink in the case index is the last element in heap.

tests not SwiftPM compatible

when I try to run the tests with SwiftPM I get:

$ swift test
Compile Swift Module 'SwiftPriorityQueue' (1 sources)
error: no tests found; create a target in the 'Tests' directory

Update to Swift 4

Should we keep backwards compatibility in the main branch or ask users to build against a previous release?

Quick question

As a data structure that might need to support a huge amount of popping, why is the priority queue a struct? I'm asking since--to my knowledge--the mutating functions will create a new priority queue every time they're called. Or is it optimized away?

tests spawn a GUI

quite irritatingly, the tests briefly spawn a GUI (which won't work on Linux).

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.