C++ Classes and Data Structures Jeffrey S. Childs

Slides:



Advertisements
Similar presentations
Chapter 22 Implementing lists: linked implementations.
Advertisements

DATA STRUCTURES USING C++ Chapter 5
Chapter 3 – Lists A list is just what the name implies, a finite, ordered sequence of items. Order indicates each item has a position. A list of size 0.
Backup Slides. An Example of Hash Function Implementation struct MyStruct { string str; string item; };
Lecture 10 Sept 29 Goals: hashing dictionary operations general idea of hashing hash functions chaining closed hashing.
hashing1 Hashing It’s not just for breakfast anymore!
Chapter 6. 2 Objectives You should be able to describe: Function and Parameter Declarations Returning a Single Value Pass by Reference Variable Scope.
1 Data Structures Data Structures Topic #2. 2 Today’s Agenda Data Abstraction –Given what we talked about last time, we need to step through an example.
1 Chapter 6 Lists Plus. ADT Sorted List Operations Transformers n MakeEmpty n InsertItem n DeleteItem Observers n IsFull n LengthIs n RetrieveItem Iterators.
1 C++ Classes and Data Structures Jeffrey S. Childs Chapter 8 Stacks and Queues Jeffrey S. Childs Clarion University of PA © 2008, Prentice Hall.
Pointers CS362. Pointers A Pointer is a variable that can hold a memory address Pointers can be used to: Indirectly reference existing variables (sometimes.
HASHING Section 12.7 (P ). HASHING - have already seen binary and linear search and discussed when they might be useful (based on complexity)
C++ Classes and Data Structures Jeffrey S. Childs
A First Book of C++: From Here To There, Third Edition2 Objectives You should be able to describe: Function and Parameter Declarations Returning a Single.
1 C++ Classes and Data Structures Jeffrey S. Childs Chapter 13 Recursion Jeffrey S. Childs Clarion University of PA © 2008, Prentice Hall.
1 C++ Classes and Data Structures Jeffrey S. Childs Chapter 14 Introduction to Sorting Algorithms Jeffrey S. Childs Clarion University of PA © 2008, Prentice.
1 C++ Classes and Data Structures Jeffrey S. Childs Chapter 5 An Array Class Jeffrey S. Childs Clarion University of PA © 2008, Prentice Hall.
1 Chapter 7 Stacks and Queues. 2 Stack ADT Recall that ADT is abstract data type, a set of data and a set of operations that act upon the data. In a stack,
Pointers OVERVIEW.
1 C++ Classes and Data Structures Jeffrey S. Childs Chapter 3 More About Classes Jeffrey S. Childs Clarion University of PA © 2008, Prentice Hall.
Chapter 1 Object Oriented Programming. OOP revolves around the concept of an objects. Objects are created using the class definition. Programming techniques.
1 Chapter 10 Trees. 2 Definition of Tree A tree is a set of linked nodes, such that there is one and only one path from a unique node (called the root.
1 C++ Classes and Data Structures Jeffrey S. Childs Chapter 4 Pointers and Dynamic Arrays Jeffrey S. Childs Clarion University of PA © 2008, Prentice Hall.
1 C++ Classes and Data Structures Jeffrey S. Childs Chapter 2 Overloaded Operators, Class Templates, and Abstraction Jeffrey S. Childs Clarion University.
1 Chapter 7 The Linked List as a Data Structure. 2 The List ADT A list is a list of elements. The list of elements consist of the data acted upon by list.
C++ Classes and Data Structures Jeffrey S. Childs
Nyhoff, ADTs, Data Structures and Problem Solving with C++, Second Edition, © 2005 Pearson Education, Inc. All rights reserved Stacks.
WEEK 1 Hashing CE222 Dr. Senem Kumova Metin
ICOM 4035 – Data Structures Dr. Manuel Rodríguez Martínez Electrical and Computer Engineering Department.
COSC 2007 Data Structures II Chapter 13 Advanced Implementation of Tables IV.
Chapter Functions 6. Modular Programming 6.1 Modular Programming Modular programming: breaking a program up into smaller, manageable functions or modules.
1 C++ Classes and Data Structures Jeffrey S. Childs Chapter 11 Hash Tables Jeffrey S. Childs Clarion University of PA © 2008, Prentice Hall.
1 C++ Classes and Data Structures Jeffrey S. Childs Chapter 15 Other Data Structures Jeffrey S. Childs Clarion University of PA © 2008, Prentice Hall.
Chapter 5 Linked List by Before you learn Linked List 3 rd level of Data Structures Intermediate Level of Understanding for C++ Please.
1 Chapter 6 Methods for Making Data Structures. 2 Dynamic Arrays in Data Structures In almost every data structure, we want functions for inserting and.
1 CS 132 Spring 2008 Chapter 5 Linked Lists p
Chapter 16: Linked Lists.
Lecture 6 of Computer Science II
C++ Programming:. Program Design Including
Pointers and Linked Lists
Pointers and Linked Lists
Hash table CSC317 We have elements with key and satellite data
CSCI 210 Data Structures and Algorithms
C Programming Tutorial – Part I
CSE 143 Linked Lists [Chapter , 8.8] 3/30/98.
Review Graph Directed Graph Undirected Graph Sub-Graph
Hash Tables.
Map interface Empty() - return true if the map is empty; else return false Size() - return the number of elements in the map Find(key) - if there is an.
Prof. Neary Adapted from slides by Dr. Katherine Gibson
C++ Classes and Data Structures Jeffrey S. Childs
C++ Classes and Data Structures Jeffrey S. Childs
Introduction to Classes
Chapter 18: Linked Lists.
Data structures in C++.
6 Chapter Functions.
Hash Tables Chapter 12 discusses several ways of storing information in an array, and later searching for the information. Hash tables are a common.
Doubly Linked List Implementation
CS202 - Fundamental Structures of Computer Science II
Advanced Implementation of Tables
Pointers & Dynamic Data Structures
Hash Tables Chapter 12 discusses several ways of storing information in an array, and later searching for the information. Hash tables are a common.
CENG 218 Classes and Data Structures
Data Structures & Algorithms
CS210- Lecture 16 July 11, 2005 Agenda Maps and Dictionaries Map ADT
Lists CMSC 202, Version 4/02.
Lists CMSC 202, Version 4/02.
Doubly Linked List Implementation
Lecture-Hashing.
Data Structures & Programming
Presentation transcript:

C++ Classes and Data Structures Jeffrey S. Childs Chapter 11 Hash Tables Jeffrey S. Childs Clarion University of PA © 2008, Prentice Hall 1 1

Contents Terminology: key, value, hash function Operations: insert, retrieve.. Collisions and chaining Ideal Hash Table Implementation using Linked Lists 2

Hash Table ADT The hash table is a table of elements that have keys A hash function is used for locating a position in the table The table of elements is the set of data acted upon by the hash table operations Key -> Element in the table 3

Hash Table ADT Operations insert, to insert an element into a table retrieve, to retrieve an element from the table remove, to remove an element from the table update, to update an element in the table an operation to empty out the hash table 4

Fast Search A hash table uses a function of the key value of an element to identify its location in an array. A search for an element can be done in ( 1 ) time. The function of the key value is called a hash function. 5 5

Hash Functions The input into a hash function is a key value The output from a hash function is an index of an array (hash table) where the object containing the key is located Example of a hash function: h( k ) = k % 100 6

Example Using a Hash Function Suppose our hash function is: h( k ) = k % 100 We wish to search for the object containing key value 214 k is set to 214 in the hash function The result is 14 The object containing key value 214 is stored at index 14 of the array (hash table) The search is done in ( 1 ) time 7

Inserting an Element An element is inserted into a hash table using the same hash function h( k ) = k % 100 To find where an element is to be inserted, use the hash function on its key If the key value is 214, the object is to be stored in index 14 of the array Insertion is done in ( 1 ) time 8

Consider the Big Picture … If we have millions of key values, it may take a long time to search a regular array or a linked list for a specific part number (on average, we might compare 500,000 key values) Using a hash table, we simply have a function which provides us with the index of the array where the object containing the key is located 9

Collisions Consider the hash function h( k ) = k % 100 A key value of 393 is used for an object, and the object is stored at index 93 Then a key value of 193 is used for a second object; the result of the hash function is 93, but index 93 is already occupied This is called a collision 10

How are Collisions Resolved? The most popular way to resolve collisions is by chaining Instead of having an array of objects, we have an array of linked lists, each node of which contains an object An element is still inserted by using the hash function -- the hash function provides an index of a linked list, and the element is inserted at the front of that (usually short) linked list When searching for an element, the hash function is used to get the correct linked list, then the linked list is searched for the key (still much faster than comparing 500,000 keys) 11

Example Using Chaining 1 2 3 4 5 6 A hash table which is initially empty. Every element is a LinkedList object. Only the start pointer of the LinkedList object is shown, which is set to NULL. The hash function is: h( k ) = k % 7 12

Example Using Chaining (cont.) 1 2 3 4 5 6 INSERT object with key 31 The hash function is: h( k ) = k % 7 13

Example Using Chaining (cont.) 1 2 3 4 5 6 INSERT object with key 31 31 % 7 is 3 The hash function is: h( k ) = k % 7 14

Example Using Chaining (cont.) 1 2 3 4 5 6 INSERT object with key 31 31 % 7 is 3 31 The hash function is: h( k ) = k % 7 15

Example Using Chaining (cont.) 1 2 3 4 5 6 Note: The whole object is stored but only the key value is shown INSERT object with key 31 31 % 7 is 3 31 The hash function is: h( k ) = k % 7 16

Example Using Chaining (cont.) 1 2 3 4 5 6 31 The hash function is: h( k ) = k % 7 17

Example Using Chaining (cont.) 1 2 3 4 5 6 INSERT object with key 9 31 The hash function is: h( k ) = k % 7 18

Example Using Chaining (cont.) 1 2 3 4 5 6 INSERT object with key 9 9 % 7 is 2 31 The hash function is: h( k ) = k % 7 19

Example Using Chaining (cont.) 1 2 3 4 5 6 9 INSERT object with key 9 9 % 7 is 2 31 The hash function is: h( k ) = k % 7 20

Example Using Chaining (cont.) 1 2 3 4 5 6 9 31 The hash function is: h( k ) = k % 7 21

Example Using Chaining (cont.) 1 2 3 4 5 6 9 INSERT object with key 36 31 The hash function is: h( k ) = k % 7 22

Example Using Chaining (cont.) 1 2 3 4 5 6 9 INSERT object with key 36 36 % 7 is 1 31 The hash function is: h( k ) = k % 7 23

Example Using Chaining (cont.) 1 2 3 4 5 6 36 9 INSERT object with key 36 36 % 7 is 1 31 The hash function is: h( k ) = k % 7 24

Example Using Chaining (cont.) 1 2 3 4 5 6 36 9 31 The hash function is: h( k ) = k % 7 25

Example Using Chaining (cont.) 1 2 3 4 5 6 36 9 INSERT object with key 42 31 The hash function is: h( k ) = k % 7 26

Example Using Chaining (cont.) 1 2 3 4 5 6 36 9 INSERT object with key 42 42 % 7 is 0 31 The hash function is: h( k ) = k % 7 27

Example Using Chaining (cont.) 1 2 3 4 5 6 42 36 9 INSERT object with key 42 42 % 7 is 0 31 The hash function is: h( k ) = k % 7 28

Example Using Chaining (cont.) 1 2 3 4 5 6 42 36 9 31 The hash function is: h( k ) = k % 7 29

Example Using Chaining (cont.) 1 2 3 4 5 6 42 36 9 INSERT object with key 46 31 The hash function is: h( k ) = k % 7 30

Example Using Chaining (cont.) 1 2 3 4 5 6 42 36 9 INSERT object with key 46 46 % 7 is 4 31 The hash function is: h( k ) = k % 7 31

Example Using Chaining (cont.) 1 2 3 4 5 6 42 36 9 INSERT object with key 46 46 % 7 is 4 31 46 The hash function is: h( k ) = k % 7 32

Example Using Chaining (cont.) 1 2 3 4 5 6 42 36 9 31 46 The hash function is: h( k ) = k % 7 33

Example Using Chaining (cont.) 1 2 3 4 5 6 42 36 9 INSERT object with key 20 31 46 The hash function is: h( k ) = k % 7 34

Example Using Chaining (cont.) 1 2 3 4 5 6 42 36 9 INSERT object with key 20 20 % 7 is 6 31 46 The hash function is: h( k ) = k % 7 35

Example Using Chaining (cont.) 1 2 3 4 5 6 42 36 9 INSERT object with key 20 20 % 7 is 6 31 46 The hash function is: h( k ) = k % 7 20 36

Example Using Chaining (cont.) 1 2 3 4 5 6 42 36 9 31 46 The hash function is: h( k ) = k % 7 20 37

Example Using Chaining (cont.) 1 2 3 4 5 6 42 36 9 INSERT object with key 2 31 46 The hash function is: h( k ) = k % 7 20 38

Example Using Chaining (cont.) 1 2 3 4 5 6 42 36 9 INSERT object with key 2 2 % 7 is 2 31 46 The hash function is: h( k ) = k % 7 20 39

Example Using Chaining (cont.) 1 2 3 4 5 6 42 COLLISION occurs… 36 9 INSERT object with key 2 2 % 7 is 2 31 46 The hash function is: h( k ) = k % 7 20 40

Example Using Chaining (cont.) 1 2 3 4 5 6 42 But key 2 is just inserted in the linked list 36 9 INSERT object with key 2 2 % 7 is 2 31 46 The hash function is: h( k ) = k % 7 20 41

Example Using Chaining (cont.) 1 2 3 4 5 6 42 The insert function of LinkedList inserts a new element at the BEGINNING of the list 36 9 INSERT object with key 2 2 % 7 is 2 31 46 The hash function is: h( k ) = k % 7 20 42

Example Using Chaining (cont.) 1 2 3 4 5 6 42 36 9 INSERT object with key 2 2 % 7 is 2 31 46 The hash function is: h( k ) = k % 7 20 43

Example Using Chaining (cont.) 1 2 3 4 5 6 42 36 9 INSERT object with key 2 2 % 7 is 2 31 46 The hash function is: h( k ) = k % 7 20 44

Example Using Chaining (cont.) 1 2 3 4 5 6 42 36 9 INSERT object with key 2 2 % 7 is 2 31 46 The hash function is: h( k ) = k % 7 20 45

Example Using Chaining (cont.) 1 2 3 4 5 6 42 36 9 INSERT object with key 2 2 % 7 is 2 31 46 The hash function is: h( k ) = k % 7 20 46

Example Using Chaining (cont.) 1 2 3 4 5 6 42 36 9 INSERT object with key 2 2 % 7 is 2 31 46 The hash function is: h( k ) = k % 7 20 47

Example Using Chaining (cont.) 1 2 3 4 5 6 42 36 2 9 INSERT object with key 2 2 % 7 is 2 31 46 The hash function is: h( k ) = k % 7 20 48

Example Using Chaining (cont.) 1 2 3 4 5 6 42 36 2 9 31 46 The hash function is: h( k ) = k % 7 20 49

Example Using Chaining (cont.) 1 2 3 4 5 6 42 36 2 9 INSERT object with key 24 31 46 The hash function is: h( k ) = k % 7 20 50

Example Using Chaining (cont.) 1 2 3 4 5 6 42 36 2 9 INSERT object with key 24 24 % 7 is 3 31 46 The hash function is: h( k ) = k % 7 20 51

Example Using Chaining (cont.) 1 2 3 4 5 6 42 36 2 9 INSERT object with key 24 24 % 7 is 3 31 46 The hash function is: h( k ) = k % 7 20 52

Example Using Chaining (cont.) 1 2 3 4 5 6 42 36 2 9 INSERT object with key 24 24 % 7 is 3 31 46 The hash function is: h( k ) = k % 7 20 53

Example Using Chaining (cont.) 1 2 3 4 5 6 42 36 2 9 INSERT object with key 24 24 % 7 is 3 31 46 The hash function is: h( k ) = k % 7 20 54

Example Using Chaining (cont.) 1 2 3 4 5 6 42 36 2 9 INSERT object with key 24 24 % 7 is 3 31 46 The hash function is: h( k ) = k % 7 20 55

Example Using Chaining (cont.) 1 2 3 4 5 6 42 36 2 9 INSERT object with key 24 24 % 7 is 3 24 31 46 The hash function is: h( k ) = k % 7 20 56

Example Using Chaining (cont.) 1 2 3 4 5 6 42 36 2 9 24 31 46 The hash function is: h( k ) = k % 7 20 57

Example Using Chaining (cont.) 1 2 3 4 5 6 42 36 2 9 **FIND** the object with key 9 24 31 46 The hash function is: h( k ) = k % 7 20 58

Example Using Chaining (cont.) 1 2 3 4 5 6 42 36 2 9 **FIND** the object with key 9 9 % 7 is 2 24 31 46 The hash function is: h( k ) = k % 7 20 59

Example Using Chaining (cont.) 1 2 3 4 5 6 42 We search this linked list for the object with key 9 36 2 9 **FIND** the object with key 9 9 % 7 is 2 24 31 46 The hash function is: h( k ) = k % 7 20 60

Example Using Chaining (cont.) 1 2 3 4 5 6 42 Remember…the whole object is stored, only the key is shown 36 2 9 **FIND** the object with key 9 9 % 7 is 2 24 31 46 The hash function is: h( k ) = k % 7 20 61

Example Using Chaining (cont.) 1 2 3 4 5 6 42 Does this object contain key 9? 36 2 9 **FIND** the object with key 9 9 % 7 is 2 24 31 46 The hash function is: h( k ) = k % 7 20 62

Example Using Chaining (cont.) 1 2 3 4 5 6 42 Does this object contain key 9? No, so go on to the next object. 36 2 9 **FIND** the object with key 9 9 % 7 is 2 24 31 46 The hash function is: h( k ) = k % 7 20 63

Example Using Chaining (cont.) 1 2 3 4 5 6 42 Does this object contain key 9? 36 2 9 **FIND** the object with key 9 9 % 7 is 2 24 31 46 The hash function is: h( k ) = k % 7 20 64

Example Using Chaining (cont.) 1 2 3 4 5 6 42 Does this object contain key 9? YES, found it! Return the object. 36 2 9 **FIND** the object with key 9 9 % 7 is 2 24 31 46 The hash function is: h( k ) = k % 7 20 65

Uniform Hashing When the elements are spread evenly (or near evenly) among the indexes of a hash table, it is called uniform hashing If elements are spread evenly, such that the number of elements at an index is less than some small constant, uniform hashing allows a search to be done in ( 1 ) time The hash function largely determines whether or not we will have uniform hashing 66

Bad Hash Functions h( k ) = 5 is obviously a bad hash function h( k ) = k % 100 could be a bad hash function if there is meaning attached to parts of a key Consider that the key might be an employee id The last two digits may give the state of birth 67

Ideal Hash Function for Uniform Hashing The hash table size should be a prime number that is not too close to a power of 2 31 is a prime number but is too close to a power of 2 97 is a prime number not too close to a power of 2 A good hash function might be: h( k ) = k % 97 If the constant use in a hash and the number of buckets are coprime, collisions are minimized 68 68

Hash Functions Can be Made for Keys that are Strings 1 int sum = 0; 2 for ( int i = 0; i < int( str.length( ) ); i++ ) 3 sum += str[ i ]; 4 hash_index = sum % 97; 69

Speed vs. Memory Conservation Speed comes from reducing the number of collisions In a search, if there are no collisions, the first element in the linked list in the one we want to find (fast) Therefore, the greatest speed comes about by making a hash table much larger than the number of keys (but there will still be an occasional collision) 70

Speed vs. Memory Conservation (cont.) Each empty LinkedList object in a hash table wastes 8 bytes of memory (4 bytes for the start pointer and 4 bytes for the current pointer) The best memory conservation comes from trying to reduce the number of empty LinkedList objects The hash table size would be made much smaller than the number of keys (there would still be an occasional empty linked list) 71

Hash Table Design Decide whether speed or memory conservation is more important (and how much more important) for the application Come up with a good table size which Allows for the use of a good hash function Strikes the appropriate balance between speed and memory conservation 72

Ideal Hash Tables Can we have a hash function which guarantees that there will be no collisions? Yes: h( k ) = k Each key k is unique; therefore, each index produced from h( k ) is unique Consider 300 employees that have a 4 digit id A hash table size of 10000 with the hash function above guarantees the best possible speed 73

Ideal Hash Tables (cont.) Should we use LinkedList objects if there are no collisions? Suppose each Employee object takes up 100 bytes An array size of 10000 Employee objects with only 300 used indexes will have 9700 unused indexes, each taking up 100 bytes Best to use LinkedList objects (in this case) – the 9700 unused indexes will only use 8 bytes each Additional space can be saved by not storing the employee id in the object (if no collisions) 74

Ideal Hash Tables (cont.) Can we have a hash table without any collisions and without any empty linked lists? Sometimes. Consider 300 employees with id’s from 0 to 299. We can make a hash table size of 300, and use h( k ) = k LinkedList objects wouldn’t be necessary and in fact, would waste space It would also not be necessary to store the employee id in the object This is actually an array 75 75

Implementing a Hash Table We’ll implement a HashTable with linked lists (chaining) without chaining, a hash table can become full If the client has the ideal hash table mentioned on the previous slide, he/she would be better off to just use an Array for the hash table 76

Implementing a Hash Function We shouldn’t write the hash function The client should write the hash function that he/she would like to use Then, the client should pass the hash function that he/she wrote as a parameter into the constructor of the HashTable class This can be implemented with function pointers 77

Function Pointers A function pointer is a pointer that holds the address of a function The function can be called using the function pointer instead of the function name 78

Function Pointers (cont.) Example of a function pointer declaration: float (*funcptr) (string); 79

Function Pointers (cont.) Example of a function pointer declaration: float (*funcptr) (string); funcptr is the name of the pointer; the name can be chosen like any other pointer name 80

Function Pointers (cont.) Example of a function pointer declaration: float (*funcptr) (string); The parentheses are necessary. 81

Function Pointers (cont.) Example of a function pointer declaration: float (*funcptr) (string); The return type of the function that funcptr can point to is given here (in this case, the return type is a float) 82

Function Pointers (cont.) Example of a function pointer declaration: float (*funcptr) (string); The parameter list of a function that funcptr can point to is given here – in this case, there is only one parameter of string type. 83

Function Pointers (cont.) Example of a function pointer declaration: float (*funcptr) (string); What would a function pointer declaration look like if the function it can point to has a void return type and accepts two integer parameters? void (*fptr) (int, int) 84 84

Function Pointers (cont.) void (*fp) (int, int); 85

Function Pointers (cont.) void (*fp) (int, int); void foo( int a, int b ) { cout << “a is: “ << a << endl; cout << “b is: “ << b << endl; } A function that fp can point to 86

Assigning the Address of a Function to a Function Pointer void (*fp) (int, int); void foo( int a, int b ) { cout << “a is: “ << a << endl; cout << “b is: “ << b << endl; } fp = foo; The address of foo is assigned to fp like this 87

Calling a Function by Using a Function Pointer void (*fp) (int, int); void foo( int a, int b ) { cout << “a is: “ << a << endl; cout << “b is: “ << b << endl; } fp( 5, 10 ); Once the address of foo has been assigned to fp, the foo function can be called using fp like this 88

Design of the HashTable Constructor Once the client designs the hash function, the client passes the name of the hash function, as a parameter into the HashTable constructor The HashTable constructor accepts the parameter using a function pointer in this parameter location The address of the function is saved to a function pointer in the private section Then, the hash table can call the hash function that the client made by using the function pointer 89

HashTable.h 1 #include "LinkedList.h" 2 #include "Array.h“ 3 4 template <class DataType> 5 class HashTable 6 { 7 public: 8 HashTable( int (*hf)(const DataType &), int s ); 9 bool insert( const DataType & newObject ); 10 bool retrieve( DataType & retrieved ); 11 bool remove( DataType & removed ); 12 bool update( DataType & updateObject ); 13 void makeEmpty( ); HashTable.h continued… 90

HashTable.h Space is necessary here 14 private: 15 Array< LinkedList<DataType> > table; 16 int (*hashfunc)(const DataType &); 17 }; 18 19 #include "HashTable.cpp" 91

Clientele The LinkedList class is being used in the HashTable class, along with the Array class Note that when one writes a class the clientele extends beyond the main programmers who might use the class The clientele extends to people who write other classes Remember! Check LinkedList.h because its operations are needed in implementation. 92 92

HashTable Constructor 1 template <class DataType> 2 HashTable<DataType>::HashTable( 3 int (*hf)(const DataType &), int s ) 4 : table( s ) 5 { 6 hashfunc = hf; 7 } This call to the Array constructor creates an Array of LinkedList’s of type DataType Check the private part of .h 93 93

HashTable Constructor (cont.) 1 template <class DataType> 2 HashTable<DataType>::HashTable( 3 int (*hf)(const DataType &), int s ) 4 : table( s ) 5 { 6 hashfunc = hf; 7 } The DataType for Array is LinkedList<DataType> (DataType in Array is different than DataType in HashTable) 94

HashTable Constructor (cont.) 1 template <class DataType> 2 HashTable<DataType>::HashTable( 3 int (*hf)(const DataType &), int s ) 4 : table( s ) 5 { 6 hashfunc = hf; 7 } In the Array constructor, an Array of size s is made, having LinkedList elements – when this array is created, the LinkedList constructor is called for each element. 95

HashTable Constructor (cont.) 1 template <class DataType> 2 HashTable<DataType>::HashTable( 3 int (*hf)(const DataType &), int s ) 4 : table( s ) 5 { 6 hashfunc = hf; 7 } 96

insert 8 template <class DataType> 9 bool HashTable<DataType>::insert( 10 const DataType & newObject ) 11 { 12 int location = hashfunc( newObject ); 13 if ( location < 0 || location >= table.length( ) ) 14 return false; 15 table[ location ].insert( newObject ); 16 return true; 17 } Keep in mind that this is a LinkedList object. 97

retrieve 18 template <class DataType> 19 bool HashTable<DataType>::retrieve( 20 DataType & retrieved ) 21 { 22 int location = hashfunc( retrieved ); 23 if ( location < 0 || location >= table.length( ) ) 24 return false; 25 if ( !table[ location ].retrieve( retrieved ) ) 26 return false; 27 return true; 28 } 98

remove 29 template <class DataType> 30 bool HashTable<DataType>::remove( 31 DataType & removed ) 32 { 33 int location = hashfunc( removed ); 34 if ( location < 0 || location >= table.length( ) ) 35 return false; 36 if ( !table[ location ].remove( removed ) ) 37 return false; 38 return true; 39 } 99

update 40 template <class DataType> 41 bool HashTable<DataType>::update( 42 DataType & updateObject ) 43 { 44 int location = hashfunc( updateObject ); 45 if ( location < 0 || location >= table.length( ) ) 46 return false; 47 if ( !table[location].find( updateObject ) ) 48 return false; 49 table[location].replace( updateObject ); 50 return true; 51 } 100

makeEmpty 50 template <class DataType> 51 void HashTable<DataType>::makeEmpty( ) 52 { 53 for ( int i = 0; i < table.length( ); i++ ) 54 table[ i ].makeEmpty( ); 55 } 101

Using HashTable 1 #include <iostream> 2 #include <string> 3 #include "HashTable.h" 4 5 using namespace std; 6 7 struct MyStruct { 8 string str; 9 int num; 10 bool operator ==( const MyStruct & r ) { return str == r.str; } 11 }; str will be the key 102

Using HashTable (cont.) 1 #include <iostream> 2 #include <string> 3 #include "HashTable.h" 4 5 using namespace std; 6 7 struct MyStruct { 8 string str; 9 int num; 10 bool operator ==( const MyStruct & r ) { return str == r.str; } 11 }; It is necessary to overload the == operator for the LinkedList functions 103

Using HashTable (cont.) 1 #include <iostream> 2 #include <string> 3 #include "HashTable.h" 4 5 using namespace std; 6 7 struct MyStruct { 8 string str; 9 int num; 10 bool operator ==( const MyStruct & r ) { return str == r.str; } 11 }; In the actual code, a comment is placed above HashTable, telling the client that this is needed and what is required. 104

Using HashTable (cont.) 12 const int SIZE1 = 97, SIZE2 = 199; 13 14 int hash1( const MyStruct & obj ); 15 int hash2( const MyStruct & obj ); 16 17 int main( ) 18 { 19 HashTable<MyStruct> ht1( hash1, SIZE1 ), 20 ht2( hash2, SIZE2); 105

Using HashTable (cont.) 21 MyStruct myobj; 22 23 myobj.str = "elephant"; 24 myobj.num = 25; 25 ht1.insert( myobj ); 26 27 myobj.str = "giraffe"; 28 myobj.num = 50; 29 ht2.insert( myobj ); … // other code using the hash tables 106

Using HashTable (cont.) 30 return 0; 31 } 32 33 int hash1( const MyStruct & obj ) 34 { 35 int sum = 0; 36 for ( int i = 0; i < 3 && i < int( obj.str.length( ) ); i++ ) 37 sum += obj.str[ i ]; 38 return sum % SIZE1; 39 } 107

A Hash Table is Like a List The hash table ADT description is very close to the list ADT description The only items missing from the hash table ADT description are: an iterator a function to determine when the “list” is empty find, to determine whether an element is in the “list” a current position If we had all of these, we would have a fast list (or an enhanced hash table) 108

Iterator Everything would be easy to implement for the hash table, except for the iterator The iterator is an important part of a “list”, so we would like it to be as fast as possible We can iterate through a collision list, but finding the next collision list to iterate through might not be so fast… 109

Iterator (cont.) table Large gap with empty linked lists . 110

Iterator (cont.) Instead, we can have a linked list run through the collision lists, so that the linked list contains every element Then, iterating through the linked list is simple and fast 111

Time Complexities for List ADT insert – we’ll insert at the head of the linked list – ( 1 ) iterator – each step will be ( 1 ) find – element is found by hashing, so it is ( 1 ) for uniform hashing (the hash function and hash table are designed so that the length of the collision list is bounded by some small constant) retrieve – is ( 1 ) for uniform hashing more… 112

Time Complexities for List ADT (cont.) replace – is ( 1 ) using the current position an operation to determine whether or not the list is empty – is ( 1 ), because we just test the linked list to see if it is empty an operation to empty out the list – is ( n ), the best we can do, since each node must be freed remove – to make this last operation as fast as possible, consider using a doubly-linked list for the linked list… 113

Remove In Chapter 10, we’ve seen that a node in a doubly-linked list can be removed in ( 1 ) time, given a pointer to it Using hashing, we obtain a collision list, which will have a pointer to the node we wish to remove 114

Doubly-Linked List ADT The description for the doubly-linked list ADT is the same as that for the list ADT We don’t consider implementation in the ADT description, and double links have to do with implementation The implementation of the doubly-linked list using the HashTable is also not a part of the ADT description 115

Avoiding Special Cases To avoid special cases, we’ll have a header node and a trailer node in the doubly-linked list Few data structures use arrays of doubly- linked lists – if such a use arises, we could create a doubly-linked list without header and trailer nodes to avoid wasting memory 116

An Example trailer hash function: h( k ) = k % 11 The red line is the doubly-linked list 33 22 47 31 header 99 36 70 63 53 65 0 1 2 3 4 5 6 7 8 9 10 117

An Example (cont.) trailer hash function: h( k ) = k % 11 Each node has three pointers (not just one). 33 22 47 31 header 99 36 70 63 53 65 0 1 2 3 4 5 6 7 8 9 10 118

An Example (cont.) trailer hash function: h( k ) = k % 11 The elements were inserted in this order: 33, 65, 63, 31, 53, 22, 47, 99, 36, 70 33 22 47 31 header 99 36 70 63 53 65 0 1 2 3 4 5 6 7 8 9 10 119

An Example (cont.) trailer hash function: h( k ) = k % 11 33, 65, 63, 31, 53, 22, 47, 99, 36, 70 – since each node is inserted at the beginning … 33 22 47 31 header 99 36 70 63 53 65 0 1 2 3 4 5 6 7 8 9 10 120

An Example (cont.) trailer hash function: h( k ) = k % 11 33, 65, 63, 31, 53, 22, 47, 99, 36, 70 – you’ll see these nodes from trailer to header. 33 22 47 31 header 99 36 70 63 53 65 0 1 2 3 4 5 6 7 8 9 10 121

An Example (cont.) trailer hash function: h( k ) = k % 11 INSERT: 52 33 22 47 31 header 99 36 70 63 53 65 0 1 2 3 4 5 6 7 8 9 10 122

An Example (cont.) trailer hash function: h( k ) = k % 11 INSERT: 52 The hash function gives us index 8. 33 22 47 31 header 99 36 70 63 53 65 0 1 2 3 4 5 6 7 8 9 10 123

An Example (cont.) trailer hash function: h( k ) = k % 11 INSERT: 52 So 52 is inserted at the beginning of the collision list there… 33 22 47 31 header 99 36 70 63 53 65 0 1 2 3 4 5 6 7 8 9 10 124

An Example (cont.) trailer hash function: h( k ) = k % 11 INSERT: 52 So 52 is inserted at the beginning of the collision list there… 33 22 47 31 header 63 99 36 70 53 65 0 1 2 3 4 5 6 7 8 9 10 125

An Example (cont.) trailer hash function: h( k ) = k % 11 INSERT: 52 So 52 is inserted at the beginning of the collision list there. 33 22 47 63 31 header 99 36 70 53 65 0 1 2 3 4 5 6 7 8 9 10 126

An Example (cont.) trailer hash function: h( k ) = k % 11 INSERT: 52 So 52 is inserted at the beginning of the collision list there. 33 22 47 63 31 header 99 36 70 52 53 65 0 1 2 3 4 5 6 7 8 9 10 127

An Example (cont.) trailer hash function: h( k ) = k % 11 The new node, 52, must also be inserted at the beginning of the doubly-linked list… 33 22 47 63 31 header 99 36 70 52 53 65 0 1 2 3 4 5 6 7 8 9 10 128

An Example (cont.) trailer hash function: h( k ) = k % 11 The new node, 52, must also be inserted at the beginning of the doubly-linked list… 33 22 47 63 31 header 99 36 70 52 53 65 0 1 2 3 4 5 6 7 8 9 10 129

An Example (cont.) trailer hash function: h( k ) = k % 11 The new node, 52, must also be inserted at the beginning of the doubly-linked list 33 22 47 63 31 header 99 36 70 52 53 65 0 1 2 3 4 5 6 7 8 9 10 130

An Example (cont.) trailer hash function: h( k ) = k % 11 REMOVE: 31 33 22 47 63 31 header 99 36 70 52 53 65 0 1 2 3 4 5 6 7 8 9 10 131

An Example (cont.) trailer hash function: h( k ) = k % 11 REMOVE: 31 The hash function gives us index 9, where we’ll find 31 33 22 47 63 31 header 99 36 70 52 53 65 0 1 2 3 4 5 6 7 8 9 10 132

An Example (cont.) trailer hash function: h( k ) = k % 11 REMOVE: 31 Node 53 contains the pointer to it… 33 22 47 63 31 header 99 36 70 52 53 65 0 1 2 3 4 5 6 7 8 9 10 133

An Example (cont.) trailer hash function: h( k ) = k % 11 so 31 can be removed from the doubly-linked list in ( 1 ) time… 33 22 47 63 31 header 99 36 70 52 53 65 0 1 2 3 4 5 6 7 8 9 10 134

An Example (cont.) trailer hash function: h( k ) = k % 11 so 31 can be removed from the doubly-linked list in ( 1 ) time 33 22 47 63 31 header 99 36 70 52 53 65 0 1 2 3 4 5 6 7 8 9 10 135

An Example (cont.) trailer hash function: h( k ) = k % 11 31 is also removed from the collision list using LinkedList remove… 33 22 47 63 31 header 99 36 70 52 53 65 0 1 2 3 4 5 6 7 8 9 10 136

An Example (cont.) trailer hash function: h( k ) = k % 11 31 is also removed from the collision list using LinkedList remove 33 22 47 63 header 99 36 70 52 53 65 0 1 2 3 4 5 6 7 8 9 10 137

Application of HashTable and LinkedList: LRU We want to maintain a cache of N elements that are inserted and accessed frequently 0 1 2 3 4 5 6 7 8 9 10 138

Application of HashTable and LinkedList: LRU We already have N elements. A new element e comes in: we need to remove the oldest, then insert e e 0 1 2 3 4 5 6 7 8 9 10 139

Application of HashTable and LinkedList: LRU But iterating the HashTable’s collision lists to look for the oldest member would be too slow 0 1 2 3 4 5 6 7 8 9 10 140

Application of HashTable and LinkedList: LRU Instead: maintain a list trailer header ……... 0 1 2 3 4 5 6 7 8 9 10 141

Application of HashTable and LinkedList: LRU header trailer This was the first (oldest) 0 1 2 3 4 5 6 7 8 9 10 142

Application of HashTable and LinkedList: LRU If a new element comes in, add it in the front of the list header trailer This was the first (oldest) 0 1 2 3 4 5 6 7 8 9 10 143

Application of HashTable and LinkedList: LRU If a new element comes in, add it in the front of the list header trailer This was the first (oldest) 0 1 2 3 4 5 6 7 8 9 10 144

Application of HashTable and LinkedList: LRU If an element in the HashTable is accessed, move it in the front of the list header trailer access This was the first (oldest) 0 1 2 3 4 5 6 7 8 9 10 145

Application of HashTable and LinkedList: LRU Eventually the HashTable gets full. But now we know that we need to remove the last item trailer header ……... 0 1 2 3 4 5 6 7 8 9 10 146

Application of HashTable and LinkedList: LRU Removed.. trailer header ……... 0 1 2 3 4 5 6 7 8 9 10 147

Application of HashTable and LinkedList: LRU Now add “e” and put it in the head of the list header trailer e ……... 0 1 2 3 4 5 6 7 8 9 10 148

Doubly-Linked List Order The order of the doubly-linked list can be maintained independently of the singly- linked list If we wanted a sorted doubly-linked list using the same insertion order of the elements, it would look like this… 149

Sorted Example hash function: h( k ) = k % 11 33 22 47 31 header 99 36 70 63 53 65 trailer 0 1 2 3 4 5 6 7 8 9 10 150

Sorted Example (cont.) hash function: h( k ) = k % 11 INSERT: 52 33 22 47 31 header 99 36 70 63 53 65 trailer 0 1 2 3 4 5 6 7 8 9 10 151

Sorted Example (cont.) hash function: h( k ) = k % 11 INSERT: 52 The hash function gives us index 8. 33 22 47 31 header 99 36 70 63 53 65 trailer 0 1 2 3 4 5 6 7 8 9 10 152

Sorted Example (cont.) hash function: h( k ) = k % 11 INSERT: 52 So 52 is inserted at the beginning of the collision list there… 33 22 47 31 header 99 36 70 63 53 65 trailer 0 1 2 3 4 5 6 7 8 9 10 153

Sorted Example (cont.) hash function: h( k ) = k % 11 INSERT: 52 So 52 is inserted at the beginning of the collision list there… 33 22 47 31 header 63 99 36 70 53 65 trailer 0 1 2 3 4 5 6 7 8 9 10 154

Sorted Example (cont.) hash function: h( k ) = k % 11 INSERT: 52 So 52 is inserted at the beginning of the collision list there. 33 22 47 63 31 header 99 36 70 53 65 trailer 0 1 2 3 4 5 6 7 8 9 10 155

Sorted Example (cont.) hash function: h( k ) = k % 11 INSERT: 52 So 52 is inserted at the beginning of the collision list there. 33 22 47 63 31 header 99 36 70 52 53 65 trailer 0 1 2 3 4 5 6 7 8 9 10 156

Sorted Example (cont.) hash function: h( k ) = k % 11 52 must also be inserted in the doubly-linked list… 33 22 47 63 31 header 99 36 70 52 53 65 trailer 0 1 2 3 4 5 6 7 8 9 10 157

Sorted Example (cont.) hash function: h( k ) = k % 11 52 must also be inserted in the doubly-linked list. 33 22 47 63 31 header 99 36 70 52 53 65 trailer 0 1 2 3 4 5 6 7 8 9 10 158

Sorted Example (cont.) hash function: h( k ) = k % 11 52 must also be inserted in the doubly-linked list. However… 33 22 47 63 31 header 99 36 70 52 53 65 trailer 0 1 2 3 4 5 6 7 8 9 10 159

Sorted Example (cont.) hash function: h( k ) = k % 11 it would take ( n ) time since its position in the doubly-linked list must be found 33 22 47 63 31 header 99 36 70 52 53 65 trailer 0 1 2 3 4 5 6 7 8 9 10 160

Sorted Example (cont.) hash function: h( k ) = k % 11 REMOVE: 31 33 22 47 63 31 header 99 36 70 52 53 65 trailer 0 1 2 3 4 5 6 7 8 9 10 161

Sorted Example (cont.) hash function: h( k ) = k % 11 REMOVE: 31 The hash function gives us index 9, where we’ll find 31 33 22 47 63 31 header 99 36 70 52 53 65 trailer 0 1 2 3 4 5 6 7 8 9 10 162

Sorted Example (cont.) hash function: h( k ) = k % 11 REMOVE: 31 In ( 1 ) time, it is removed from the doubly-linked list… 33 22 47 63 31 header 99 36 70 52 53 65 trailer 0 1 2 3 4 5 6 7 8 9 10 163

Sorted Example (cont.) hash function: h( k ) = k % 11 REMOVE: 31 In ( 1 ) time, it is removed from the doubly-linked list 33 22 47 63 31 header 99 36 70 52 53 65 trailer 0 1 2 3 4 5 6 7 8 9 10 164

Sorted Example (cont.) hash function: h( k ) = k % 11 REMOVE: 31 In ( 1 ) time, it is also removed from the collision list… 33 22 47 63 31 header 99 36 70 52 53 65 trailer 0 1 2 3 4 5 6 7 8 9 10 165

Sorted Example (cont.) hash function: h( k ) = k % 11 REMOVE: 31 In ( 1 ) time, it is also removed from the collision list 33 22 47 63 header 99 36 70 52 53 65 trailer 0 1 2 3 4 5 6 7 8 9 10 166

Memory Considerations Each node has three pointers now: template <class DataType> struct Node { DataType info; Node<DataType> *next; Node<DataType> *dlnext; Node<DataType> *dlback; }; For the collision list 167

Memory Considerations (cont.) Each node has three pointers now: template <class DataType> struct Node { DataType info; Node<DataType> *next; Node<DataType> *dlnext; Node<DataType> *dlback; }; For the doubly-linked list 168

Memory Considerations (cont.) If there is only one node in the collision list, on average, then the LinkedList used for each element also has two pointers: start and current This gives us a total of 20 bytes of wasted memory per node (housekeeping) 169

Memory Considerations (cont.) If each element is 20 bytes, 50% of memory is wasted However, if each element is 980 bytes, only 2% of memory is wasted Element size is an important consideration 170

LinkedList Considerations In order to make the implementation easy, we’ll have to make some changes to the LinkedList class It will become a specialized “helper” class for the doubly-linked list 171

Changes to LinkedList change the name of the class from LinkedList to CollisionList modify the Node struct so it has the three pointers we need we need a function to retrieve the current pointer, called getcurrent instead of eliminating the current position when we insert a node, we will set the current position to the node we inserted 172

HashTable Class Considerations We’ll make some changes to the HashTable class, too It will, again, be a specialized “helper” class just for use in the doubly-linked list 173

HashTable Changes rename the HashTable class to DLHashTable, short for “Doubly-Linked Hash Table” keep the location, used throughout the class, as a private data member have a function getcurrent, which retrieves the current pointer of the CollisionList that was used in the last use of location; that is, return table[location].getcurrent( ) 174

HashTable Changes We’ll also need some functions which are convenient for the copy constructor and deepCopy gethashfunc, which will return the hash function pointer sethashfunc, which will set a hash function pointer getsize, which will get the Array size of the hash table changeSize, which will change the Array size of the hash table 175

Returning a Function Pointer int (*gethashfunc( ) const)(const DataType &); This is the function prototype for gethashfunc, the function to return the hash function pointer. 176

Returning a Function Pointer (cont.) int (*gethashfunc( ) const)(const DataType &); This is the return type of the function – the return type is spread out instead of being at the beginning of the function heading, where it normally is. 177

Returning a Function Pointer (cont.) int (*gethashfunc( ) const)(const DataType &); The return type indicates we are returning a function pointer which can point to a function that passes in DataType by const reference and returns an integer (describing a hash function) 178

Returning a Function Pointer (cont.) int (*gethashfunc( ) const)(const DataType &); This is the name of the function that returns the function pointer – it has an empty parameter list in this case. 179

Returning a Function Pointer (cont.) int (*gethashfunc( ) const)(const DataType &); This const means that we are not going to change any of the data members in this function (it is the same use of const that you normally see at the end of an isEmpty function heading). 180

Returning a Function Pointer (cont.) The function definition in the implementation file template <class DataType> int (*DLHashTable<DataType>::gethashfunc( ) const) (const DataType &) { return hashfunc; } 181

DoublyLinkedList.h 1 #include "DLHashTable.h" 2 3 template <class DataType> 4 class DoublyLinkedList 5 { 6 public: 7 DoublyLinkedList( 8 int (*hf)(const DataType &), int s ); 9 DoublyLinkedList( 10 const DoublyLinkedList<DataType> & aplist ); 11 ~DoublyLinkedList( ); 12 DoublyLinkedList<DataType> & operator =( 13 const DoublyLinkedList<DataType> & rlist ); 182

DoublyLinkedList.h (cont.) 14 bool insert( const DataType & element ); 15 bool first( DataType & listEl ); 16 inline bool getNext( DataType & listEl ); 17 bool last( DataType & listEl ); 18 inline bool getPrevious( DataType & listEl ); 19 bool find ( const DataType & element ); 20 bool retrieve( DataType & element ); 21 bool remove( DataType & element ); 22 bool replace( const DataType & newElement ); 23 bool isEmpty( ) const; 24 void makeEmpty( ); another iterator 183

DoublyLinkedList.h (cont.) 25 private: 26 DLHashTable<DataType> table; 27 Node<DataType> *current; 28 Node<DataType> headerNode; 29 Node<DataType> trailerNode; 30 Node<DataType> *header; 31 Node<DataType> *trailer; 32 inline void deepCopy( 33 const DoublyLinkedList<DataType> & original ); 34 }; 35 36 #include "DoublyLinkedList.cpp" These are static nodes (not made in the heap). 184

