0% found this document useful (0 votes)
49 views

Tutorial 2

This document discusses positional lists and their implementation as linked lists. It defines positional lists as an abstract data type consisting of a linear sequence of positions that each store an element. A linked positional list implementation stores these positions as nodes in a doubly-linked list. Students are given an exercise to implement the positional list ADT using this linked list implementation. The document provides guidance on using sentinels and helper methods to simplify the implementation.

Uploaded by

朱宸烨
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
49 views

Tutorial 2

This document discusses positional lists and their implementation as linked lists. It defines positional lists as an abstract data type consisting of a linear sequence of positions that each store an element. A linked positional list implementation stores these positions as nodes in a doubly-linked list. Students are given an exercise to implement the positional list ADT using this linked list implementation. The document provides guidance on using sentinels and helper methods to simplify the implementation.

Uploaded by

朱宸烨
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 7

COMP2123/2823/9123 Week 2 Tutorial

Languages and their naming conventions There is a well-known convention in Java, where method
names (and variable names) are in camel case; that is the words are concatenated, and each (except
the first) starts with a Capital letter. In Python, the convention is to name methods and variables where
words are separated by an underscore character. So a Java method might be getElement, and in
Python we would call this get_element. In this course, we will often adopt the Java convention
in our explanations; but when you are writing code, you should follow the convention of whatever
language you are using. So to do an exercise in Python, you would write a method get_element
even if our instructions speak about getElement.

1 Positional Lists

1.1 Positional List ADT


A position is an abstract data type (ADT) which models the abstract notion of the place where a
single object is stored within a data structure.

A positional list is an ADT which consists of a linear sequence of positions, each of which stores an
element. When the structure of the positional list is changed (for example, by insertion of a new item
somewhere), the positions continue to store the same element, unlike an index.

1.2 ADT Method Summary


The positional list ADT methods are summarized here for your convenience, though it is recom-
mended that you consult the textbook for more details.

Positions support the following method:

getElement(): Return the element stored at this position.

Positional lists support the following traversal methods:

first(): Return the position of the first element.


last(): Return the position of the last element.
before(p): Return the position of the element preceding the one at position p.
after(p): Return the position of the element following the one at position p.

1
COMP2123/2823/9123

And the following update methods:

insertBefore(p, e): Insert a new element e into the list before position p.
insertAfter(p, e): Insert a new element e into the list after position p.
remove(p): Remove from the list the element at position p.

1.3 Linked Positional Lists


A linked positional list is an implementation of this ADT, which allows us to store lists in non-
adjacent areas of memory, in structures called nodes. Each node encodes a single position. In a
doubly-linked list, each node stores a reference to both the previous and following node in the list.

In an upcoming exercise you will implement the positional list ADT methods for a linked positional
list, but first we will warm up with an exercise on paper.

1.4 Paper Exercise: Working with Nodes


Below is a diagram of five nodes arranged to form a doubly-linked list.

p1 p2 p3

null "a" "b" "c" "d" "e" null

Here each three-cell rectangle represents a node object. From left to right, the cells represent:

• A reference to the previous node;

• The element stored by the node (in this case, each node stores a one-character string);

• A reference to the next node.

We also have three reference variables, p1, p2, and p3, which refer to different nodes in the list.

Consider the following sequence of statements given in pseudocode below. Show the output that is
printed when this pseudocode is applied to the linked list given in the diagram above, and draw the
resulting state at the end of this sequence. (Be warned, the resulting state is not a nicely-formed
doubly-linked list, unlike the state at the beginning.)

Data Structures and Algorithms Page 2 of 7


COMP2123/2823/9123

1 print(p2.getElement())
2 print(p3.getNext().getElement())
3 p1.setNext(p3.getNext())
4 p2.getPrev().setElement("f")
5 p2.getNext().getNext().setNext(p2.getNext())
6 p1.setPrev(new Node(p1,"g",p2))
7 p2 = p3
8 print(p1.getNext().getPrev().getElement())

Attempt the exercise yourself on paper, and also go over the solution with your tutor in class.

1.5 Assessed Exercise: Implementing a Linked Positional List [Due: Noon on


Wednesday March 21] [Value: 1 point]
In this exercise you will implement the positional list ADT, as a doubly linked list.

