Lecture 8 Notes

advertisement
Lecture 8
CS203
Implementation of Data Structures
2
In the last couple of weeks, we have covered various
data structures that are implemented in the Java
Collections Framework. As a working programmer,
you may often use these, and most of the rest of the
time, you will use various libraries that supply
alternatives. You will not often have to implement the
data structures yourself.
However, in order to understand how these structures
work at a lower level, we will cover implementation
this week.
Lists
3
A list stores data in sequential order. For example, a
list of students, a list of available rooms, a list of cities,
and a list of books, etc. can be stored using lists. The
common operations on a list are usually the following:
·
Retrieve an element from this list.
·
Insert a new element to this list.
·
Delete an element from this list.
·
Find how many elements are in this list.
·
Find if an element is in this list.
·
Find if this list is empty.
Two Ways to Implement Lists
4
There are two common ways to implement a list in Java.
Using arrays. One is to use an array to store the
elements. The array is dynamically created. If the
capacity of the array is exceeded, create a new larger
array and copy all the elements from the current array to
the new array.
Using a linked list. The other approach is to use a linked
structure. A linked structure consists of nodes. Each
node is dynamically created to hold an element. All the
nodes are linked together to form a list.
Two Ways to Implement Lists
5
Sample Code is linked from course page
ArrayList and LinkedList
6
For convenience, let’s name these two classes: MyArrayList
and MyLinkedList. These two classes have common
operations, but different data fields. The common operations
can be generalized in an interface or an abstract class. A good
strategy is to combine the virtues of interfaces and abstract
classes by providing both interface and abstract class in the
design so the user can use either the interface or the abstract
class whichever is convenient. Such an abstract class is known
as a convenience class.
MyArrayList
MyList
MyAbstractList
MyLinkedList
MyList Interface and MyAbstractList Class
«interface»
MyList<E>
7
+add(e: E) : void
Appends a new element at the end of this list.
+add(index: int, e: E) : void
Adds a new element at the specified index in this list.
+clear(): void
Removes all the elements from this list.
+contains(e: E): boolean
Returns true if this list contains the element.
+get(index: int) : E
Returns the element from this list at the specified index.
+indexOf(e: E) : int
Returns the index of the first matching element in this list.
+isEmpty(): boolean
Returns true if this list contains no elements.
+lastIndexOf(e: E) : int
Returns the index of the last matching element in this list.
+remove(e: E): boolean
Removes the element from this list.
+size(): int
Returns the number of elements in this list.
+remove(index: int) : E
Removes the element at the specified index and returns the
removed element.
+set(index: int, e: E) : E
Sets the element at the specified index and returns the
element you are replacing.
MyAbstractList<E>
#size: int
The size of the list.
#MyAbstractList()
Creates a default list.
#MyAbstractList(objects: E[])
Creates a list from an array of objects.
+add(e: E) : void
Implements the add method.
+isEmpty(): boolean
Implements the isEmpty method.
+size(): int
Implements the size method.
+remove(e: E): boolean
Implements the remove method.
MyList
MyAbstractList
Array List
8
Once an array is created, its size cannot be changed.
Nevertheless, you can still use array to implement
dynamic data structures. The trick is to create a new
larger array to replace the current array if the current
array cannot hold new elements in the list.
Initially, an array, say data of Object[] type, is created
with a default size. When inserting a new element into
the array, first ensure there is enough room in the
array. If not, create a new array twice the size of the
current one. Copy the elements from the current array
to the new array. The new array now becomes the
current array.
Insertion
9
Before inserting a new element at a specified index, shift
all the elements
after the index to the right and increase the list size by 1.
Before inserting
e at insertion point i
0
e0 e 1
e
After inserting
e at insertion point i,
list size is
incremented by 1
1
…
i
… ei-1 ei
i+1 …
k-1 k
…
ek-1 ek
ei+1
Insertion point
0
1
e0 e1
…
i
… ei-1 e
data.length -1
…shift…
i+1 i+2 …
ei
e inserted here
ei+1
…
k
k+1
ek-1 ek
data.length -1
Deletion
10
To remove an element at a specified index, shift all the
elements after the
index to the left by one position and decrease the list size by 1.
Before deleting the
element at index i
0
1
e0 e 1
…
i
… ei-1 ei
Delete this element
After deleting the
element, list size is
decremented by 1
0
1
e0 e1
…
i
… ei-1 ei+1
i+1 …
k-1 k
…
ek-1 ek
ei+1
…shift…
…
…
data.length -1
k-2 k-1
ek-1 ek
data.length -1
Implementing MyArrayList
11
MyAbstractList<E>
MyArrayList<E>
-data: E[]
+MyArrayList()
Creates a default array list.
+MyArrayList(objects: E[])
Creates an array list from an array of objects.
-ensureCapacity(): void
Doubles the current array size if needed.
Linked Lists
12
Since MyArrayList is implemented using an array, the methods get(int index)
and set(int index, Object o) for accessing and modifying an element through
an index and the add(Object o) for adding an element at the end of the list
are efficient.
However, the methods add(int index, Object o) and remove(int index) are
inefficient because it requires shifting potentially a large number of
elements. You can use a linked structure to implement a list to improve
efficiency for adding and removing an element anywhere in a list.
Nodes in Linked Lists
A linked list consists of nodes. Each node contains an
element, and each node is linked to its next neighbor.
Thus a node can be defined as a class, as follows:
13
head
Node 1
Node 2
element
element
next
next
class Node<E> {
E element;
Node next;
public Node(E o) {
element = o;
}
}
Node n
…
element
null
tail
Adding Three Nodes
14
The variable head refers to the first node in the list, and
the variable tail refers to the last node in the list. If the list
is empty, both are null. For example, you can create three
nodes to store three strings in a list, as follows:
Step 1: Declare head and tail:
Node<String> head = null;
Node<String> tail = null;
The list is empty now
Adding Three Nodes, cont.
15
Step 2: Create the first node and insert it to the list:
head = new Node<String>("Chicago");
tail = head;
After the first node is inserted
inserted
"Chicago"
head
tail
next: null
Adding Three Nodes, cont.
16
Step 3: Create the second node and insert it to the
list:
tail
tail.next = new Node<String>("Denver");
head
"Chicago"
"Denver"
next
next: null
tail
tail = tail.next;
head
"Chicago"
"Denver"
next
next: null
Adding Three Nodes, cont.
17
Step 4: Create the third node and insert it to the list:
tail
tail.next =
new Node<String>("Dallas");
head
"Chicago"
"Denver"
"Dallas"
next
next
next: null
tail
tail = tail.next;
head
"Chicago"
"Denver"
"Dallas"
next
next
next: null
Traversing All Elements in the List
18
Each node contains the element and a data field
named next that points to the next element. If the
node is the last in the list, its pointer data field next
contains the value null. You can use this property to
detect the last node. For example, you may write the
following loop to traverse all the nodes in the list.
Node<E> current = head;
while (current != null) {
System.out.println(current.element);
current = current.next;
}
MyLinkedList
19
MyAbstractList<E>
Node<E>
m
1
element: E
next: Node<E>
MyLinkedList<E>
-head: Node<E>
-tail: Node<E>
1
Link
MyLinkedList
+MyLinkedList()
Creates a default linked list.
+MyLinkedList(objects: E[])
Creates a linked list from an array of objects.
+addFirst(e: E): void
Adds the object to the head of the list.
+addLast(e: E): void
Adds the object to the tail of the list.
+getFirst(): E
Returns the first object in the list.
+getLast(): E
Returns the last object in the list.
+removeFirst(): E
Removes the first object from the list.
+removeLast(): E
Removes the last object from the list.
TestMyLinkedList
Run
Implementing addFirst(E o)
public void addFirst(E o) {
Node<E> newNode = new
Node<E>(o);
newNode.next = head;
head = newNode;
size++;
if (tail == null)
tail = head;
}
20
head
e0
tail
…
next
A new node
to be inserted
element
here
next
ei
ei+1
next
next
…
ek
null
(a) Before a new node is inserted.
head
tail
element
e0
next
next
New node inserted here
…
ei
ei+1
next
next
(b) After a new node is inserted.
…
ek
null
Implementing addLast(E o)
public void addLast(E o) {
if (tail == null) {
head = tail = new Node<E>(element);
}
else {
tail.next = new Node(element);
tail = tail.next;
}
head
size++;
}
…
21
tail
e0
ei
ei+1
next
next
next
…
ek
null
A new node
to be inserted
here
(a) Before a new node is inserted.
o
null
head
e0
next
tail
…
ei
ei+1
next
next
(b) After a new node is inserted.
…
ek
o
next
null
New node inserted here
Implementing add(int index, E o)
public void add(int index, E o) {
if (index == 0) addFirst(o);
else if (index >= size)
addLast(o);
else {
head
Node<E> current = head;
for (int i = 1; i < index; i++)
e0
current = current.next;
next
Node<E> temp = current.next;
current.next = new Node<E>(o);
(current.next).next = temp;
size++;
}
}
head
e0
22
…
current
temp
ei
ei+1
next
next
A new node
to be inserted
here
e
tail
…
ek
null
(a) Before a new node is inserted.
null
current
temp
ei
ei+1
next
next
…
next
A new node
is inserted in
the list
e
next
tail
…
ek
null
(b) After a new node is inserted.
Implementing removeFirst()
public E removeFirst() {
if (size == 0) return null;
else {
Node<E> temp = head;
head = head.next;
size--;
if (head == null) tail =
null;
return temp.element;
}
}
23
tail
head
e0
e1
next
next
…
Delete this node
ei
ei+1
next
next
…
null
(a) Before the node is deleted.
head
e1
next
tail
…
ei
ei+1
next
next
…
ek
ek
null
(b) After the first node is deleted
public E removeLast() {
if (size == 0) return null;
else if (size == 1)
{
Node<E> temp = head;
head = tail = null;
size = 0;
return temp.element;
}
else
{
Node<E> current = head;
for (int i = 0; i < size - 2; i++)
current = current.next;
Node temp = tail;
tail = current;
tail.next = null;
size--;
return temp.element;
}
}
Implementing
removeLast()
24
tail
current
head
…
e0
e1
next
next
ek-2
ek-1
ek
next
next
null
(a) Before the node is deleted.
Delete this node
tail
head
…
e0
e1
next
next
ek-2
ek-1
next
null
(b) After the last node is deleted
Implementing remove(int index)
public E remove(int index) {
if (index < 0 || index >= size) return null;
else if (index == 0) return removeFirst();
else if (index == size - 1) return
removeLast();
else {
Node<E> previous = head;
head
for (int i = 1; i < index; i++) {
element
previous = previous.next;
next
}
Node<E> current = previous.next;
previous.next = current.next;
size--;
head
return current.element;
}
element
}
next
25
…
previous
current
current.next
element
element
element
next
next
next
tai
…
elem
null
Node to be deleted
(a) Before the node is deleted.
…
previous
current.next
element
element
next
next
(b) After the node is deleted.
tai
…
elem
null
Time Complexity for ArrayList and LinkedList
Methods
MyArrayList/ArrayList
26
MyLinkedList/LinkedList
add(index: int, e: E)
O (1)
O (n )
O (1)
O (n )
clear()
O (1)
O (1)
contains(e: E)
O (n )
O (n )
get(index: int)
O(1)
O (n )
indexOf(e: E)
O (n )
O (n )
isEmpty()
O (1)
O (1)
lastIndexOf(e: E)
O (n )
O (n )
remove(e: E)
O (n )
O (n )
size()
O (1)
O (1)
remove(index: int)
O(n)
O (n )
set(index: int, e: E)
O(n)
O (n )
addFirst(e: E)
O(n)
O (1)
removeFirst()
O(n)
O (1)
add(e: E)
Circular Linked Lists
27

