Slide 17 for ENCM 339 Fall 2015

Steve Norman, PhD, PEng

Electrical & Computer Engineering Schulich School of Engineering University of Calgary

Fall Term, 2015 SN’s ENCM 339 Fall 2015 Slide Set 17 slide 2/46 Contents

Data

Abstract data types

Linked data structures

A singly- type in C++

Operations and implementation of SLListV1

The Big 3 for a linked-list class

Ordered linked lists SN’s ENCM 339 Fall 2015 Slide Set 17 slide 3/46 Outline of Slide Set 17

Data structures

Abstract data types

Linked data structures

A singly-linked list type in C++

Operations and implementation of SLListV1

The Big 3 for a linked-list class

Ordered linked lists SN’s ENCM 339 Fall 2015 Slide Set 17 slide 4/46 Data structures

In discussion of programming, the term data means a collection of data items, organized to support some of the following goals:

I fast insertion or removal of data items

I fast access to data items

I fast searching for the location of a particular data item, if the item is in the collection

I maintaining items in some kind of sorted order

I keeping the memory footprint of the collection as small as possible There are design trade-offs—no single design is best for all of the above goals. SN’s ENCM 339 Fall 2015 Slide Set 17 slide 5/46

Here are two kinds of data structures that everybody in this course should be familiar with:

I arrays

I C and C++ structure objects We’re about to study a kind of data structure called a linked list. Students in the Software Engineering major or Computer Engineering minor will study many other data structures in CPSC 319. Note that data structure is not a synonym for structure object. A structure object is just one of the many kinds of data structures. SN’s ENCM 339 Fall 2015 Slide Set 17 slide 6/46 Outline of Slide Set 17

Data structures

Abstract data types

Linked data structures

A singly-linked list type in C++

Operations and implementation of SLListV1

The Big 3 for a linked-list class

Ordered linked lists SN’s ENCM 339 Fall 2015 Slide Set 17 slide 7/46 Abstract data types

An abstract (ADT) can be thought of as a description of a type

I with very precise specification of all of the operations available with objects of the given type;

I with very little or no specification of how the operations are implemented—no detail about all the numbers, addresses, if-statements, loops, and so on, that will be used to make the type work. SN’s ENCM 339 Fall 2015 Slide Set 17 slide 8/46 Example ADT: C++ vector

Suppose that you use a vector in a C++ program, with particular C++ compiler and a particular C++ library. Then vector is really a specific, “concrete” type—all of the implementation details can be found in the (surprisingly complicated!) library source code.

However, descriptions of vector types in textbooks and online documentation tend to treat vector as an ADT, and focus on describing the operations available with vector objects. SN’s ENCM 339 Fall 2015 Slide Set 17 slide 9/46

A few of the most important vector operations are

I initialization of an empty vector

I initialization of a vector to have some non-zero number of elements, with initial element values specified one way or another

I access to an individual element using an index

I resizing a vector—making the number of elements smaller or larger

Of course, the abstraction is not perfect. To use a vector object safely and effectively, it is helpful to know that its elements are all stored in a single array on the . SN’s ENCM 339 Fall 2015 Slide Set 17 slide 10/46 Data structures, ADTs, and C++ classes An ADT is a design idea. When that idea is translated into code for a C++ class, development often follows this pattern:

I The operations of the ADT are translated into prototypes for public member functions.

I For each public member function, parameter types, the return value types and supporting documentation reflect the operation performed by that function.

I One or more data structures are chosen for the implementation of the class.

I The choice of data structure(s) leads to choices for private members and choices for algorithms used in member function definitions. SN’s ENCM 339 Fall 2015 Slide Set 17 slide 11/46 Outline of Slide Set 17

Data structures

Abstract data types

Linked data structures

A singly-linked list type in C++

Operations and implementation of SLListV1

The Big 3 for a linked-list class

Ordered linked lists SN’s ENCM 339 Fall 2015 Slide Set 17 slide 12/46 Linked data structures A linked data structure stores data items in a collection of nodes. A node is a chunk of memory that contains

I some data

I information about where zero, one, or two or more other nodes are located Typically in C and C++,

I a node is a structure object allocated in the heap

I information about locations of other nodes is stored in pointers (Sometimes class types are used instead of struct types for node types in C++.) SN’s ENCM 339 Fall 2015 Slide Set 17 slide 13/46 Examples of linked data structures