A scaffold is provided in the workspace for this exercise in the Assessments section on Ed. It contains
the following:

• Position. In Java, this is an interface; In Python it is an abstract base class. This describes
the methods required by the position ADT. You should not edit this interface or class.
• PositionalList. In Java, this is an interface; In Python it is an abstract base class. This
describes the methods required by the positional list ADT. You should not edit this interface or
class.
• Node class. In Java this implements the Position interface and is a nested class within
the LinkedPositionalList skeleton code. In Python it inherits from the Position abstract base
class, and has been coded in the same file as LinkedPositionalList skeleton code. This has
been implemented for you, it includes some suitable methods and variables to be used in a
doubly-linked list implementation of PositionalList. You do not need to edit this class.
• LinkedPositionalList class. In Java this implements the PositionalList interface,
while in Python it inherits from the PositionalList abstract base class. The fields and
constructor have been implemented for you, but you will need to implement all of the other
methods that we have defined in the skeleton code.

You need to implement all of the methods defined by the positional list ADT:

first(): Return the position of the first element.


last(): Return the position of the last element.
before(p): Return the position of the element preceding the one at position p.
after(p): Return the position of the element following the one at position p.
insertBefore(p, e): Insert a new element e into the list before position p.
insertAfter(p, e): Insert a new element e into the list after position p.
remove(p): Remove from the list the element at position p.

As well as some extra methods:

Data Structures and Algorithms Page 3 of 7


COMP2123/2823/9123

size(): Return the total number of elements in the list.


isEmpty(): Return the position of the last element.
insertFirst(e): Insert a new element e at the front of the list.
insertLast(e): Insert a new element e at the back of the list.
set(p, e): Replace the element stored at position p with element e. Return the
replaced element.

When you have a lot of methods that do similar things, you can often make your code much easier to
write and maintain by using a helper method. In this exercise, you might find it very useful to create
a method which inserts an element between two nodes:

insertBetween(p, q, e): Insert a new element e into the list between position p and
position q.

1.6 Discussion
Sentinels: header and trailer Similar to the textbook and lecture slides, our implementation keeps
sentinel nodes at each end of the list (look in the constructor to see how this is happening). These
sentinel nodes are not part of the list of elements being stored, so don’t count them as part of the size,
and your code should never return them. If a method should return a position outside list, then return
null instead.

Using sentinels can make code much simpler, because they remove lots of special cases that we’d
otherwise have to code for. For example, inserting a new element before the first element of the list
can follow the same algorithm as inserting one in the middle, instead of being a special case we have
to code for.
Error checking In Section 7.3.3 of the textbook Data Structures and Algorithms in Java (6th edition)
by Goodrich, Tamassia and Goldwasser you can find definitions, and implementations of, very similar
interfaces to the ones we are using in this week’s tutorials. However, we have simplified the interfaces
somewhat, so that our implementations can also be a bit simpler than the corresponding ones in the
textbook.
The simplifications we’ve made to the interface involve removing all the error checking (e.g. are
the Positions part of the right type of list, are the Positions being used still valid, etc.) It is good
programming practice to have these sorts of checks. Because we are much more interested in studying
how the data structure works, rather than engineering highly defensive code, we have decided to focus
our attention on the former in this tutorial!

1.7 Further reading


For more detailed information on positional lists, see section 2.2.2 (pages 63-67) of Algorithm Design
and Applications.

Data Structures and Algorithms Page 4 of 7


COMP2123/2823/9123

2 Stacks and Queues

2.1 ADT definitions


A stack is an ADT which consists of a collection of objects which can be inserted and removed
according to a Last-In First-Out (LIFO) principle.

A queue is an ADT which consists of a collection of objects which can be inserted and removed
according to a First-In First-Out (FIFO) principle.

2.2 ADT Method Summaries


The stack and queue ADT methods are summarized here, though it is recommended that you consult
the textbook if you need more explanations.

Stacks support the following methods:

push(o): Insert object o at the top of the stack.


pop(): Remove from the stack and return the object at the top of the stack.
peek(): Return the object at the top of the stack, without removing it.