A circular, singly linked list is like a singly linked list, except that the pointer
of the last node points back to the first node.


Example uses: playing video/audio files repeatedly
ALT + TAB in Windows.
head
Node 1
Node 2
element
element
next
next
Node n
…
element
next
tail
Doubly Linked Lists
28
 A doubly linked list contains the nodes with two
pointers. One points to the next node and the other
points to the previous node. These two pointers
are conveniently called a forward pointer and a
backward pointer. So, a doubly linked list can be
traversed forward and backward.
head
Node 1
Node 2
element
element
next
next
null
null
previous
previous
Node n
…
element
tail
Circular Doubly Linked Lists
29

A circular, doubly linked list is doubly linked list, except that the forward
pointer of the last node points to the first node and the backward
pointer of the first pointer points to the last node.
head
Node 1
Node 2
element
element
next
next
next
previous
previous
previous
Node n
…
element
tail
Stacks
30
A stack can be viewed as a special type of list, where the elements are
accessed, inserted, and deleted only from the end, called the top, of the
stack.
Data1
Data2
Data3
Data2
Data1
Data1
Data3
Data2
Data2
Data1
Data1
Data1
Data3
Data2
Data1
Queues
31
A queue represents a waiting list. A queue can be viewed
as a special
type of list, where the elements are inserted into the end (tail) of the
queue, and are accessed and deleted from the beginning (head) of the
queue.
Data1
Data2
Data3
Data2
Data1
Data1
Data3
Data2
Data1
Data3
Data3
Data2
Data1
Data2
Data3
Implementing Stacks and Queues
32
Using an array list to implement Stack
Use a linked list to implement Queue
Since the insertion and deletion operations on a stack are made only at the
end of the stack, using an array list to implement a stack is more efficient
than a linked list. Since deletions are made at the beginning of the list, it is
more efficient to implement a queue using a linked list than an array list.
This section implements a stack class using an array list and a queue using
a linked list.
Design of the Stack and Queue Classes
33
There are two ways to design the stack and queue
classes:
 Using inheritance: You can define the stack class by extending
