Loading [MathJax]/jax/output/SVG/config.js

Abstract

These lecture notes introduce the notion of ordered data structures, i.e. some basic containers of elements that can be used to organise data in a specific way. The historic hero introduced in these notes is Donald Knuth, who has been one of the most relevant scientists and contributors on the topic of the formal analysis of the computational complexity of algorithms.

Copyright notice. This work is licensed under a Creative Commons Attribution 4.0 International License. You are free to share (i.e. copy and redistribute the material in any medium or format) and adapt (e.g. remix, transform, and build upon the material) for any purpose, even commercially, under the following terms: attribution, i.e. you must give appropriate credit, provide a link to the license, and indicate if changes were made. You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use. The licensor cannot revoke these freedoms as long as you follow the license terms.

Historic hero: Donald Knuth

Donald Ervin Knuth is one of the most important Computer Scientist of the past 50 years. He is one of the most important contributors to the theoretical and practical development of the analysis of the computational complexity of algorithms, that we have introduced in the previous lecture. Among his huge list of contributions, there are especially two works that deserve a particular attention in this context: its series of monographs about algorithms and their analysis entitled The Art of Computer Programming, and the TeX typesetting system for writing academic documents, that has been used to write the aforementioned series and it is one of the most used tools for communicating and publishing scientific results in academia.

img/knuth.jpg

Figure 1. Donal Knuth in 2005. Picture by Jacob Appelbaum, source: https://commons.wikimedia.org/wiki/File:KnuthAtOpenContentAlliance.jpg.

According to several experts, the series of monographs he has written is one of the most comprehensive works to programming and algorithmics. The project started in 1962 as a twelve-chapter book, and then split in a series of seven volumes, of which only the first four have been published so far – while the others are still in writing. The first volume is entirely dedicated to the mathematical foundations for allowing a formal analysis of algorithms, and to a comprehensive introduction of all the basic data structures.

Data structures are the possible ways in which we organise the information to be processed and returned by a computer, so as it can be accessed and modified in an efficient and computational manner. In practice, a data structure is a sort of bucket where we can place some information, that provides some methods to add and retrieve pieces of such information. The most simple data structures are lists and, as such, they are the first data structures introduced in Knuth's first volume of The Art of Computer Programming [1]. They are, probably, one of the most important building blocks of algorithms, since they have plenty of applications.

Ordered data structures

In this lectures, we will introduce three specific data structures, that are discussed in details in the following sections, i.e.: lists, stacks, and queues. Their main characteristic, in addition of being among the most basic and used data structures in algorithms (and, more concretely, in programs), is that the order in which their elements have been added matters. In fact, they interlink all their elements so as to create an ordered chain of elements which allows us to have a clear prediction of the behaviour of the addition and removal operations they make available.

Lists

A list is a countable sequence of ordered and repeatable elements. It is countable because there is a proper way of knowing the length of the list (i.e. how many elements it contains). In particular, ThyMopani makes available a particular support algorithm, i.e. def len(countable_object), that takes a countable element as input (like a list), and returns the number of elements that it contains. Its elements are ordered because they are placed in the list in a specific order, which is preserved even if we add or remove particular elements. Its elements are also repeatable since they may appear more than one time in the list.

Of course, there exist several real examples of such abstract lists in real-life objects. For instance, in Figure 2, we show a table of content of a book and a bibliographic reference list of an article. Both of them are concrete objects that are built starting from the abstract notion of a list. 

img/list.png

Figure 2. Two examples of a list in real objects: the table of contents of a book (left), and a bibliographic reference list in a research paper (right). Left picture by Marcus Holland-Moritz, source: https://www.flickr.com/photos/mhx/4347706564/. Right screenshot, source: https://doi.org/10.7717/peerj-cs.132.

In ThyMopani, a new list can be instantiated by means of the constructor list(). For instance, my_first_list = list() will create an empty list and associates it to the variable my_first_list. A list can be seen as a left-to-right sequence of elements, where the left-most identifies the head of the list, while the last one represents the tail of the list. Several operations can be done on lists, in particular:

In Listing 1, we show some examples of the use of lists in ThyMopani. In these examples, we describe with natural language comments (introduced by a #) the various aspects related to the creation and modification of lists.

​my_first_list = list()  # this creates a new list

my_first_list.append(34)  # these two lines add two numbers
my_first_list.append(15)  # to the list in this precise order
# currently my_first_list contains two elements:
# list([ 34, 15 ])

my_first_list.append("Silvio")  # a list can contains element of any kind
# now my_first_list contains: 
# list([34, 15, "Silvio"])

my_first_list.remove(34)  # it removes the first intance of the number 34
# my_first_list became: 
# list([15, "Silvio"])

my_first_list.extend(my_first_list)  # it add again all the elements in my_first_list to the list itself
# current status of my_first_list: 
# list([15, "Silvio", 15, "Silvio"])

my_first_list_len = len(my_first_list) # it stores 4 in my_first_list_len
Listing 1. How ThyMopani allows us to create and handle lists – with numbers and strings.

Stacks

A stack is a kind of list seen from a particular perspective, i.e. from bottom to top, and with a specific set of operations. Figure 3 shows two different example of stacks in real-life objects. We have a stack of chairs (left) and a pile of books (right). The main characteristic of the elements of this structure is that they follow a last in first out strategy (LIFO) for addition and removal. Basically, it means that the last element inserted in the structure is placed in the top of the stack and, thus, it is also the first one that will be removed when requested. In addition, to obtain the element placed, for instance, in the middle of the stack, we necessarily need to remove all the elements that have been added after such middle element, from the most recent elements to the eldest ones.

img/stack.png

Figure 3. Two examples of a stack of real objects: a stack of chairs (left), and a pile of books (right). Left picture by Jeremy Brooks, source: https://www.flickr.com/photos/jeremybrooks/16410797960/. Right picture by Cary Lee, source: https://www.flickr.com/photos/the1andonlycary/3310345438/.

In ThyMopeni, a new stack can be instantiated by means of the constructor deque(). For instance, my_first_stack = deque() will create an empty stack and associates it to the variable my_first_stack. Three main operations can be done on stacks, in particular:

In Listing 2, we show some examples of the use of stacks in ThyMopani. In particular, we organise in a stack some books (actually, their titles) written by Neil Gaiman.

​my_first_stack = deque()  # this creates a new stack

my_first_stack.append("Good Omens")  # these two lines add two books
my_first_stack.append("Neverwhere")  # to the stack in this precise order (this is on top)
# currently my_first_stack contains two elements:
# "Neverwhere"])
# deque(["Good Omens",

my_first_stack.append("The Name of the Rose")  # add a new book
# now my_first_stack contains:
# "The Name of the Rose")]
# "Neverwhere",
# deque(["Good Omens",

my_first_stack.pop()  # it removes the element on top of the stack, i.e. "The Name of the Rose"
# my_first_stack became:
# "Neverwhere"])
# deque(["Good Omens",

​my_second_stack = deque()  # this creates a new stack
my_second_stack.append("American Gods")   # these two lines add two books
my_second_stack.append("Fragile Things")  # to the stack in this precise order (this is on top)
# currently my_second_stack contains two elements:
# "Fragile Things"])
# deque(["American Gods",

my_first_stack.extend(my_second_stack)  # it add all the elements in my_second_stack on top of my_first_stack
# current status of my_first_stack:
# "Fragile Things"])
# "American Gods",
# "Neverwhere",
# deque(["Good Omens",
Listing 2. How ThyMopani allows us to create and handle stacks – with book titles.

Queue

A queue is a kind of list seen by another perspective, i.e. from left to right, and with a specific set of operations. Figure 4  shows two different example of queues in real-life objects. We have a queue of children (left) and a line of cabs (right). The main characteristic of the elements of this structure is that they follow a first in first out strategy (FIFO) for addition and removal. Basically, it means that the first element inserted in the structure is placed in the left-most part of the queue and, thus, it is also the first one that will be removed when requested. Similar to stacks, even in queues it is necessary to remove all the elements that have been added before a certain target element – i.e. from the eldest elements to the most recent ones – to obtain it.

img/queue.png

Figure 4. Two examples of a queue of real objects: a queue of children waiting their turn for playing with a slide (left), and a cab wait line (right). Left picture by Prateek Rungta, source: https://www.flickr.com/photos/rungta/4409560365/. Right picture by Lynda Bullock, source: https://www.flickr.com/photos/just1snap/5141019486/.

In ThyMopeni, a new stack can be instantiated by means of the constructor deque(), which is the same used for stasks. Thus, it is the way one uses it that classifies the object instantiated as a stack or a queue. Thus, as before, my_first_queue = deque() will create an empty queue and associates it to the variable my_first_queue. Three main operations can be done on stacks, in particular:

In Listing 3, we show some examples of the use of queues in ThyMopani. In particular, we organise in a queue some (fictional) people that are waiting in the library so as to borrow some books.

​​my_first_queue = deque()  # this creates a new queue

my_first_stack.append("Vanessa Ives")  # these two lines add two people
my_first_stack.append("Mike Wheeler")  # to the stack in this precise order (this is on top)
# currently my_first_queue contains two elements:
# deque(["Vanessa Ives", "Mike Wheeler")

my_first_queue.append("Eleven")  # add a new person
# now my_first_queue contains:
# deque(["Vanessa Ives", "Mike Wheeler", "Eleven"])

my_first_stack.popleft()  # it removes the first element added in the queue, i.e. "Vanessa Ives"
# my_first_stack became:
# deque(["Mike Wheeler", "Eleven"])

​my_second_queue = deque()  # this creates a new queue
my_second_queue.append("Micheal Walsh")   # these two lines add two people
my_second_queue.append("Lawrence Cohen")  # to the queue in this precise order
# currently my_second_queue contains two elements:
# deque(["Michael Walsh", "Lawrence Cohen"])

my_first_queue.extend(my_second_queue)  # it add all the elements in my_second_queue on the right of my_first_queue
# current status of my_first_queue:
# deque(["Mike Wheeler", "Eleven", "Michael Walsh", "Lawrence Cohen"])
Listing 3. How ThyMopani allows us to create and handle queues – with people.

Exercises

  1. Write a pseudocode in ThyMopani so as to create a list with the following elements ordered alphabetically: ​Harry​, ​Draco​, ​Hermione​, ​​Ron​, ​Severus​.

  2. Consider to have a stack obtained by processing, one by one, the elements included in the previous list, i.e. my_stack = deque(["Draco", "Harry", "Hermione", "Ron", "Severus"]). Describe the status of my_stack after the execution of each of the following operations: my_stack.pop()my_stack.pop()my_stack.append("Voldemort").
  3. Consider to have a queue obtained by processing, one by one, the elements included in the previous list, i.e. my_queue = deque(["Draco", "Harry", "Hermione", "Ron", "Severus"]). Describe the status of my_queue after the execution of each of the following operations: my_queue.popleft()my_queue.append("Voldemort")my_queue.popleft().

References