Oct 14, 2022
An ordering $(A, \leq)$ (an ordering over type $A$) is ...
An ordering needs to be...
Course 1 $\leq$ Course 2 iff Course 1 is a prereq of Course 2
Is this a valid ordering?
Yes!
(Partial order, as opposed to Total order)
A partial ordering needs to be...
A total ordering needs to be...
For a sort order $(A, \leq)$
A partial order may not have a unique greatest/least element
$\leq$ can be described explicitly, by a set of tuples.
$$\{\; (a, a), (a, b), (a, c), \ldots, (b, b), \ldots, (z, z) \;\}$$If $(x, y)$ is in the set, $x \leq y$
$\leq$ can be described by a mathematical rule.
$$\{\; (x, y) \;|\; x, y \in \mathbb Z, \exists a \in \mathbb Z_0^{+} : x + a = y \;\}$$$x \leq y$ iff $x, y$ are integers, and there is a non-negative integer $a$ s.t. $x + a = y$
Multiple orderings can be defined for the same set.
We use a subscripts to separate orderings (e.g., $\leq_1$, $\leq_2$, $\leq_3$)
We can transform orderings.
Reverse: If $x \leq_1 y$, then define $y \leq_r x$
Lexical: Given $\leq_1, \leq_2, \leq_3, \ldots$
$\leq$ can be described an ordering over a key derived from the element.
$x \leq_{edge} y$ iff $weight(x) \leq weight(y)$
$x \leq_{student} y$ iff $name(x) \leq_{lex} name(y)$
We say that the weight (resp., name) is a key.
A Topological Sort of a partial order $(A, \leq_1)$ is any total order $(A, \leq_2)$ over $A$ that "agrees" with $(A, \leq_1)$
For any two elements $x, y \in A$:
The following are all topological sorts over our partial order example:
(If the partial order is a schedule requirement, each topological sort is a possible schedule)
And now, for an ordering-based ADT...
How do we store these items?
5, 9, 2, 7?
9, 7, 5, 2?
2, 5, 7, 9?
Base Data Structure: Linked List
def pqueueSort[A](items: Seq[A], pqueue: PriorityQueue[A]): Seq[A] =
{
val out = new Array[A](items.size)
for(item <- items){ pqueue.enqueue(item) }
i = out.size - 1
while(!pqueue.isEmpty) { buffer(i) = pqueue.dequeue; i-- }
return out.toSeq
}
Seq/Buffer | PQueue | |
---|---|---|
Input | (7, 4, 8, 2, 5, 3, 9) | () |
Step 1 | (4, 8, 2, 5, 3, 9) | (7) |
Step 2 | (8, 2, 5, 3, 9) | (7, 4) |
į§ į§ į§ | ||
Step N | [_, _, _, _, _, _, _] | (7, 4, 8, 2, 5, 3, 9) |
Step N+1 | [_, _, _, _, _, _, 9] | (7, 4, 8, 2, 5, 3) |
Step N+2 | [_, _, _, _, _, 8, 9] | (7, 4, 2, 5, 3) |
Step N+3 | [_, _, _, _, 7, 8, 9] | (4, 2, 5, 3) |
Step N+4 | [_, _, _, 5, 7, 8, 9] | (4, 2, 3) |
Step N+5 | [_, _, 4, 5, 7, 8, 9] | (2, 3) |
Step N+6 | [_, 3, 4, 5, 7, 8, 9] | (2) |
Step 2N | [2, 3, 4, 5, 7, 8, 9] | () |
def pqueueSort[A](items: Seq[A], pqueue: PriorityQueue[A]): Seq[A] =
{
val out = new Array[A](items.size)
for(item <- items){ pqueue.enqueue(item) }
i = out.size - 1
while(!pqueue.isEmpty) { buffer(i) = pqueue.dequeue; i-- }
return out.toSeq
}
What's the complexity?
Base Data Structure: Linked List
Seq/Buffer | PQueue | |
---|---|---|
Input | (7, 4, 8, 2, 5, 3, 9) | () |
Step 1 | (4, 8, 2, 5, 3, 9) | (7) |
Step 2 | (8, 2, 5, 3, 9) | (7, 4) |
Step 3 | (2, 5, 3, 9) | (8, 7, 4) |
Step 4 | (5, 3, 9) | (8, 7, 4, 2) |
Step 5 | (3, 9) | (8, 7, 5, 4, 2) |
Step 6 | (9) | (8, 7, 5, 4, 3, 2) |
Step N | [_, _, _, _, _, _, _] | (9, 8, 7, 5, 4, 3, 2) |
Step N+1 | [_, _, _, _, _, _, 9] | (8, 7, 5, 4, 3, 2) |
į§ į§ į§ | ||
Step 2N | [2, 3, 4, 5, 7, 8, 9] | () |
def pqueueSort[A](items: Seq[A], pqueue: PriorityQueue[A]): Seq[A] =
{
val out = new Array[A](items.size)
for(item <- items){ pqueue.enqueue(item) }
i = out.size - 1
while(!pqueue.isEmpty) { buffer(i) = pqueue.dequeue; i-- }
return out.toSeq
}
What's the complexity?
Operation | Lazy | Proactive |
---|---|---|
enqueue | $O(1)$ | $O(n)$ |
dequeue | $O(n)$ | $O(1)$ |
head | $O(n)$ | $O(1)$ |
Can we do better?