Oct 26, 2022
(n∑i=1O(log(i)))+(n∑i=1O(log(n−i)))
<O(nlog(n))
After updating current, fixUp or fixDown.
If new current > parent, current moves up.
If new current < parent, current moves down.
How do we know where the value appears in the heap?
Input: Array
Output: Array reorderd to be a heap
O(log(n)∑i=1n2i⋅(i+1))
O(nlog(n)∑i=1i2i+12i)
O(nlog(n)∑i=1i2i)
O(n∞∑i=1i2i)
∑∞i=1i2i is known to converge to a constant.
O(n)
We can go from an unsorted array to a heap in O(n)
(but heap sort still requires nlog(n) for dequeueing)
An unordered collection of unique elements.
An unordered collection of non-unique elements.
Property | Seq | Set | Bag |
---|---|---|---|
Explicit Order | ✓ | ||
Enforced Uniqueness | ✓ | ||
Iterable | ✓ | ✓ | ✓ |
class TreeNode[T](
var _value: T,
var _left: Option[TreeNode[T]]
var _right: Option[TreeNode[T]]
)
class Tree[T] {
var root: Option[TreeNode[T]] = None // empty tree
}
trait Tree[+T]
case class TreeNode[T](
value: T,
left: Tree[T],
right: Tree[T]
) extends Tree[T]
case object EmptyTree extends Tree[Nothing]
def printTree[T](root: ImmutableTree[T], indent: Int) =
{
root match {
case TreeNode(v, left, right) =>
print((“ “ * indent) + v)
printTree(left, indent + 2)
printTree(right, indent + 2)
case EmptyTree =>
/* Do Nothing */
}
}
The height of a tree is the height of the root.
def height[T](root: Tree[T]): Int =
{
root match {
case EmptyTree =>
0
case TreeNode(v, left, right) =>
1 + Math.max( height(left), height(right) )
}
}
A Binary Tree over where each node stores a unique key, and a value's keys are ordered.
X1 partitions its children.
Goal: Find an item with key k in a BST rooted at root
def find[V: Ordering](root: BST[V], target: V): Option[V] =
root match {
case TreeNode(v, left, right) =>
if(Ordering[V].lt( target, v )){ return find(left, target) }
else if(Ordering[V].lt( v, target )){ return find(right, target) }
else { return Some(v) }
case EmptyTree =>
return None
}
What's the complexity? (how many times do we call 'find'?) O(d)
Goal: Insert an item with key k in a BST rooted at root
def insert[V: Ordering](root: BST[V], value: V): BST[V] =
node match {
case TreeNode(v, left, right) =>
if(Ordering[V].lt( target, v ) ){
return TreeNode(v, insert(left, target), right)
} else if(Ordering[V].lt( v, target ) ){
return TreeNode(v, left, insert(right, target))
} else {
return node // already present
}
case EmptyTree =>
return TreeNode(value, EmptyTree, EmptyTree)
}
What's the complexity? O(d)
Goal: Remove the item with key k from a BST rooted at root
What's the complexity? O(d)
Operation | Runtime |
---|---|
find | O(d) |
insert | O(d) |
remove | O(d) |
What's that in terms of n? O(n)
Does it need to be that bad?
How do we implement bags?
Idea 1: Just allow multiple copies (XL≤X1)
Idea 2: One copy, but store a count
trait Tree[+K, +V]
case class TreeNode[K, V](
key: K,
value: V,
left: Tree[K, V],
right: Tree[K, V]
) extends Tree[K, V]
case object EmptyTree extends Tree[Nothing, Nothing]
Balancing Trees