DoublyLinkedList.h (cont.) 25 private: 26 DLHashTable<DataType> table; 27 Node<DataType> *current; 28 Node<DataType> headerNode; 29 Node<DataType> trailerNode; 30 Node<DataType> *header; 31 Node<DataType> *trailer; 32 inline void deepCopy( 33 const DoublyLinkedList<DataType> & original ); 34 }; 35 36 #include "DoublyLinkedList.cpp" header will point to headerNode 185

DoublyLinkedList.h (cont.) 25 private: 26 DLHashTable<DataType> table; 27 Node<DataType> *current; 28 Node<DataType> headerNode; 29 Node<DataType> trailerNode; 30 Node<DataType> *header; 31 Node<DataType> *trailer; 32 inline void deepCopy( 33 const DoublyLinkedList<DataType> & original ); 34 }; 35 36 #include "DoublyLinkedList.cpp" likewise, trailer will point to trailerNode 186

DoublyLinkedList.h (cont.) 25 private: 26 DLHashTable<DataType> table; 27 Node<DataType> *current; 28 Node<DataType> headerNode; 29 Node<DataType> trailerNode; 30 Node<DataType> *header; 31 Node<DataType> *trailer; 32 inline void deepCopy( 33 const DoublyLinkedList<DataType> & original ); 34 }; 35 36 #include "DoublyLinkedList.cpp" We don’t really need the header and trailer pointers, but without them the code may be confusing… 187