The next several slides show a few different kinds of linked data structures. The data in each node in all of the examples is a single integer. That’s for simplicity—storing a collection of integers is not the most practical application of linked data structures! You can assume that all of the nodes are in the heap. I am being deliberately vague about where and how the pointer variables root, head, tail, and dummy are allocated. I’ll be more precise about that later. SN’s ENCM 339 Fall 2015 Slide Set 17 slide 14/46 Binary search

An example of a binary is shown on the next slide. Binary search trees (BSTs) are perhaps both the most beautiful and most useful kind of linked data structure. For the BST shown on the slide, what kind of code would work to set up a node type in C? In C++? Variations of BSTs are used, for example, to support the C++ library set, multiset, map and container types. Unfortunately, some BST programming is difficult (for example, removal of a node from a BST is surprisingly hard to to do properly), so we won’t look at the details of BSTs in ENCM 339. SN’s ENCM 339 Fall 2015 Slide Set 17 slide 15/46

root data 60 left right

data 35 data 80 left right left right

data 10 data 50 data 90 left right left right left right SN’s ENCM 339 Fall 2015 Slide Set 17 slide 16/46 Singly-linked list with head and tail pointers

head data data data data 200 100 400 300 next next next next

tail

For this kind of list, what kind of code would work to set up a node type in C? In C++? We’ll soon see that traversal—visiting all of the nodes—is easy going head-to-tail, but awkward and slow going tail-to-head. SN’s ENCM 339 Fall 2015 Slide Set 17 slide 17/46 Singly-linked list with only a head pointer

For some uses of singly-linked lists, maintaining a tail pointer is unnecessary work . . .

head data data data data 200 100 400 300 next next next next SN’s ENCM 339 Fall 2015 Slide Set 17 slide 18/46 Circular doubly-linked list with a “dummy” node

data data data data X 66 33 99 dummy next next next next

prev prev prev prev

X is borrowed from ENEL 353 and means “don’t-care”. The actual data items, are, first-to-last, 66, 33, 99. Last-to-first, they’re 99, 33, 66. SN’s ENCM 339 Fall 2015 Slide Set 17 slide 19/46 Circular doubly-linked lists . . .

Something like the list on the previous slide is what you get with list types in the standard C++ library. Traversals from last-to-first and first-to-last are equally easy with doubly-linked lists. We will not study doubly-linked lists in detail in ENCM 339, and they will not appear on the final exam. SN’s ENCM 339 Fall 2015 Slide Set 17 slide 20/46 Outline of Slide Set 17

Data structures

Abstract data types

Linked data structures

A singly-linked list type in C++

Operations and implementation of SLListV1

The Big 3 for a linked-list class

Ordered linked lists SN’s ENCM 339 Fall 2015 Slide Set 17 slide 21/46 A singly-linked list type in C++

Let’s look at an as-simple-as-possible type, with a few of the features of forward_list types available in the C++11 standard library. We’ll call the type SLListV1, short for singly-linked list, Version 1. Below is the node type. The typedef allows us to make a quick change to the type of data item stored in a node. typedef int ItemType; struct Node { ItemType item; Node *next; }; SN’s ENCM 339 Fall 2015 Slide Set 17 slide 22/46

