mirror of
https://github.com/pgpainless/pgpainless.git
synced 2024-12-24 03:47:57 +01:00
Implement PairPriorityQueue
This commit is contained in:
parent
9560270386
commit
71f859af9b
2 changed files with 190 additions and 0 deletions
|
@ -0,0 +1,44 @@
|
|||
// SPDX-FileCopyrightText: 2023 Heiko Schaefer <heiko@schaefer.name>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package org.pgpainless.wot.dijkstra
|
||||
|
||||
import java.util.PriorityQueue
|
||||
|
||||
/**
|
||||
* A de-duplicating min-priority queue for key-value pairs.
|
||||
*
|
||||
* When an element is popped, the queue entry with the *most desirable
|
||||
* value* (that is: low cost) is popped (if there are multiple elements
|
||||
* with the same minimal value, one of them is returned.)
|
||||
*
|
||||
* When inserting an element, if there is already an element with the same
|
||||
* key, the element with the smaller value is kept.
|
||||
*/
|
||||
internal class PairPriorityQueue<K, V : Comparable<V>>() {
|
||||
|
||||
// NOTE: This implementation is not optimized for efficient inserts!
|
||||
// - Each insert() involves a linear search by key
|
||||
// - Each insert() sorts eagerly (via j.u.PriorityQueue.add())
|
||||
|
||||
private val pq: PriorityQueue<Pair<K, V>> = PriorityQueue { o1, o2 ->
|
||||
// Order priority queue entries by value (min first)
|
||||
o1.second.compareTo(o2.second)
|
||||
}
|
||||
|
||||
fun insertOrUpdate(key: K, value: V) {
|
||||
when (val element = pq.find { it.first == key }) {
|
||||
null -> pq.add(Pair(key, value)) // Add as a new element
|
||||
else -> {
|
||||
// If the new value is "cheaper": replace the element
|
||||
if (value < element.second) {
|
||||
pq.remove(element)
|
||||
pq.add(Pair(key, value))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun pop(): Pair<K, V>? = pq.poll()
|
||||
}
|
|
@ -0,0 +1,146 @@
|
|||
// SPDX-FileCopyrightText: 2023 Heiko Schaefer <heiko@schaefer.name>
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package org.pgpainless.wot.dijkstra
|
||||
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class PairPriorityQueueTest {
|
||||
|
||||
@Test
|
||||
fun simple1() {
|
||||
val pq: PairPriorityQueue<Int, Int> = PairPriorityQueue();
|
||||
|
||||
pq.insertOrUpdate(0, 0);
|
||||
pq.insertOrUpdate(1, 1);
|
||||
pq.insertOrUpdate(2, 2);
|
||||
pq.insertOrUpdate(3, 3);
|
||||
pq.insertOrUpdate(4, 4);
|
||||
pq.insertOrUpdate(5, 5);
|
||||
|
||||
assertEquals(Pair(0, 0), pq.pop());
|
||||
assertEquals(Pair(1, 1), pq.pop());
|
||||
assertEquals(Pair(2, 2), pq.pop());
|
||||
assertEquals(Pair(3, 3), pq.pop());
|
||||
assertEquals(Pair(4, 4), pq.pop());
|
||||
assertEquals(Pair(5, 5), pq.pop());
|
||||
|
||||
assertEquals(null, pq.pop());
|
||||
assertEquals(null, pq.pop());
|
||||
}
|
||||
|
||||
@Test
|
||||
fun simple2() {
|
||||
val pq: PairPriorityQueue<Int, Int> = PairPriorityQueue();
|
||||
|
||||
pq.insertOrUpdate(0, 0);
|
||||
pq.insertOrUpdate(1, -1);
|
||||
pq.insertOrUpdate(2, -2);
|
||||
pq.insertOrUpdate(3, -3);
|
||||
pq.insertOrUpdate(4, -4);
|
||||
pq.insertOrUpdate(5, -5);
|
||||
|
||||
assertEquals(Pair(5, -5), pq.pop());
|
||||
assertEquals(Pair(4, -4), pq.pop());
|
||||
assertEquals(Pair(3, -3), pq.pop());
|
||||
assertEquals(Pair(2, -2), pq.pop());
|
||||
assertEquals(Pair(1, -1), pq.pop());
|
||||
assertEquals(Pair(0, 0), pq.pop());
|
||||
|
||||
assertEquals(null, pq.pop());
|
||||
assertEquals(null, pq.pop());
|
||||
}
|
||||
|
||||
@Test
|
||||
fun simple3() {
|
||||
val pq: PairPriorityQueue<Int, Int> = PairPriorityQueue();
|
||||
|
||||
pq.insertOrUpdate(0, 0);
|
||||
pq.insertOrUpdate(1, 1);
|
||||
pq.insertOrUpdate(5, 5);
|
||||
pq.insertOrUpdate(2, 2);
|
||||
pq.insertOrUpdate(4, 4);
|
||||
pq.insertOrUpdate(3, 3);
|
||||
|
||||
assertEquals(Pair(0, 0), pq.pop());
|
||||
assertEquals(Pair(1, 1), pq.pop());
|
||||
assertEquals(Pair(2, 2), pq.pop());
|
||||
assertEquals(Pair(3, 3), pq.pop());
|
||||
assertEquals(Pair(4, 4), pq.pop());
|
||||
assertEquals(Pair(5, 5), pq.pop());
|
||||
assertEquals(null, pq.pop());
|
||||
assertEquals(null, pq.pop());
|
||||
}
|
||||
|
||||
@Test
|
||||
fun simple4() {
|
||||
val pq: PairPriorityQueue<Int, Int> = PairPriorityQueue();
|
||||
assertEquals(null, pq.pop());
|
||||
|
||||
pq.insertOrUpdate(0, 0);
|
||||
pq.insertOrUpdate(0, 0);
|
||||
assertEquals(Pair(0, 0), pq.pop());
|
||||
assertEquals(null, pq.pop());
|
||||
}
|
||||
|
||||
@Test
|
||||
fun simple5() {
|
||||
val pq: PairPriorityQueue<Int, Int> = PairPriorityQueue();
|
||||
assertEquals(null, pq.pop());
|
||||
|
||||
pq.insertOrUpdate(0, 0);
|
||||
pq.insertOrUpdate(0, 0);
|
||||
assertEquals(Pair(0, 0), pq.pop());
|
||||
pq.insertOrUpdate(0, 0);
|
||||
assertEquals(Pair(0, 0), pq.pop());
|
||||
assertEquals(null, pq.pop());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun duplicates() {
|
||||
val pq: PairPriorityQueue<Int, Int> = PairPriorityQueue();
|
||||
|
||||
// Insert different keys with value i.
|
||||
for (i in 19 downTo 0) {
|
||||
pq.insertOrUpdate(i, i);
|
||||
}
|
||||
// Insert the same keys with lower value `i-1`.
|
||||
// This should overwrite the old keys.
|
||||
for (i in 19 downTo 0) {
|
||||
pq.insertOrUpdate(i, i - 1);
|
||||
}
|
||||
|
||||
// Insert the same keys with a higher value.
|
||||
// These should be ignored.
|
||||
for (i in 19 downTo 0) {
|
||||
pq.insertOrUpdate(i, i);
|
||||
}
|
||||
|
||||
for (i in 0 until 20) {
|
||||
assertEquals(Pair(i, i - 1), pq.pop());
|
||||
}
|
||||
assertEquals(null, pq.pop());
|
||||
assertEquals(null, pq.pop());
|
||||
}
|
||||
|
||||
@Test
|
||||
fun insert_pop() {
|
||||
val pq: PairPriorityQueue<Int, Int> = PairPriorityQueue();
|
||||
|
||||
// Insert different keys with value i+1.
|
||||
for (i in 9 downTo 0) {
|
||||
pq.insertOrUpdate(i, i + 1);
|
||||
}
|
||||
// Insert the same keys with their own value. This should
|
||||
// overwrite the old keys.
|
||||
for (i in 0 until 10) {
|
||||
pq.insertOrUpdate(i, i);
|
||||
assertEquals(Pair(i, i), pq.pop());
|
||||
}
|
||||
assertEquals(null, pq.pop());
|
||||
assertEquals(null, pq.pop());
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue