07_13 -- Linked Lists 2.key

Linked Lists, Part 2
data
next
1
data
next
2
null
Please take a handout &
Sit in row H or forward
CS 60, Spring 2016
Week 7, Class 13
Linked Lists, Part 2
data
next
1
data
next
2
null
Please take a handout &
Sit in row H or forward
CS 60, Spring 2016
Week 7, Class 13
How to create a new data structure
Three steps to think about
Operations: what can the data structure do?
• The operations are the interface: the public methods & fields.
• The operations can be specified in a Java interface, if there might be multiple ways to implement the same interface.
• An operation’s cost is sometimes an unofficial part of the interface.
A linked list’s operations
purpose
signature
cost
test for emptiness
booleanisEmpty()
the length of the list
intlength()
O(1)
O(1)
prepend an element (just like cons)
voidaddToFront(inti)
O(1)
true if i is in the list
booleancontains(inti)
returns the ith element of the list
int get(int i)
O(n)
O(n)
and, because we’re implementing our linked list in Java:
purpose
signature
cost
at least one constructor
List()
represent instance as String
StringtoString()
compare to another Object
booleanequals(Objectobj)
O(1)
O(n)
O(n)
compute a hash code
inthashCode()
O(n)
so, e.g., instances can be placed in a dictionary
How to create a new data structure
Three steps to think about
Operations: what can the data structure do?
• The operations are the interface: the public methods & fields.
• The operations can be specified in a Java interface, if there might be multiple ways to implement the same interface.
• An operation’s cost is sometimes an unofficial part of the interface.
Representation: how will the structure store its data?
• The representation is part of the implementation: the private fields.
• The fields are themselves data structures!
A linked list's representation
Drawing pictures is an excellent way to design the representation
List myList
List
purpose
the list element
signature
List
int data
int
data
List
1
List next
a reference to List next
the rest of the list
int
data
List
2
List next
int
data
List next null
How would you represent the empty list?
List
List
List
myList
myList null
data 0
List next null
int
Option 1: “sentinel” data value, e.g., 0
BUT: how is this different from a list with one element?
Option 2: List variable whose value is null
BUT: we can’t call methods, e.g., isEmpty.
3
A linked list's representation
We divide the representation into two classes: List and ListNode
List myList
List
purpose
a reference to the first element
of the list
signature
List
ListNode
ListNode head
head
ListNode
purpose
the list element
signature
ListNode
int data
a reference to ListNode next
the rest of the list
int
data
ListNode
next
ListNode
1
List empty
List
ListNode
head null
int
data
ListNode
next
ListNode
2
int
data
3
ListNode
next null
How to create a new data structure
Three steps to think about
Operations: what can the data structure do?
• The operations are the interface: the public methods & fields.
• The operations can be specified in a Java interface, if there might be multiple ways to implement the same interface.
• An operation’s cost is sometimes an unofficial part of the interface.
Representation: how will the structure store its data?
• The representation is part of the implementation: the private fields.
• The fields are themselves data structures!
Implementation: write the operations
• the bodies of the public methods.
• use the representation
• add private helper methods as needed
• re-use as much code as possible
A ListNode’s implementation
1. Package declaration
2. Class declaration
3. Method declarations
4. Tests!
package edu.hmc.cs.cs60;
/**
* Represents an element in a linked list
* (similar to a "pair" in Racket)
*/
public class ListNode {
/** the element */
public int data;
5. Field declarations
/** a reference to the next node */
public ListNode next;
6. Method implementations
public ListNode(int element, ListNode next) {
this.data = element;
this.next = next;
}
public fields?!
Yes, because the class has no behavior.
(We’ll come back to this.)
/**
* create a one-element list
* @param element
*/
public ListNode(int element) {
this(element, null);
}
ListNode
purpose
the list element
signature
int data
a reference to ListNode next
the rest of the list
}
A List’s implementation
1. Package declaration
2. Class declaration
3. Method declarations
4. Tests!
5. Field declarations
package edu.hmc.cs.cs60;
/**
* A linked list of integers
*/
public class List {
/**
* a reference to the first element of the list
* (null if the list is empty)
*/
private ListNode head;
/**
* creates an empty list
*/
public List() {
this.head = null;
}
6. Method implementations
/**
* @return true if the list has zero elements
*/
public boolean isEmpty() {
return (this.head == null);
}
List
purpose
a reference to the first element
of the list
signature
...
ListNode head
}
Let’s implement length
Here it is with a for loop.
/**
* @return the number of elements in the list
*/
public int length() {
int result = 0;
for (ListNode start = this.head; start != null; start = start.next) {
result++;
}
return result;
}
Write a version with a while loop.
List this
List
ListNode
head
ListNode
//picturescanhelp!
int
data
ListNode
next
ListNode
1
int
data
ListNode
next
ListNode
2
int
data
3
ListNode
next null
Let’s implement length
Two reasonable versions
/**
* @return the number of elements in the list
*/
public int length() {
int result = 0;
for (ListNode start = this.head; start != null; start = start.next) {
result++;
}
return result;
}
/**
* @return the number of elements in the list
*/
public int length() {
ListNode start = this.head;
int result = 0;
while (start != null) {
result++;
start = start.next;
}
return result;
}
What’s the Big-O?
where we’re interested in the behavior as the size of the list grows, and a “step” is an access to a ListNode field.
O(n)
A linked list’s operations
purpose
signature
cost
test for emptiness
booleanisEmpty()
the length of the list
intsize()
intlength()
O(1)
O(1)
prepend an element (just like cons)
voidaddToFront(inti)
O(1)
true if i is in the list
booleancontains(inti)
returns the ith element of the list
int get(int i)
O(n)
O(n)
and, because we’re implementing our linked list in Java:
purpose
signature
cost
at least one constructor
List()
represent instance as String
StringtoString()
compare to another Object
booleanequals(Objectobj)
O(1)
O(n)
O(n)
compute a hash code
inthashCode()
O(n)
so, e.g., instances can be placed in a dictionary
Big idea
We can trade space for time.
List
purpose
List
signature
a reference to the first element of ListNode head
the list
purpose
signature
a reference to the first element of ListNode head
the list
the number of
int size
elements in the list
/**
* @return the number of elements in the list
*/
public int length() {
ListNode start = this.head;
int result = 0;
while (start != null) {
result++;
start = start.next;
}
return result;
}
O(n)
/**
* @return the number of elements in the list
*/
public int length() {
return this.size;
}
O(1)
public List() {
this.head = null;
this.size = 0;
}
public boolean isEmpty() {
return this.length() == 0;
}
upload.wikimedia.org/wikipedia/commons/8/8c/Key_break.jpg
Let’s implement contains
With O(n) accesses to ListNode fields
/**
* Checks whether an element is in the list
* @param i the element to check
* @return true if the element is in the list
*/
public boolean contains(int i) {
for (ListNode node = this.head; node != null; node = node.next) {
if (node.data == i) {
return true;
}
}
return false;
}
List this
int
i
List
int
size
ListNode
head
0
//picturescanhelp!
3
ListNode
int
data
ListNode
next
ListNode
1
int
data
ListNode
next
ListNode
2
int
data
3
ListNode
next null
Let’s implement contains
With O(n) accesses to ListNode fields
/**
* Checks whether an element is in the list
* @param i the element to check
* @return true if the element is in the list
*/
public boolean contains(int i) {
for (ListNode node = this.head; node != null; node = node.next) {
if (node.data == i) {
return true;
}
}
return false;
}
List this
int
i
List
int
size
ListNode
head
0
//picturescanhelp!
3
ListNode
int
data
ListNode
next
ListNode
1
int
data
ListNode
next
ListNode
2
int
data
3
ListNode
next null
Let’s implement addToFront
With O(1) accesses to ListNode fields
/**
* Add an element to the front of the list (in constant time)
* @param i the new element
*/
public void addToFront(int i) {
ListNode newNode = new ListNode(i, this.head);
this.head = newNode;
this.size++;
}
List this
int
i
List
int
size
ListNode
head
//picturescanhelp!
3
ListNode
int
data
ListNode
next
0
ListNode
1
int
data
ListNode
next
ListNode
2
int
data
3
ListNode
next null
Let’s implement addToFront
With O(1) accesses to ListNode fields
/**
* Add an element to the front of the list (in constant time)
* @param i the new element
*/
public void addToFront(int i) {
ListNode newNode = new ListNode(i, this.head);
this.head = newNode;
this.size++;
}
List this
int
i
0
ListNode newNode
List
int
size
ListNode
head
ListNode
int
data
ListNode
next
//picturescanhelp!
3
ListNode
0
int
data
ListNode
next
ListNode
1
int
data
ListNode
next
ListNode
2
int
data
3
ListNode
next null
Let’s implement addToFront
With O(1) accesses to ListNode fields
/**
* Add an element to the front of the list (in constant time)
* @param i the new element
*/
public void addToFront(int i) {
ListNode newNode = new ListNode(i, this.head);
this.head = newNode;
this.size++;
}
List this
int
i
0
ListNode newNode
List
int
size
ListNode
head
ListNode
int
data
ListNode
next
//picturescanhelp!
3
ListNode
0
int
data
ListNode
next
ListNode
1
int
data
ListNode
next
ListNode
2
int
data
3
ListNode
next null
Let’s implement addToFront
With O(1) accesses to ListNode fields
/**
* Add an element to the front of the list (in constant time)
* @param i the new element
*/
public void addToFront(int i) {
ListNode newNode = new ListNode(i, this.head);
this.head = newNode;
this.size++;
}
List this
int
i
0
ListNode newNode
List
int
size
ListNode
head
ListNode
int
data
ListNode
next
//picturescanhelp!
4
ListNode
0
int
data
ListNode
next
ListNode
1
int
data
ListNode
next
ListNode
2
int
data
3
ListNode
next null
Implementing get
With O(n) accesses to ListNode fields
/**
* Get the element at the specified index
* @param i the index
* @return the ith element of the list
* @throws IllegalArgumentException if the index is invalid
*/
public int get(int i) {
if ( (i < 0) || (i >= this.length()) ) {
throw new IllegalArgumentException("Invalid index: " + i);
}
// advance to the ith element
ListNode currentNode = this.head;
int currentIndex = 0;
while (currentIndex < i) {
currentNode = currentNode.next;
currentIndex++;
}
return currentNode.data;
}
Implementing get with validation
With O(n) accesses to ListNode fields
/**
* Get the element at the specified index
* @param i the index
* @return the ith element of the list
* @throws IllegalArgumentException if the index is invalid
*/
public int get(int i) {
if ( (i < 0) || (i >= this.length()) ) {
throw new IllegalArgumentException("Invalid index: " + i);
}
// advance to the ith element
ListNode currentNode = this.head;
int currentIndex = 0;
while (currentIndex < i) {
currentNode = currentNode.next;
currentIndex++;
}
return currentNode.data;
}
Let’s implement getLast
With O(n) accesses to ListNode fields
/**
*
* @return the value of the last element of the list
* @throws IllegalArgumentException if the list is empty
*/
public int getLast() {
return this.get(this.length() - 1);
}
Q
Let’s implement getLast
With O(n) accesses to ListNode fields
/**
*
* @return the value of the last element of the list
* @throws IllegalArgumentException if the list is empty
*/
public int getLast() {
return this.get(this.length() - 1);
}
This seems unusual
The ListNode class has public fields, so anyone can access them.
package edu.hmc.cs.cs60;
package edu.hmc.cs.cs60;
/**
* Represents an element in a linked list
* (similar to a "cons cell" in Racket)
*/
public class ListNode {
/** the element */
public int data;
/**
* A linked list of integers
*/
public class List {
...
/** a reference to the next node */
public ListNode next;
public int get(int i) {
ListNode currentNode = this.head;
int currentIndex = 0;
while (currentIndex < i) {
currentNode = currentNode.next;
currentIndex++;
}
return currentNode.data;
}
public ListNode(int element, ListNode next) {
this.data = element;
this.next = next;
}
/**
* create a one-element list
* @param element
*/
public ListNode(int element) {
this(element, null);
}
}
...
}
A Java idiom: private, inner classes
The ListNode class definition is nested inside the List class!
package edu.hmc.cs.cs60;
package edu.hmc.cs.cs60;
/**
* Represents an element in a linked list
* (similar to a "cons cell" in Racket)
*/
private class ListNode {
/** the element */
public int data;
/**
* A linked list of integers
*/
public class List {
/**
* Represents an element in a linked list
* (similar to a "cons cell" in Racket)
*/
private class ListNode {
. . .
}
/** a reference to the next node */
public ListNode next;
public int get(int i) {
ListNode currentNode = this.head;
int currentIndex = 0;
while (currentIndex < i) {
currentNode = currentNode.next;
currentIndex++;
}
return currentNode.data;
}
public ListNode(int element, ListNode next) {
this.data = element;
this.next = next;
}
/**
* create a one-element list
* @param element
*/
public ListNode(int element) {
this(element, null);
}
}
...
}
A linked list’s operations
purpose
signature
cost
intlength()
O(1)
O(1)
✔
✔
prepend an element (just like cons)
voidaddToFront(inti)
O(1)
✔
true if i is in the list
booleancontains(inti)
returns the ith element of the list
int get(int i)
O(n)
O(n)
✔
✔
test for emptiness
booleanisEmpty()
the length of the list
and, because we’re implementing our linked list in Java:
purpose
signature
cost
at least one constructor
List()
represent instance as String
StringtoString()
compare to another Object
booleanequals(Objectobj)
O(1)
O(n)
O(n)
compute a hash code
inthashCode()
O(n)
so, e.g., instances can be placed in a dictionary
✔