Here is a sketch of the class definition: class SLListV1 { public:

member function prototypes

private: Node *headM; // points to first node Node *tailM; // points to last node }; For a linked data structure, before we start to think about what operations are supported, it’s a good idea to consider . . .

I What does an empty data structure look like?

I What does a data structure with one item look like?

I What does a data structure with a few items look like? SN’s ENCM 339 Fall 2015 Slide Set 17 slide 23/46 Outline of Slide Set 17

Data structures

Abstract data types

Linked data structures

A singly-linked list type in C++

Operations and implementation of SLListV1

The Big 3 for a linked-list class

Ordered linked lists SN’s ENCM 339 Fall 2015 Slide Set 17 slide 24/46 Operations and implementation of SLListV1

So far, we’ve made diagrams showing SLListV1 objects with no items, one item, and a few items. A more detailed sketch of a class definition is given on the next slide. For each of the operations offered by SLListV1,

I are there any REQUIRES conditions?

I what does the operation do? Note: Except for print_on_cout, names of operations reflect names for operations with standard C++ library containers. class SLListV1 { public: SLListV1() : headM(0), tailM(0) { } // We’ll need to add the Big 3!

bool empty() const { return headM == 0; } const ItemType& front() const { return headM->item; } const ItemType& back() const { return tailM->item; }

void push_front(const ItemType& new_item); void push_back(const ItemType& new_item); void pop_front();

void print_on_cout() const; private: Node *headM; Node *tailM; }; SN’s ENCM 339 Fall 2015 Slide Set 17 slide 26/46 The print_on_cout operation

This is a good starting point, because

I it provides a simple example of linked-list traversal;

I it helps with testing code for all of the other operations. Let’s write a function definition for SLListV1::print_on_cout.

By the way, this kind of feature is not offered by library container classes—library containers offer more “general purpose” ways to traverse containers, using types called iterators. SN’s ENCM 339 Fall 2015 Slide Set 17 slide 27/46 The push_front operation

What should the output from this code fragment be? SLListV1 listA; listA.print_on_cout(); listA.push_front(10); listA.push_front(20); listA.push_front(30); listA.print_on_cout();

Let’s write a function definition for SLListV1::push_front. SN’s ENCM 339 Fall 2015 Slide Set 17 slide 28/46 The push_back operation

What should the output from this code fragment be? SLListV1 listB; listB.print_on_cout(); listB.push_back(70); listB.push_back(80); listB.push_back(90); listB.print_on_cout();

Let’s write a function definition for SLListV1::push_back. SN’s ENCM 339 Fall 2015 Slide Set 17 slide 29/46 The pop_front operation

What should the output from this code fragment be? SLListV1 listC; listC.push_back(40); listC.push_back(50); listC.push_back(60); listC.pop_front(); listC.print_on_cout(); listC.pop_front(); listC.pop_front(); listC.print_on_cout();

Let’s write a function definition for SLListV1::pop_front. SN’s ENCM 339 Fall 2015 Slide Set 17 slide 30/46 Why is there no pop_back operation?

It is quite easy to use the tailM pointer to delete the last node belonging to an SLListV1 object. Why would it be slow and difficult to complete the pop_back operation? (Think of a list with a large number of items.) SN’s ENCM 339 Fall 2015 Slide Set 17 slide 31/46 Outline of Slide Set 17

Data structures

Abstract data types

Linked data structures

A singly-linked list type in C++

Operations and implementation of SLListV1

The Big 3 for a linked-list class

Ordered linked lists SN’s ENCM 339 Fall 2015 Slide Set 17 slide 32/46 The Big 3 for a linked-list class

If a non-empty SLListV1 object goes out of scope, the memory allocated for its nodes will be leaked. An attempt to copy a SLListV1 object will result in memberwise copy. Except for the case of copying an empty list, the eventual outcome of memberwise copy will be a small-scale digital trainwreck. So a class like SLListV1 really needs a destructor, a copy constructor, and a copy assignment operator. SN’s ENCM 339 Fall 2015 Slide Set 17 slide 33/46 Version 2

class SLListV2 { public: SLListV2() : headM(0), tailM(0) {} SLListV2(const SLListV2& src); SLListV2& operator=(const SLListV2& rhs); ~SLListV2();

same operations as in SLListV1

private: Node *headM; Node *tailM; }; Let’s start by thinking about the destructor . . . SN’s ENCM 339 Fall 2015 Slide Set 17 slide 34/46

The job of the destructor is to deallocate all of the nodes belonging to a list object. Here is some code that might or might not work: SLListV2::~SLListV2() { Node *p = headM; while (p != 0) { delete p; p = p->next; } } What is defective about the above code, and how can it be fixed? An alternate, less efficient approach is to use calls to pop_front. Let’s write code for that. SN’s ENCM 339 Fall 2015 Slide Set 17 slide 35/46 The copy constructor

To initialize a linked list as a copy of an existing source linked list, here are some of the problems we need to consider:

I What if the source list is empty?

I How should head and tail pointers be set up in the destination list, if the source list is not empty?

I How should nodes be allocated, their items be given values, and their next pointers be properly set up?

Let’s write a copy constructor for SLListV2. SN’s ENCM 339 Fall 2015 Slide Set 17 slide 36/46 The copy assignment operator Review: In coding a copy assignment operator . . .

I (1) Self-assignment and chained assignment should be handled correctly.

I (2) Usually, resources “owned” by *this prior to assignment must be released.

I (3) Resources needed to hold copies of data from the rhs object must be allocated.

I (4) Data from the rhs object must be copied to the *this object. There is a fairly generic pattern for (1). (2), (3) and (4) can be somewhat merged together, and exact details depend very much on what kind of data structure is being copied. SN’s ENCM 339 Fall 2015 Slide Set 17 slide 37/46

We won’t write code for the copy assignment operator of SLListV2. But let’s briefly describe two approaches to coding actions (2), (3) and (4) from the previous slide.

I What would be a straightforward approach, based on algorithms we have seen earlier in this slide set?

I What would be a trickier but often more efficient approach? SN’s ENCM 339 Fall 2015 Slide Set 17 slide 38/46 Outline of Slide Set 17

Data structures

Abstract data types

Linked data structures

A singly-linked list type in C++

Operations and implementation of SLListV1

The Big 3 for a linked-list class

Ordered linked lists SN’s ENCM 339 Fall 2015 Slide Set 17 slide 39/46 Ordered linked lists Before we think about ordered linked lists, let’s start with an ordered list ADT (). An ordered list is a list of items in which the first item must be ≤ the second item, the second item must be ≤ the third item, and so on. Of course, for this to work, the item type must allow comparison to determine whether some item A is less than, equal to, or greater than some other item B. The essential operations of the ADT are . . .

I insertion: adding an item to the list such that the list stays ordered;

I removal: taking away an item, leaving the remaining items ordered. SN’s ENCM 339 Fall 2015 Slide Set 17 slide 40/46 Data structures for the ordered list ADT

For short lists—with no more than around eight items—here are two good choices:

I array

I singly-linked list For longer lists, these are good choices:

I array, if insertion and removal of items are very infrequent events

I some version of , if insertion and removal of items are fairly frequent events SN’s ENCM 339 Fall 2015 Slide Set 17 slide 41/46 OLList: a linked-list implemented of the ordered list ADT

The next two slides present two separate outlines of a class definition for OLList. A question about Outline #1: Are duplicate items allowed in an OLList object? Two questions about Outline #2:

I How many member variables does an OLList object have?

I What exactly is going on with the struct type definition? SN’s ENCM 339 Fall 2015 Slide Set 17 slide 42/46 // Outline #1 ... typedef int ItemType;

class OLList { public: OLList() : headM(0), tailM(0) {} OLList(const OLList& src); OLList& operator=(const OLList& rhs); ~OLList(); void insert(const ItemType& new_item); // PROMISES: A copy of new_item is added to the // list. Updated, longer list is ordered. other operations, including remove private: private members }; SN’s ENCM 339 Fall 2015 Slide Set 17 slide 43/46

// Outline #2 ... typedef int ItemType;

class OLList { public: public members private: struct Node { ItemType item; Node *next; }; Node *headM; Node *tailM; }; SN’s ENCM 339 Fall 2015 Slide Set 17 slide 44/46 How to code the insert function?

An outline of a function definition appears on the next slide. Let’s make some brief notes about Cases 2 and 3. Before writing code for Case 4, let’s draw a diagram to show what the stack and heap look like when we arrive at Case 4 in the fourth of the following four calls to insert ... int main() { OLList list; list.insert(200); list.insert(100); list.insert(400); list.insert(300); // ... } SN’s ENCM 339 Fall 2015 Slide Set 17 slide 45/46

void OLList::insert(const ItemType& new_item) { Node *new_node = new Node; new_node->item = new_item;

if (headM == 0) { // Case 1: insert into empty list new_node->next = 0; headM = tailM = new_node; return; }

Case 2: new_item <= headM->item

Case 3: new_item >= tailM->item

Case 4: Uh-oh, must insert between two existing nodes! } SN’s ENCM 339 Fall 2015 Slide Set 17 slide 46/46 Removal of an item from an ordered linked list

Lectures won’t look at this problem in detail. Removal of the item in the head node is relatively easy: It’s like the pop_front operation for one of the SLList types. Removal of any other item is harder: After a node is selected for deletion, a link among the remaining nodes must be altered to “bridge the gap” left by deletion of the removed node.