DoublyLinkedList.h (cont.) 25 private: 26 DLHashTable<DataType> table; 27 Node<DataType> *current; 28 Node<DataType> headerNode; 29 Node<DataType> trailerNode; 30 Node<DataType> *header; 31 Node<DataType> *trailer; 32 inline void deepCopy( 33 const DoublyLinkedList<DataType> & original ); 34 }; 35 36 #include "DoublyLinkedList.cpp" Without header, we would access the first data node with: headerNode.dlnext 188

DoublyLinkedList.h (cont.) 25 private: 26 DLHashTable<DataType> table; 27 Node<DataType> *current; 28 Node<DataType> headerNode; 29 Node<DataType> trailerNode; 30 Node<DataType> *header; 31 Node<DataType> *trailer; 32 inline void deepCopy( 33 const DoublyLinkedList<DataType> & original ); 34 }; 35 36 #include "DoublyLinkedList.cpp" WITH header, we would access the first data node with: header->dlnext 189

DoublyLinkedList.h (cont.) With this private section, do we really need a destructor, copy constructor, and overloaded assignment operator? 25 private: 26 DLHashTable<DataType> table; 27 Node<DataType> *current; 28 Node<DataType> headerNode; 29 Node<DataType> trailerNode; 30 Node<DataType> *header; 31 Node<DataType> *trailer; 32 inline void deepCopy( 33 const DoublyLinkedList<DataType> & original ); 34 }; 35 36 #include "DoublyLinkedList.cpp" 190

DoublyLinkedList.h (cont.) 25 private: 26 DLHashTable<DataType> table; 27 Node<DataType> *current; 28 Node<DataType> headerNode; 29 Node<DataType> trailerNode; 30 Node<DataType> *header; 31 Node<DataType> *trailer; 32 inline void deepCopy( 33 const DoublyLinkedList<DataType> & original ); 34 }; 35 36 #include "DoublyLinkedList.cpp" YES. There can be pointers to dynamic memory here (for example, header->next). 191

DoublyLinkedList.h (cont.) 25 private: 26 DLHashTable<DataType> table; 27 Node<DataType> *current; 28 Node<DataType> headerNode; 29 Node<DataType> trailerNode; 30 Node<DataType> *header; 31 Node<DataType> *trailer; 32 inline void deepCopy( 33 const DoublyLinkedList<DataType> & original ); 34 }; 35 36 #include "DoublyLinkedList.cpp" Sometimes pointers to dynamic memory are hidden. 192

Constructor 1 template <class DataType> 2 DoublyLinkedList<DataType>::DoublyLinkedList( 3 int (*hf)(const DataType &), int s ) 4 : table( hf, s ), header( &headerNode ), 5 trailer( &trailerNode ) 6 { 7 current = header->dlnext = trailer; 8 trailer->dlback = header; 9 } When current is set to the trailer node, it means there is no current position 193

Copy Constructor 10 template <class DataType> 11 DoublyLinkedList<DataType>::DoublyLinkedList( 12 const DoublyLinkedList<DataType> & aplist ) 13 : table( aplist.table.gethashfunc( ), aplist.table.getsize( ) ) 15 { 16 deepCopy( aplist ); 17 } Calls the constructor for DLHashTable in the copy – passes in the hash function and size of the aplist hash table. 194

Destructor 18 template <class DataType> 19 DoublyLinkedList<DataType>::~DoublyLinkedList( ) 20 { 21 makeEmpty( ); 22 } 195

Overloaded Assignment Operator 23 template <class DataType> 24 DoublyLinkedList<DataType> & 25 DoublyLinkedList<DataType>::operator =( 26 const DoublyLinkedList<DataType> & rlist ) 27 { 28 if ( this == &rlist ) 29 return *this; 30 makeEmpty( ); 31 deepCopy( rlist ); 32 return *this; 33 } 196

insert 34 template <class DataType> 35 bool DoublyLinkedList<DataType>::insert( 36 const DataType & element ) 37 { 38 if ( !table.insert( element ) ) 39 return false; 40 41 current = table.getcurrent( ); When insert in the CollisionList is (eventually) used, it now makes the inserted node the current node. 197

insert (cont.) 34 template <class DataType> 35 bool DoublyLinkedList<DataType>::insert( 36 const DataType & element ) 37 { 38 if ( !table.insert( element ) ) 39 return false; 40 41 current = table.getcurrent( ); This function eventually calls getcurrent in the CollisionList, which returns the current pointer there… 198

insert (cont.) 34 template <class DataType> 35 bool DoublyLinkedList<DataType>::insert( 36 const DataType & element ) 37 { 38 if ( !table.insert( element ) ) 39 return false; 40 41 current = table.getcurrent( ); So the address of the node that was just inserted is assigned to the (different) current pointer here 199

insert (cont.) 34 template <class DataType> 35 bool DoublyLinkedList<DataType>::insert( 36 const DataType & element ) 37 { 38 if ( !table.insert( element ) ) 39 return false; 40 41 current = table.getcurrent( ); insert continued… 200

insert (cont.) 42 current->dlnext = header->dlnext; 43 current->dlback = header; 44 header->dlnext = header->dlnext->dlback = current; 45 current = trailer; 46 47 return true; 48 } In showing how insert works, we’ll use this convention for the parts of Node. info dlback dlnext next 201

insert (cont.) 42 current->dlnext = header->dlnext; 43 current->dlback = header; 44 header->dlnext = header->dlnext->dlback = current; 45 current = trailer; 46 47 return true; 48 } header current collision list start The current node has been inserted into the collision list (line 38 of insert) doubly-linked list 202

insert (cont.) 42 current->dlnext = header->dlnext; 43 current->dlback = header; 44 header->dlnext = header->dlnext->dlback = current; 45 current = trailer; 46 47 return true; 48 } header current collision list start However, it still needs to be placed into the doubly-linked list. doubly-linked list 203

insert (cont.) 42 current->dlnext = header->dlnext; 43 current->dlback = header; 44 header->dlnext = header->dlnext->dlback = current; 45 current = trailer; 46 47 return true; 48 } header current collision list start doubly-linked list 204

insert (cont.) 42 current->dlnext = header->dlnext; 43 current->dlback = header; 44 header->dlnext = header->dlnext->dlback = current; 45 current = trailer; 46 47 return true; 48 } header current collision list start doubly-linked list 205

insert (cont.) 42 current->dlnext = header->dlnext; 43 current->dlback = header; 44 header->dlnext = header->dlnext->dlback = current; 45 current = trailer; 46 47 return true; 48 } header current collision list start doubly-linked list 206

insert (cont.) 42 current->dlnext = header->dlnext; 43 current->dlback = header; 44 header->dlnext = header->dlnext->dlback = current; 45 current = trailer; 46 47 return true; 48 } header current collision list start doubly-linked list 207

insert (cont.) 42 current->dlnext = header->dlnext; 43 current->dlback = header; 44 header->dlnext = header->dlnext->dlback = current; 45 current = trailer; 46 47 return true; 48 } header current collision list start doubly-linked list 208

insert (cont.) 42 current->dlnext = header->dlnext; 43 current->dlback = header; 44 header->dlnext = header->dlnext->dlback = current; 45 current = trailer; 46 47 return true; 48 } header current collision list start doubly-linked list 209

insert (cont.) 42 current->dlnext = header->dlnext; 43 current->dlback = header; 44 header->dlnext = header->dlnext->dlback = current; 45 current = trailer; 46 47 return true; 48 } header current collision list start doubly-linked list 210

insert (cont.) 42 current->dlnext = header->dlnext; 43 current->dlback = header; 44 header->dlnext = header->dlnext->dlback = current; 45 current = trailer; 46 47 return true; 48 } header current collision list start doubly-linked list 211

insert (cont.) 42 current->dlnext = header->dlnext; 43 current->dlback = header; 44 header->dlnext = header->dlnext->dlback = current; 45 current = trailer; 46 47 return true; 48 } header current collision list start doubly-linked list 212

insert (cont.) 42 current->dlnext = header->dlnext; 43 current->dlback = header; 44 header->dlnext = header->dlnext->dlback = current; 45 current = trailer; 46 47 return true; 48 } header current collision list start Now, current has been inserted into the doubly- linked list. doubly-linked list 213

insert (cont.) 42 current->dlnext = header->dlnext; 43 current->dlback = header; 44 header->dlnext = header->dlnext->dlback = current; 45 current = trailer; 46 47 return true; 48 } There won’t be a current position in the DoublyLinkedList after the insertion – the client is using the DoublyLinkedList and this is what the ADT describes. 214

first 49 template <class DataType> 50 bool DoublyLinkedList<DataType>::first( 51 DataType & listEl ) 52 { 53 if ( header->dlnext == trailer ) 54 return false; 55 56 current = header->dlnext; 57 listEl = current->info; 58 return true; 59 } 215

getNext 60 template <class DataType> 61 inline bool DoublyLinkedList<DataType>::getNext( 62 DataType & listEl ) 63 { 64 if ( current->dlnext == trailer ) 65 current = trailer; 66 if ( current == trailer ) 67 return false; 68 69 current = current->dlnext; 70 listEl = current->info; 71 return true; 72 } 216

last 73 template <class DataType> 74 bool DoublyLinkedList<DataType>::last( 75 DataType & listEl ) 76 { 77 if ( header->dlnext == trailer ) 78 return false; 79 80 current = trailer->dlback; 81 listEl = current->info; 82 return true; 83 } 217

getPrevious 84 template <class DataType> 85 inline bool DoublyLinkedList<DataType>::getPrevious( 86 DataType & listEl ) 87 { 88 if ( current->dlback == header ) 89 current = trailer; 90 if ( current == trailer ) 91 return false; 92 93 current = current->dlback; 94 listEl = current->info; 95 return true; 96 } 218

find 97 template <class DataType> 98 bool DoublyLinkedList<DataType>::find( 99 const DataType & element ) 100 { 101 DataType el = element; 102 if ( table.retrieve( el ) ) { 103 current = table.getcurrent( ); 104 return true; 105 } 106 107 current = trailer; 108 return false; 109 } If we pass in element here, retrieve will change the value of it… 219

find (cont.) 97 template <class DataType> 98 bool DoublyLinkedList<DataType>::find( 99 const DataType & element ) 100 { 101 DataType el = element; 102 if ( table.retrieve( el ) ) { 103 current = table.getcurrent( ); 104 return true; 105 } 106 107 current = trailer; 108 return false; 109 } which will give us an error…in find, we are not supposed to retrieve 220

find (cont.) 97 template <class DataType> 98 bool DoublyLinkedList<DataType>::find( 99 const DataType & element ) 100 { 101 DataType el = element; 102 if ( table.retrieve( el ) ) { 103 current = table.getcurrent( ); 104 return true; 105 } 106 107 current = trailer; 108 return false; 109 } So element is copied to el first, then passed into retreive. 221

find (cont.) 97 template <class DataType> 98 bool DoublyLinkedList<DataType>::find( 99 const DataType & element ) 100 { 101 DataType el = element; 102 if ( table.retrieve( el ) ) { 103 current = table.getcurrent( ); 104 return true; 105 } 106 107 current = trailer; 108 return false; 109 } We could have passed by value here (instead of by const reference) to avoid this problem… 222

find (cont.) 97 template <class DataType> 98 bool DoublyLinkedList<DataType>::find( 99 const DataType & element ) 100 { 101 DataType el = element; 102 if ( table.retrieve( el ) ) { 103 current = table.getcurrent( ); 104 return true; 105 } 106 107 current = trailer; 108 return false; 109 } but element copying (pass by value or here) can be avoided altogether by making a find function in DLHashTable (an exercise) 223

find (cont.) 97 template <class DataType> 98 bool DoublyLinkedList<DataType>::find( 99 const DataType & element ) 100 { 101 DataType el = element; 102 if ( table.retrieve( el ) ) { 103 current = table.getcurrent( ); 104 return true; 105 } 106 107 current = trailer; 108 return false; 109 } 224

retrieve 110 template <class DataType> 111 bool DoublyLinkedList<DataType>::retrieve( 112 DataType & element ) 113 { 114 if ( !find( element ) ) 115 return false; 116 117 element = current->info; 118 return true; 119 } 225

remove 120 template <class DataType> 121 bool DoublyLinkedList<DataType>::remove( 122 DataType & element ) 123 { 124 if ( !retrieve( element ) ) 125 return false; 126 current->dlback->dlnext = current->dlnext; 127 current->dlnext->dlback = current->dlback; 128 current = trailer; 129 table.remove( element ); 130 131 return true; 132 } sets element before it is removed 226

remove (cont.) 120 template <class DataType> 121 bool DoublyLinkedList<DataType>::remove( 122 DataType & element ) 123 { 124 if ( !retrieve( element ) ) 125 return false; 126 current->dlback->dlnext = current->dlnext; 127 current->dlnext->dlback = current->dlback; 128 current = trailer; 129 table.remove( element ); 130 131 return true; 132 } also sets current to the node that element is in 227

remove (cont.) 120 template <class DataType> 121 bool DoublyLinkedList<DataType>::remove( 122 DataType & element ) 123 { 124 if ( !retrieve( element ) ) 125 return false; 126 current->dlback->dlnext = current->dlnext; 127 current->dlnext->dlback = current->dlback; 128 current = trailer; 129 table.remove( element ); 130 131 return true; 132 } removes current node from doubly-linked list 228

