449 lines
11 KiB
Plaintext
449 lines
11 KiB
Plaintext
---
|
|
template: templates/cse250_2022_slides.erb
|
|
title: Linked Lists and Iterators
|
|
date: Sept 21, 2022
|
|
textbook: "Ch. 7"
|
|
---
|
|
|
|
<section>
|
|
<h4 class="slide_title">Seq[T]</h4>
|
|
<dl>
|
|
<dt>Array[T]</dt>
|
|
<dd><b>Pros:</b> $O(1)$ <tt>apply</tt>, <tt>update</tt></dd>
|
|
<dd><b>Cons:</b> $O(n)$ <tt>remove</tt>, <tt>insert</tt>, <tt>append</tt></dd>
|
|
|
|
<dt>ArrayBuffer[T]</dt>
|
|
<dd><b>Pros:</b> $O(1)$ <tt>apply</tt>, <tt>update</tt>, Amortized $O(1)$ <tt>append</tt></dd>
|
|
<dd><b>Cons:</b> $O(n)$ <tt>remove</tt>, <tt>insert</tt></dd>
|
|
|
|
<div class="fragment">
|
|
<dt>List[T] (linked list)</dt>
|
|
<dd><b>Pros:</b> ???</dd>
|
|
<dd><b>Cons:</b> ???</dd>
|
|
</div>
|
|
</dl>
|
|
</section>
|
|
|
|
<section>
|
|
<svg data-src="graphics/09b/linked-list-example.svg"/>
|
|
</section>
|
|
|
|
<section>
|
|
<h4 class="slide_title">mutable.List[T] : mutable.Seq[T]</h4>
|
|
|
|
<pre><code class="scala">
|
|
class SinglyLinkedList[T] extends Seq[T]
|
|
{
|
|
var head: Option[SinglyLinkedListNode[T]] = None
|
|
|
|
/* ... */
|
|
}
|
|
</code></pre>
|
|
|
|
<pre><code class="scala">
|
|
class SinglyLinkedListNode(
|
|
var value: T,
|
|
var next: Option[SinglyLinkedListNode[T]] = None
|
|
)
|
|
</code></pre>
|
|
</section>
|
|
|
|
<section>
|
|
<h4 class="slide_title">The <tt>mutable.Seq</tt> ADT</h4>
|
|
|
|
<dl style="font-size: 80%;">
|
|
<dt><tt>length: Int</tt></dt>
|
|
<dd>Count the number of elements in the seq.</dd>
|
|
|
|
<dt><tt>apply(<b>idx</b>: Int): A</tt></dt>
|
|
<dd>Get the element (of type A) at position <b>idx</b>.</dd>
|
|
|
|
<dt><tt>insert(<b>idx</b>: Int, <b>elem</b>: A): Unit</tt></dt>
|
|
<dd>Insert an element at position <b>idx</b> with value <b>elem</b>.</dd>
|
|
|
|
<dt><tt>remove(<b>idx</b>: Int): A</tt></dt>
|
|
<dd>Remove the element at position <b>idx</b> and return the removed value.</dd>
|
|
</dl>
|
|
</section>
|
|
|
|
<section>
|
|
<h4 class="slide_title">mutable.List[T] : mutable.Seq[T]</h4>
|
|
|
|
<p>Implementing <tt>length</tt>.</p>
|
|
|
|
<pre class="fragment"><code class="scala">
|
|
def length: Int =
|
|
{
|
|
var i = 0
|
|
var current = head
|
|
while(current.isDefined){ i += 1; curr = curr.get.next }
|
|
return i
|
|
}
|
|
</code></pre>
|
|
|
|
<p class="fragment"><b>Complexity: </b> $O(n)$</p>
|
|
</section>
|
|
|
|
<section>
|
|
<h4 class="slide_title">mutable.List[T] : mutable.Seq[T]</h4>
|
|
|
|
<p><b>Idea:</b> Keep track of the length</p>
|
|
|
|
<pre><code class="scala">
|
|
class SinglyLinkedList[T] extends Seq[T]
|
|
{
|
|
var head: Option[SinglyLinkedListNode[T]] = None
|
|
var length = 0
|
|
|
|
/* ... */
|
|
}
|
|
</code></pre>
|
|
<p class="fragment"><b>Complexity: </b> $O(1)$</p>
|
|
</section>
|
|
|
|
<section>
|
|
<h4 class="slide_title">mutable.List[T] : mutable.Seq[T]</h4>
|
|
|
|
<p><tt>apply(2)</tt>.</p>
|
|
|
|
<svg data-src="graphics/09b/linked-list-apply.svg"/>
|
|
|
|
</section>
|
|
|
|
<section>
|
|
<h4 class="slide_title">mutable.List[T] : mutable.Seq[T]</h4>
|
|
|
|
<p>Implementing <tt>apply</tt>.</p>
|
|
|
|
<pre><code class="scala">
|
|
def apply(idx: Int): T =
|
|
{
|
|
var current = head
|
|
for(i <- 0 until idx){
|
|
if(current.isEmpty) { throw IndexOutOfBoundsException(idx) }
|
|
current = current.get.next
|
|
}
|
|
if(current.isEmpty) { throw IndexOutOfBoundsException(idx) }
|
|
return current
|
|
}
|
|
</code></pre>
|
|
|
|
<p class="fragment"><b>Complexity: </b> $O(n)$ <span class="fragment">(or $\Theta(\texttt{idx})$)</span></p>
|
|
</section>
|
|
|
|
<section>
|
|
<h4 class="slide_title">mutable.List[T] : mutable.Seq[T]</h4>
|
|
|
|
<p><tt>insert(1, "D")</tt>.</p>
|
|
|
|
<svg data-src="graphics/09b/linked-list-insert.svg"/>
|
|
</section>
|
|
|
|
<section>
|
|
<h4 class="slide_title">mutable.List[T] : mutable.Seq[T]</h4>
|
|
|
|
<p>Implementing <tt>insert</tt>.</p>
|
|
|
|
<pre style="transform: scale(0.8); margin-top: -50px; margin-bottom: -50px;"><code class="scala">
|
|
def insert(idx: Int, value: T): Unit =
|
|
{
|
|
if(idx == 0){
|
|
head = Some( new SinglyLinkedListNode(value, head) )
|
|
} else {
|
|
var current = head
|
|
for(i <- 0 until idx){
|
|
if(curr.isEmpty) { throw IndexOutOfBoundsException(idx) }
|
|
curr = curr.get.next
|
|
}
|
|
curr.next = Some( new SinglyLinkedListNode(value, curr.next) )
|
|
}
|
|
length += 1
|
|
}
|
|
</code></pre>
|
|
|
|
<p class="fragment"><b>Complexity: </b> $O(n)$ <span class="fragment">(or $\Theta(\texttt{idx})$)</span></p>
|
|
</section>
|
|
|
|
<section>
|
|
<p>Let's use <tt>apply()</tt></p>
|
|
</section>
|
|
|
|
<section>
|
|
<h4 class="slide_title">mutable.List[T] : mutable.Seq[T]</h4>
|
|
|
|
<pre><code class="scala">
|
|
def sum(list: List[Int]): Unit =
|
|
{
|
|
val total: Int = 0
|
|
for(i <- 0 until list.length){ total += list(i) }
|
|
return total
|
|
}
|
|
</code></pre>
|
|
|
|
<p>What is the complexity?</p>
|
|
</section>
|
|
|
|
<section>
|
|
<h4 class="slide_title">mutable.List[T] : mutable.Seq[T]</h4>
|
|
|
|
<center>
|
|
$\sum_{i = 0}^{n-1} T_{apply}(i)$
|
|
<span class="fragment">
|
|
$=\sum_{i = 0}^{n-1} i$
|
|
</span>
|
|
</center>
|
|
|
|
<p class="fragment">
|
|
$$=\sum_{i = 0}^{n-1} \frac{(n-1)(n-1+1)}{2} = \frac{n^2 - n}{2} = \Theta(n^2)$$
|
|
</p>
|
|
|
|
<p class="fragment">Can we do better?</p>
|
|
</section>
|
|
|
|
<section>
|
|
<h4 class="slide_title">mutable.List[T] : mutable.Seq[T]</h4>
|
|
|
|
<pre><code class="scala">
|
|
def sum(list: List[Int]): Unit =
|
|
{
|
|
val total: Int = 0
|
|
val current = list.head
|
|
while(current.isDefined){
|
|
total += current.get.value
|
|
current = current.get.next
|
|
}
|
|
return total
|
|
}
|
|
</code></pre>
|
|
|
|
<p>What is the complexity?
|
|
<span class="fragment">$\sum_{i = 0}^{n-1} \Theta(1) = (n-1+1)\cdot \Theta(1) = \Theta(n)$</span>
|
|
</p>
|
|
</section>
|
|
|
|
<section>
|
|
<h4 class="slide_title">Access-by-Reference vs -by-Index</h4>
|
|
<p>Why does this work?</p>
|
|
|
|
<p class="fragment">What is the expensive part of <tt>apply</tt>?</p>
|
|
</section>
|
|
|
|
<section>
|
|
<h4 class="slide_title">Access-by-Reference vs -by-Index</h4>
|
|
<p>
|
|
Index → Value: $\Theta(idx)$<br>(access by index)
|
|
</p>
|
|
<p style="margin-top: 100px;" class="fragment">
|
|
<tt>SinglyLinkedListNode</tt> → Value: $\Theta(1)$<br>(access by reference)
|
|
</p>
|
|
</section>
|
|
|
|
<section>
|
|
<h4 class="slide_title">Iterator[T]</h4>
|
|
|
|
<dl>
|
|
<dt><tt>hasNext: Boolean</tt></dt>
|
|
<dd>Returns <tt>true</tt> if there are more items to retrieve.</dd>
|
|
|
|
<dt><tt>next:T</tt></dt>
|
|
<dd>Returns the next item to retrieve.</dd>
|
|
</dl>
|
|
</section>
|
|
|
|
<section>
|
|
<p>An iterator is:
|
|
<ul>
|
|
<li>A reference to an element of the collection</li>
|
|
<li>A way to get to the next<sup class="fragment" data-fragment-index=1 style="font-size: 50%">1</sup> element of the collection.</li>
|
|
</ul>
|
|
</p>
|
|
<p class="fragment" data-fragment-index=1>1: For some definition of next.</p>
|
|
</section>
|
|
|
|
<section>
|
|
<h4 class="slide_title">ListIterator[T] : Iterator[T]</h4>
|
|
|
|
<pre><code class="scala">
|
|
class ListIterator[T](
|
|
var current: Option[SinglyLinkedListNode[T]]
|
|
)
|
|
{
|
|
def hasNext: Boolean = current.isDefined
|
|
def next: T =
|
|
{
|
|
val ret = current.get.value
|
|
current = current.get.next
|
|
return ret
|
|
}
|
|
}
|
|
</code></pre>
|
|
</section>
|
|
|
|
<section>
|
|
<p>... back to LinkedLists</p>
|
|
|
|
<p class="fragment">How about positional operations:<br><tt style="font-size: 80%;">insertAfter(pos: SinglyLinkedListNode[T], value: T)</tt></p>
|
|
</section>
|
|
|
|
<section>
|
|
<h4 class="slide_title">mutable.List[T] : mutable.Seq[T]</h4>
|
|
|
|
<p><tt>insertAfter(pos, "D")</tt></p>
|
|
|
|
<svg data-src="graphics/09b/linked-list-insertPositional.svg"/>
|
|
</section>
|
|
|
|
<section>
|
|
<h4 class="slide_title">mutable.List[T] : mutable.Seq[T]</h4>
|
|
|
|
<p>Implementing a positional <tt>insertAfter</tt></p>
|
|
|
|
<pre><code class="scala">
|
|
def insertAfter(pos: SinglyLinkedListNode[T], value: T) =
|
|
{
|
|
pos.next = Some(
|
|
new SinglyLinkedListNode(value, pos.next)
|
|
)
|
|
length += 1
|
|
}
|
|
</code></pre>
|
|
|
|
<p>What is the complexity?
|
|
<span class="fragment">$\Theta(1)$</span>
|
|
</p>
|
|
|
|
</section>
|
|
|
|
<section>
|
|
<h4 class="slide_title">mutable.List[T] : mutable.Seq[T]</h4>
|
|
|
|
<p>How would you implement a positional <tt>remove</tt>?</p>
|
|
|
|
<pre class="fragment"><code class="scala">
|
|
def remove(pos: SinglyLinkedListNode[T]): T =
|
|
{
|
|
val prev = ??? /* Problem: Need element **before** pos. */
|
|
prev.next = pos.next
|
|
length -= 1
|
|
return pos.get.value
|
|
}
|
|
</code></pre>
|
|
</section>
|
|
|
|
<section>
|
|
<p><b>Idea:</b> Add a "backwards" pointer.</p>
|
|
</section>
|
|
|
|
<section>
|
|
<h4 class="slide_title">mutable.List[T] : mutable.Seq[T]</h4>
|
|
|
|
<pre><code class="scala">
|
|
class DoublyLinkedList[T] extends Seq[T]
|
|
{
|
|
var head: Option[DoublyLinkedListNode[T]] = None
|
|
var last: Option[DoublyLinkedListNode[T]] = None
|
|
var length = 0
|
|
|
|
/* ... */
|
|
}
|
|
</code></pre>
|
|
|
|
<pre><code class="scala">
|
|
class DoublyLinkedListNode[T](
|
|
var value: T,
|
|
var next: Option[DoublyLinkedListNode[T]] = None
|
|
var prev: Option[DoublyLinkedListNode[T]] = None
|
|
)
|
|
</code></pre>
|
|
</section>
|
|
|
|
<section>
|
|
<h4 class="slide_title">mutable.List[T] : mutable.Seq[T]</h4>
|
|
|
|
<p>How would you implement a positional <tt>insertAfter</tt>?</p>
|
|
|
|
<pre><code class="scala">
|
|
def insertAfter(pos: DoublyLinkedListNode[T], value: T) =
|
|
{
|
|
val newNode = new DoublyLinkedListNode(value, prev = Some(pos))
|
|
if(pos.next.isDefined){ pos.next.prev = Some(newNode)
|
|
newNode.next = pos.next }
|
|
else { last = newNode
|
|
newNode.next = None }
|
|
pos.next = Some(newNode)
|
|
length += 1
|
|
}
|
|
</code></pre>
|
|
</section>
|
|
|
|
<section>
|
|
<h4 class="slide_title">mutable.List[T] : mutable.Seq[T]</h4>
|
|
|
|
<p>How would you implement a positional <tt>remove</tt>?</p>
|
|
|
|
<pre class="fragment"><code class="scala">
|
|
def remove(pos: DoublyLinkedListNode[T]): T =
|
|
{
|
|
if(pos.prev.isDefined){ pos.prev.next = pos.next }
|
|
else { head = pos.next }
|
|
|
|
if(pos.next.isDefined){ pos.next.prev = pos.prev }
|
|
else { tail = pos.prev }
|
|
|
|
length -= 1
|
|
return pos.get.value
|
|
}
|
|
</code></pre>
|
|
</section>
|
|
|
|
<section>
|
|
<table style="font-size: 50%">
|
|
<tr>
|
|
<th>Operation</th>
|
|
<th>Array[T]</th>
|
|
<th>ArrayBuffer[T]</th>
|
|
<th>List[T] (Index)</th>
|
|
<th>List[T] (Ref)</th>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td><tt>apply(i)</tt></td>
|
|
<td>$\Theta(1)$</td>
|
|
<td>$\Theta(1)$</td>
|
|
<td>$\Theta(i)$, $O(n)$</td>
|
|
<td>$\Theta(1)$</td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td><tt>update(i, value)</tt></td>
|
|
<td>$\Theta(1)$</td>
|
|
<td>$\Theta(1)$</td>
|
|
<td>$\Theta(i)$, $O(n)$</td>
|
|
<td>$\Theta(1)$</td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td><tt>insert(i, value)</tt></td>
|
|
<td>$\Theta(n)$</td>
|
|
<td>$O(n)$, ...</td>
|
|
<td>$\Theta(i)$, $O(n)$</td>
|
|
<td>$\Theta(1)$</td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td><tt>remove(i, value)</tt></td>
|
|
<td>$\Theta(n)$</td>
|
|
<td>$\Theta(n-i)$, $O(n)$</td>
|
|
<td>$\Theta(i)$, $O(n)$</td>
|
|
<td>$\Theta(1)$</td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td><tt>append(i)</tt></td>
|
|
<td>$\Theta(n)$</td>
|
|
<td>$O(n)$, Amortized $\Theta(1)$</td>
|
|
<td>$\Theta(i)$, $O(n)$</td>
|
|
<td>$\Theta(1)$</td>
|
|
</tr>
|
|
</table>
|
|
</section> |