the array list class, and the queue class by extending the
linked list class.
ArrayList
GenericStack
LinkedList
GenericQueue
– Using composition: You can define an array list as a data field
in the stack class, and a linked list as a data field in the queue
class.
GenericStack
ArrayList
GenericQueue
LinkedList
Composition is Better
34
Both designs are fine, but using composition is
better because it enables you to define a complete
new stack class and queue class without inheriting
the unnecessary and inappropriate methods from
the array list and linked list.
MyStack and MyQueue
35
GenericStack<E>
GenericStack
-list: java.util.ArrayList<E>
An array list to store elements.
+GenericStack()
Creates an empty stack.
+getSize(): int
Returns the number of elements in this stack.
+peek(): E
Returns the top element in this stack.
+pop(): E
Returns and removes the top element in this stack.
+push(o: E): void
Adds a new element to the top of this stack.
+isEmpty(): boolean
Returns true if the stack is empty.
GenericQueue<E>
GenericQueue
-list: MyLinkedList<E>
+enqueue(e: E): void
Adds an element to this queue.
+dequeue(): E
Removes an element from this queue.
+getSize(): int
Returns the number of elements from this queue.
Priority Queue
A regular queue is a first-in and first-out data36 structure.
Elements are appended to the end of the queue and are
removed from the beginning of the queue. In a priority queue,
elements are assigned with priorities. When accessing
elements, the element with the highest priority is removed first.
A priority queue has a largest-in, first-out behavior. For
example, the emergency room in a hospital assigns patients
with priority numbers; the patient with the highest priority is
treated first.
MyPriorityQueue<E>
-heap: Heap<E>
+enqueue(element: E): void
Adds an element to this queue.
+dequeue(): E
Removes an element from this queue.
+getSize(): int
Returns the number of elements from this queue.
Heap
37
The term "Heap" is used in several different ways. The Priority
Queue example code uses a heap that adheres to this definition:
A heap is a binary tree (one in which each node has at most two
children) that with these two properties:
Order property:
If A is a parent node of B then the key of node A is ordered with
respect to the key of node B with the same ordering applying
across the heap. Either the keys of parent nodes are always
greater than or equal to those of the children and the highest key
is in the root node (max heap) or the keys of parent nodes are less
than or equal to those of the children and the lowest key is in the
root node (min heap).
Shape property:
1- All leaves are either at depth d or d-1 (for some value d).
2- All of the leaves at depth d-1 are to the right of the leaves at depth d.
3- (a) There is at most 1 node with just 1 child. (b) Any such child is the
left child of its parent, and (c) it is the rightmost leaf at depth d.
Heap
38
Representing a Heap
39
The tree is represented by values in a list or array.
For a node at position i, its left child is at position 2i+1 and
its right child is at position 2i+2, and its parent is (i-1)/2.
For example, the node for element 39 is at position 4, so
its left child (element 14) is at 9 (2*4+1), its right child
(element 33) is at 10 (2*4+2), and its parent (element 42)
is at 1 ((4-1)/2).
[0] [1] [2] [3] [4] [5] [6] [7] [8] [9] [10][11][12][13]
62
[10][11]
62 42 59 32 39 44 13 22 29 14 33 30 17 9
42
32
22
59
39
29
14
44
33
30 17
parent
13
9
left
right
Adding Elements to the Heap
40
Adding 3, 5, 1, 19, 11, and 22 to a heap, initially empty
3
5
5
3
3
(a) After adding 3
(b) After adding 5
19
5
1
(c) After adding 1
19
1
11
22
1
3
3
(d) After adding 19
(e) After adding 11
5
11
3
19
5
1
(f) After adding 22
Rebuild the heap after adding a new node
41
Adding 88 to the heap
22
22
11
3
19
5
1
88
(a) Add 88 to a heap
88
11
3
88
5
1
19
(b) After swapping 88 with 19
11
3
22
5
1
19
(b) After swapping 88 with 22
Removing the Root and Rebuild the Tree
42
Removing root 62 from the heap
62
42
32
22
59
39
29
14
44
33
30
13
17
9
Removing the Root and Rebuild the Tree
43
Move 9 to root
9
42
32
22
59
39
29
14
44
33
30
13
17
Removing the Root and Rebuild the Tree
44
Swap 9 with 59
59
42
32
22
9
39
29
14
44
33
30
13
17
Removing the Root and Rebuild the Tree
45
Swap 9 with 44
59
42
32
22
44
39
29
14
9
33
30
13
17
Removing the Root and Rebuild the Tree
46
Swap 9 with 30
59
42
32
22
44
39
29
14
30
33
9
13
17
Download