remove (cont.) 126 current->dlback->dlnext = current->dlnext; 120 template <class DataType> 121 bool DoublyLinkedList<DataType>::remove( DataType & element ) 122 123 { 124 if ( !retrieve( element ) ) 125 return false; 126 current->dlback->dlnext = current->dlnext; 127 current->dlnext->dlback = current->dlback; 128 current = trailer; 129 table.remove( element ); 130 131 return true; 132 } current collision list doubly-linked list 229

remove (cont.) 126 current->dlback->dlnext = current->dlnext; 120 template <class DataType> 121 bool DoublyLinkedList<DataType>::remove( DataType & element ) 122 123 { 124 if ( !retrieve( element ) ) 125 return false; 126 current->dlback->dlnext = current->dlnext; 127 current->dlnext->dlback = current->dlback; 128 current = trailer; 129 table.remove( element ); 130 131 return true; 132 } current collision list doubly-linked list 230

remove (cont.) 126 current->dlback->dlnext = current->dlnext; 120 template <class DataType> 121 bool DoublyLinkedList<DataType>::remove( DataType & element ) 122 123 { 124 if ( !retrieve( element ) ) 125 return false; 126 current->dlback->dlnext = current->dlnext; 127 current->dlnext->dlback = current->dlback; 128 current = trailer; 129 table.remove( element ); 130 131 return true; 132 } current collision list doubly-linked list 231

remove (cont.) 126 current->dlback->dlnext = current->dlnext; 120 template <class DataType> 121 bool DoublyLinkedList<DataType>::remove( DataType & element ) 122 123 { 124 if ( !retrieve( element ) ) 125 return false; 126 current->dlback->dlnext = current->dlnext; 127 current->dlnext->dlback = current->dlback; 128 current = trailer; 129 table.remove( element ); 130 131 return true; 132 } current collision list doubly-linked list 232

remove (cont.) 126 current->dlback->dlnext = current->dlnext; 120 template <class DataType> 121 bool DoublyLinkedList<DataType>::remove( DataType & element ) 122 123 { 124 if ( !retrieve( element ) ) 125 return false; 126 current->dlback->dlnext = current->dlnext; 127 current->dlnext->dlback = current->dlback; 128 current = trailer; 129 table.remove( element ); 130 131 return true; 132 } current collision list doubly-linked list 233

remove (cont.) 128 current = trailer; current collision list 120 template <class DataType> 121 bool DoublyLinkedList<DataType>::remove( DataType & element ) 122 123 { 124 if ( !retrieve( element ) ) 125 return false; 126 current->dlback->dlnext = current->dlnext; 127 current->dlnext->dlback = current->dlback; 128 current = trailer; 129 table.remove( element ); 130 131 return true; 132 } current collision list doubly-linked list 234

remove (cont.) 128 current = trailer; collision list current 120 template <class DataType> 121 bool DoublyLinkedList<DataType>::remove( DataType & element ) 122 123 { 124 if ( !retrieve( element ) ) 125 return false; 126 current->dlback->dlnext = current->dlnext; 127 current->dlnext->dlback = current->dlback; 128 current = trailer; 129 table.remove( element ); 130 131 return true; 132 } collision list current doubly-linked list trailer 235

remove (cont.) 129 table.remove( element ); collision list current 120 template <class DataType> 121 bool DoublyLinkedList<DataType>::remove( DataType & element ) 122 123 { 124 if ( !retrieve( element ) ) 125 return false; 126 current->dlback->dlnext = current->dlnext; 127 current->dlnext->dlback = current->dlback; 128 current = trailer; 129 table.remove( element ); 130 131 return true; 132 } collision list current doubly-linked list trailer 236

remove (cont.) 129 table.remove( element ); 120 template <class DataType> 121 bool DoublyLinkedList<DataType>::remove( DataType & element ) 122 123 { 124 if ( !retrieve( element ) ) 125 return false; 126 current->dlback->dlnext = current->dlnext; 127 current->dlnext->dlback = current->dlback; 128 current = trailer; 129 table.remove( element ); 130 131 return true; 132 } element is in the node current was pointing to collision list current doubly-linked list trailer 237

remove (cont.) 129 table.remove( element ); 120 template <class DataType> 121 bool DoublyLinkedList<DataType>::remove( DataType & element ) 122 123 { 124 if ( !retrieve( element ) ) 125 return false; 126 current->dlback->dlnext = current->dlnext; 127 current->dlnext->dlback = current->dlback; 128 current = trailer; 129 table.remove( element ); 130 131 return true; 132 } element is in the node current was pointing to collision list current doubly-linked list trailer 238

remove (cont.) 131 return true; collision list current 120 template <class DataType> 121 bool DoublyLinkedList<DataType>::remove( DataType & element ) 122 123 { 124 if ( !retrieve( element ) ) 125 return false; 126 current->dlback->dlnext = current->dlnext; 127 current->dlnext->dlback = current->dlback; 128 current = trailer; 129 table.remove( element ); 130 131 return true; 132 } collision list current doubly-linked list trailer 239

replace 133 template <class DataType> 134 bool DoublyLinkedList<DataType>::replace( 135 const DataType & newElement ) 136 { 137 if ( current == trailer ) 138 return false; 139 current->info = newElement; 140 return true; 141 } 240

isEmpty / makeEmpty 142 template <class DataType> 143 bool DoublyLinkedList<DataType>::isEmpty( ) const 144 { 145 return header->dlnext == trailer; 146 } 147 148 template <class DataType> 149 void DoublyLinkedList<DataType>::makeEmpty( ) 150 { 151 table.makeEmpty( ); 152 current = header->dlnext = trailer; 153 trailer->dlback = header; 154 } 241

deepCopy 155 template <class DataType> 156 inline void DoublyLinkedList<DataType>::deepCopy( 157 const DoublyLinkedList<DataType> & original ) 158 { 159 if ( original.table.getsize( ) != table.getsize( ) ) 160 table.changeSize( original.table.getsize( ) ); 161 table.sethashfunc( original.table.gethashfunc( ) ); 162 header = &headerNode; 163 trailer = &trailerNode; 164 Node<DataType> *save = header->dlnext = trailer; 165 trailer->dlback = header; used later to set current 242

deepCopy (cont.) start at the end of the original doubly-linked list 166 Node<DataType> *originalptr = original.trailer->dlback; 167 if ( (originalptr == original.header) || 168 !insert( originalptr->info ) ) 169 return; 243

deepCopy (cont.) 166 Node<DataType> *originalptr = original.trailer->dlback; 167 if ( (originalptr == original.header) || 168 !insert( originalptr->info ) ) 169 return; If original doubly-linked list is empty, we want to return; we’ve already created an empty copy 244

deepCopy (cont.) 166 Node<DataType> *originalptr = original.trailer->dlback; 167 if ( (originalptr == original.header) || 168 !insert( originalptr->info ) ) 169 return; We may not be able to insert because of an error in the client’s hash function (detected in DLHashTable) 245

deepCopy (cont.) 166 Node<DataType> *originalptr = original.trailer->dlback; 167 if ( (originalptr == original.header) || 168 !insert( originalptr->info ) ) 169 return; By starting at the back of the original list and going forwards, we insert each node encountered into the front of the copy – ensuring a duplication. 246

deepCopy (cont.) 192 while ( originalptr->dlback != original.header ) { 193 originalptr = originalptr->dlback; 194 if ( !insert( originalptr->info ) ) { 195 makeEmpty( ); 196 return; 197 } 198 if ( original.current == originalptr ) 199 save = header->dlnext; 200 } 201 202 current = save; 203 } We want the current pointer in the original to correspond to the current pointer in the copy 247

deepCopy (cont.) 192 while ( originalptr->dlback != original.header ) { 193 originalptr = originalptr->dlback; 194 if ( !insert( originalptr->info ) ) { 195 makeEmpty( ); 196 return; 197 } 198 if ( original.current == originalptr ) 199 save = header->dlnext; 200 } 201 202 current = save; 203 } But we can’t set current in the copy yet – on each insert, the current pointer is changed. 248

deepCopy (cont.) 192 while ( originalptr->dlback != original.header ) { 193 originalptr = originalptr->dlback; 194 if ( !insert( originalptr->info ) ) { 195 makeEmpty( ); 196 return; 197 } 198 if ( original.current == originalptr ) 199 save = header->dlnext; 200 } 201 202 current = save; 203 } So we just save the current position of the copy in the save pointer. 249

deepCopy (cont.) 192 while ( originalptr->dlback != original.header ) { 193 originalptr = originalptr->dlback; 194 if ( !insert( originalptr->info ) ) { 195 makeEmpty( ); 196 return; 197 } 198 if ( original.current == originalptr ) 199 save = header->dlnext; 200 } 201 202 current = save; 203 } Whether save was set to trailer (before the while loop), or save was set in the while loop, this sets current correctly. 250