Sept 30, 2022
What's the worst-case runtime?
Tquicksort(n)∈O(n2)
... but this isn't really representative of typical behavior
Let's talk probabilities
If X represents a random (numerical) outcome, the average over all possibilities is the expectation of X, or E[X]
E[X]=∑iPi⋅Xi
We pick the Xth largest element as a pivot
E[T(n)]={Θ(1)if n≤1E[T(X−1)+T(n−X)]+Θ(n)otherwiseWe pick the Xth largest element as a pivot
E[T(n)]={Θ(1)if n≤1E[T(X−1)]+E[T(n−X)]+Θ(n)otherwiseE[T(X−1)]
=∑ni=1Pi⋅T(Xi−1)
=∑ni=11n⋅T(i−1) (T(0) up to T(n−1))
=∑ni=11n⋅T(n−i) (T(n−1) down to T(0))
=E[T(n−X)]
We pick the Xth largest element as a pivot
E[T(n)]={Θ(1)if n≤12E[T(X−1)]+Θ(n)otherwiseEach T(X−1) is independent.
E[T(n)]={Θ(1)if n≤12(∑ni=11nE[T(i−1)])+Θ(n)otherwise
Back to induction...
Hypothesis: E[T(n)]∈O(nlog(n))
Base Case: E[T(1)]≤c(1log(1))
E[T(1)]≤c⋅(1⋅0)
E[T(1)]≰0
Base Case (take Two): E[T(2)]≤c(2log(2))
2⋅Ei[T(i−1)]+2c1≤2c
2⋅(12T(0)+12T(1))+2c1≤2c
T(0)+T(1)+2c1≤2c
2c0+2c1≤2c
True for any c≥c0+c1
Assume: E[T(n′)]≤c(n′log(n′)) for all n′<n
Show: E[T(n)]≤c(nlog(n))
2n(n−1∑i=0E[T(i)])+c1≤cnlog(n)
2n(n−1∑i=0cilog(i))+c1≤cnlog(n)
c2n(n−1∑i=0ilog(n))+c1≤cnlog(n)
c2n(n−1∑i=0ilog(n))+c1≤cnlog(n)
c2log(n)n(n−1∑i=0i)+c1≤cnlog(n)
c2log(n)n((n−1)(n−1+1)2)+c1≤cnlog(n)
clog(n)n(n2−n)+c1≤cnlog(n)
cnlog(n)−clog(n)+c1≤cnlog(n)
c1≤clog(n)
E[Tquicksort(n)]=O(nlog(n))
So is Quicksort O(nlog(n))? No!
A stack of objects on top of one another.
trait Stack[A] {
def push(element: A): Unit
def top: A
def pop: A
}
class ListStack[A] extends Stack[A] {
val _store = new SinglyLinkedList()
def push(element: A): Unit =
_store.prepend(element)
def top: A =
_store.head
def pop: A =
_store.remove(0)
}
What's the runtime?
class ArrayBufferStack[A] extends Stack[A] {
val _store = new ArrayBuffer()
def push(element: A): Unit =
_store.append(element)
def top: A =
_store.last
def pop: A =
_store.remove(store.length-1)
}
What's the runtime?
Scala's Stack implementation is based on ArrayBuffer (ArrayDequeue); Keeping memory together is worth the overhead of amortized O(1).
Outside of the US, "queueing" is lining up.
trait Queue[A] {
def enqueue(element: A): Unit
def dequeue: A
def head: A
}
class ListQueue[A] extends Queue[A] {
val _store = new DoublyLinkedList()
def enqueue(element: A): Unit =
_store.append(element)
def head: A =
_store.head
def dequeue: A =
_store.remove(0)
}
What's the runtime?
Thought question: How could you use an array to build a queue?