Lecture 8 Notes

Download Report

Transcript Lecture 8 Notes

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.
M yA rra yL ist
M yL ist
M yA bstractL ist
M yL inked L ist
MyList Interface and MyAbstractList Class
«interface »
M yL ist< E >
7
+ a dd (e: E ) : vo id
A p p en d s a n ew elem en t at th e en d of th is list.
+ a dd (ind ex: in t, e: E ) : vo id
A d d s a n ew elem en t at th e sp ecified in d ex in this list.
+ clea r(): vo id
R em oves all th e elem en ts from th is list.
+ con ta in s(e: E ): boo lean
R etu rn s tru e if this list con tain s th e elem en t.
+ g et(index: int) : E
R etu rn s th e elem en t from th is list at th e sp ecified ind ex.
+ ind exO f(e: E ) : in t
R etu rn s th e ind ex of th e first m atch ing elem en t in th is list.
+ isE m pty(): b oo lean
R etu rn s tru e if this list con tain s n o elem en ts.
+ la stInd exO f(e: E ) : in t
R etu rn s th e ind ex of th e last m atch in g elem en t in this list.
+ rem o ve(e: E ): bo olean
R em oves th e elem en t from this list.
+ size(): in t
R etu rn s th e nu m b er of elem en ts in this list.
+ rem o ve(in dex: int) : E
R em oves th e elem en t at th e sp ecified ind ex and retu rn s th e
rem ov ed elem en t.
+ set(ind ex: in t, e: E ) : E
S ets th e elem en t at th e sp ecified ind ex and retu rn s th e
elem en t you are rep lacin g.
M yA bstractL ist< E >
# size: in t
T h e size of th e list.
# M yA b stractList()
C reates a d efau lt list.
# M yA b stractList(ob jects: E [])
C reates a list from an array of ob jects.
+ add (e: E ) : void
Im p lem en ts th e ad d m eth od .
+ isE m p ty(): b oolean
Im p lem en ts th e isE m pty m eth od .
+ size(): in t
Im p lem en ts th e size m eth od .
+ rem ov e(e: E ): b oolean
Im p lem en ts th e rem ove m eth od .
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.
B efo re inserting
e at insertio n p o in t i
0
1
e0
e1
e
A fter in serting
e at insertio n p o in t i,
list size is
incre m e nted b y 1
…
…
i
i+ 1
…
k-1
e i-1 e i
e i+ 1
…
e k-1 e k
1
e0
e1
…
…
i
e i-1 e
d ata.length -1
… sh ift…
Insertio n p o int
0
k
i+ 1 i+ 2
…
k
ei
…
e k-1 e k
e inserted here
e i+ 1
k+ 1
d ata.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.
B efo re d eleting the
ele m e nt at ind e x i
0
1
e0
e1
…
…
i
i+ 1
…
k-1
e i-1 e i
e i+ 1
…
e k-1 e k
D elete this ele m e nt
A fter d eletin g the
ele m e nt, list size is
d ecrem e nted b y 1
0
1
e0
e1
…
…
i
e i-1 e i+ 1
… sh ift…
…
…
k
d ata.length -1
k-2 k-1
e k-1 e k
d ata.length -1
Implementing MyArrayList
11
M yAbstractList<E >
M yA rrayList< E >
-data: E []
+ M y A rra yL ist()
C reates a d efau lt arra y list.
+ M y A rra yL ist(o bjects: E [])
C reates a n arra y list fro m an array o f o bjects.
-en su reC a pacity(): v oid
D ou bles th e cu rrent arra y size if n eeded.
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
N ode 1
N ode 2
ele m e nt
ele m e nt
ne xt
ne xt
class Node<E> {
E element;
Node next;
public Node(E o) {
element = o;
}
}
N ode n
…
ele m e nt
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 em pty 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;
A fter th e first n od e is in serted
in serted
h ead
"C hicago"
n ext: nu ll
tail
Adding Three Nodes, cont.
16
Step 3: Create the second node and insert it to the
list:
tail
h ead
"C hicago"
"D en ver"
n ext
n ext: nu ll
tail.n ext = n e w N od e< S trin g> (" D e nv e r " );
tail
tail = tail.n ext;
h ead
"C hicago"
"D en ver"
n ext
n ext: nu ll
Adding Three Nodes, cont.
17
Step 4: Create the third node and insert it to the list:
tail
tail.next =
new Node<String>("Dallas");
h ead
"C hicago"
"D en ver"
"D allas"
n ext
n ext
n ext: nu ll
tail
tail = tail.next;
h ead
"C hicago"
n ext
"D en ver"
"D allas"
n ext
n ext: nu ll
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
M yA b stra ctL ist< E >
N o d e< E >
m
1
ele m e n t: E
ne xt: N o d e< E >
M yL inked L ist< E >
-h ead : N od e< E >
-tail: N od e< E >
1
L ink
MyLinkedList
+ M yLin k ed List()
C reates a d efau lt lin k ed list.
+ M yLin k ed List(ob jects: E [])
C reates a lin k ed list from an array of ob jects.
+ add First(e: E ): void
A d d s th e ob ject to th e h ead of th e list.
+ add Last(e: E ): void
A d d s th e ob ject to th e tail of th e list.
+ getFirst(): E
R etu rn s th e first object in th e list.
+ getLast(): E
R etu rn s th e last ob ject in th e list.
+ rem ov eFirst(): E
R em oves th e first object from th e list.
+ rem ov eLast(): E
R em oves th e last object from th e 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
h ead
e0
tail
…
n ext
A n ew n od e
to b e in serted
h ere
ei
e i+ 1
n ext
n ext
…
ek
n u ll
elem en t
n ext
(a) B efore a n ew n od e is in serted .
h ead
tail
elem en t
e0
n ext
n ext
…
ei
e i+ 1
n ext
n ext
N ew n od e in serted h ere
(b ) A fter a n ew n od e is in serted .
…
ek
n u ll
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;
}
h ead
size++;
}
e0
21
tail
…
n ext
ei
e i+ 1
n ext
n ext
…
ek
n u ll
A n ew n od e
to b e in serted
h ere
(a) B efore a n ew n od e is in serted .
o
n u ll
h ead
e0
n ext
tail
…
ei
e i+ 1
n ext
n ext
(b ) A fter a n ew n od e is in serted .
…
ek
o
n ext
n u ll
N ew n od e in serted h ere
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 {
h ead
Node<E> current = head;
for (int i = 1; i < index; i++)
e
current = current.next;
n ext
Node<E> temp = current.next;
current.next = new Node<E>(o);
(current.next).next = temp;
size++;
}
}
h ead
0
e0
22
…
cu rren t
tem p
ei
e i+ 1
n ext
n ext
A n ew n od e
to b e in serted
h ere
e
tail
…
ek
n u ll
(a) B efore a n ew n od e is in serted .
n u ll
cu rren t
tem p
ei
e i+ 1
n ext
n ext
…
n ext
A n ew n od e
is in serted in
th e list
e
n ext
tail
…
ek
n u ll
(b ) A fter a n ew n od e is in serted .
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
h ead
e0
e1
n ext
n ext
…
ei
e i+ 1
n ext
n ext
…
n u ll
D elete th is n od e
(a) B efore th e n od e is d eleted .
h ead
e1
n ext
tail
…
ei
e i+ 1
n ext
n ext
…
ek
ek
n u ll
(b ) A fter th e first n od e is d eleted
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
cu rren t
h ead
…
e0
e1
n ext
n ext
e k-2
e k -1
ek
n ext
n ext
n u ll
(a) B efore th e n od e is d eleted .
D elete th is n od e
tail
h ead
…
e0
e1
n ext
n ext
e k-2
e k -1
n ext
n u ll
(b ) A fter th e last n od e is d eleted
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;
h ead
for (int i = 1; i < index; i++) {
elem en t
previous = previous.next;
n ext
}
Node<E> current = previous.next;
previous.next = current.next;
size--;
h ead
return current.element;
}
elem en t
}
n ext
25
…
p reviou s
cu rren t
cu rren t.n ext
elem en t
elem en t
elem en t
n ext
n ext
n ext
tai
…
elem
n u ll
N od e to b e d eleted
(a) B efore th e n od e is d eleted .
…
p reviou s
cu rren t.n ext
elem en t
elem en t
n ext
n ext
(b ) A fter th e n od e is d eleted .
tai
…
elem
n u ll
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
N ode 1
N ode 2
ele m e nt
ele m e nt
ne xt
ne xt
N ode n
…
ele m e nt
ne xt
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
N ode 1
N ode 2
ele m e nt
ele m e nt
ne xt
ne xt
null
null
pre vio us
pre vio us
N ode n
…
ele m e nt
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
N ode 1
N ode 2
ele m e nt
ele m e nt
ne xt
ne xt
ne xt
pre vio us
pre vio us
pre vio us
N ode n
…
ele m e nt
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.
D ata1
D ata2
D ata3
D ata3
D ata2
D ata1
D ata1
D ata3
D ata2
D ata2
D ata1
D ata1
D ata1
D ata2
D ata1
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.
D ata1
D ata2
D ata3
D ata3
D ata2
D ata1
D ata1
D ata2
D ata1
D ata3
D ata3
D ata2
D ata1
D ata2
D ata3
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.
M yPriorityQ u eue< E >
-h eap: H ea p< E >
+ en qu eu e( elem ent: E ) : vo id
A dd s an elem ent to th is qu eu e.
+ dequ eu e(): E
R em ov es a n elem en t fr o m this qu eu e.
+ getSize(): int
R etu r n s th e nu m b er o f elem ents fr o m this qu eu e.
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