Queues support the following methods:

enqueue(o): Insert object o at the rear of the queue.


dequeue(): Remove and return from the queue the object at the front.
peek(): Return the object at the front of the queue, without removing it.

2.3 Using Stacks and Queues


Stacks and queues form the basic building blocks of many useful algorithms and data structures in
computer science. Although simple, they are very efficient, and provide powerful properties when
used together.

For the next exercises, you may use the java.util.Deque type from the Java Collections Framework
(JCF) if you are using Java, and the collections.deque type if you are using Python. Note that
java.util.Deque is only an interface. A suitable implementing class of Deque is java.util.ArrayDeque.

A deque (pronounced "deck", short for "double-ended queue") can be thought of as "simultaneously
a stack and a queue". Since they admit similar implementations, collections libraries often forgo
separate stack and queue implementations in favour of a deque implementation. Despite this, it tends
to be more conceptually useful to invoke the stack and queue ADTs when designing algorithms and
systems.

In order to use a deque implementation as a stack, consult the following table:

Data Structures and Algorithms Page 5 of 7


COMP2123/2823/9123

Stack ADT java.util.Deque Python collections.deque


push(o) push(o) append(o)
pop() pop() pop()
peek() peek() s[-1], for stack s

And to use a deque as a queue, consult the following table:

Queue ADT java.util.Deque Python collections.deque


enqueue(o) addLast(o) append(o)
dequeue() removeFirst() popleft()
peek() getFirst() q[0], for queue q

2.4 Exercise: Palindromes


A word is said to be a palindrome if it is spelt the same forwards as backwards. For example, ’eye’
and ’racecar’ are palindromes, but ’hello’ and ’book’ are not.

You can find the workspace for this exercise in the Challenges section on Ed. Implement the method
isPalindrome using a stack and/or queue to check whether a given word is a palindrome (case-
sensitive). Use the Python or Java scaffold provided.

The main function of both implementations are already written for you. The main function reads in
every line of the standard input, calls the method you have to complete, and then prints out whether
each line is a palindrome or not.

2.5 Exercise: Palindromic Sentences


A palindromic sentence is a string that is spelt the same forwards as backwards, considering only
the alphabetic letters (in particular, ignoring spaces, punctuation and case). For example, "Madam,
I’m Adam" is a palindromic sentence, as is "Never odd or even". "Hello world" is not a palindromic
sentence.

You can find the workspace for this exercise in the Challenges section on Ed. Using your isPalindrome
from the Palindromes exercise, implement the method isPalindromeSentence. Use the Python
or Java scaffold provided.

The main function of both implementations are already written for you. The main function reads in
every line of the standard input, calls the method you have to complete, and then prints out whether
each line is a palindrome sentence or not.

2.6 Exercise: Fibonacci


Recall that the Fibonacci numbers can be calculated using a recursive algorithm. However, the lan-
guage runtimes of many programming languages, such as Java, have limitations which effectively

Data Structures and Algorithms Page 6 of 7


COMP2123/2823/9123

limit the depth to which recursive method calls can go, at which point a java.lang.StackOverflowError
would be thrown. Instead of writing recursive code, which is then executed by the language runtime
using an internal stack, the programmer can write the function iteratively, accessing an explicit stack
within their code.

The Fibonacci number sequence is defined by:

f (0) = 0, f (1) = 1, f (n) = f (n − 1) + f (n − 2) for all n > 1.

You can find the workspace for this exercise in the Challenges section on Ed. Implement the getSequence
method in either the Python or Java scaffold provided, without using recursion. It should return a stack
containing the Fibonacci numbers from f (0) up to and including f (n), with f (n) as the top element.
For example:
getSequence(3) should return the stack containing (0, 1, 1, 2) (in that order; bottom to top)
getSequence(5) should return a stack containing (0, 1, 1, 2, 3, 5)

2.7 Further reading


For more detailed information on stacks and queues, see the following sections of Algorithm Design
and Applications:

• For stacks: section 2.1.1, pages 53-56.

• For queues: section 2.1.2, pages 57-59.

Data Structures and Algorithms Page 7 of 7

You might also like