Node-based storage with arrays

Slides:



Advertisements
Similar presentations
ECE 250 Algorithms and Data Structures Douglas Wilhelm Harder, M.Math. LEL Department of Electrical and Computer Engineering University of Waterloo Waterloo,
Advertisements

ECE 250 Algorithms and Data Structures Douglas Wilhelm Harder, M.Math. LEL Department of Electrical and Computer Engineering University of Waterloo Waterloo,
More Trees COL 106 Amit Kumar and Shweta Agrawal Most slides courtesy : Douglas Wilhelm Harder, MMath, UWaterloo
ECE 250 Algorithms and Data Structures Douglas Wilhelm Harder, M.Math. LEL Department of Electrical and Computer Engineering University of Waterloo Waterloo,
ECE 250 Algorithms and Data Structures Douglas Wilhelm Harder, M.Math. LEL Department of Electrical and Computer Engineering University of Waterloo Waterloo,
ECE 250 Algorithms and Data Structures Douglas Wilhelm Harder, M.Math. LEL Department of Electrical and Computer Engineering University of Waterloo Waterloo,
ECE 250 Algorithms and Data Structures Douglas Wilhelm Harder, M.Math. LEL Department of Electrical and Computer Engineering University of Waterloo Waterloo,
ECE 250 Algorithms and Data Structures Douglas Wilhelm Harder, M.Math. LEL Department of Electrical and Computer Engineering University of Waterloo Waterloo,
Douglas Wilhelm Harder, M.Math. LEL Department of Electrical and Computer Engineering University of Waterloo Waterloo, Ontario, Canada ece.uwaterloo.ca.
ECE 250 Algorithms and Data Structures Douglas Wilhelm Harder, M.Math. LEL Department of Electrical and Computer Engineering University of Waterloo Waterloo,
ECE 250 Algorithms and Data Structures Douglas Wilhelm Harder, M.Math. LEL Department of Electrical and Computer Engineering University of Waterloo Waterloo,
1 Linked Lists Assignment What about assignment? –Suppose you have linked lists: List lst1, lst2; lst1.push_front( 35 ); lst1.push_front( 18 ); lst2.push_front(
1 Data Structures and Algorithms Outline This topic will describe: –The concrete data structures that can be used to store information –The basic forms.
1 Matrix Data Structures Outline In this topic, we will cover the representation of graphs on a computer We will examine: –an adjacency matrix representation.
ECE 250 Algorithms and Data Structures Douglas Wilhelm Harder, M.Math. LEL Department of Electrical and Computer Engineering University of Waterloo Waterloo,
Outline This topic covers merge sort
The mechanics of recursion
Sorted Linked List Same objective as a linked list, but it should be sorted Sorting can be custom according to the type of nodes Offers speedups over non-sorted.
Stack.
Topological Sort In this topic, we will discuss: Motivations
Outline This topic discusses the insertion sort We will discuss:
Recursive binary search
Templates.
Introduction to classes
Poisson distribution.
Dynamically allocating arrays
Dangling pointers.
Open addressing.
Outline In this topic we will look at quicksort:
Sorted arrays.
Dynamically allocating arrays within structures
Outline This topic covers Prim’s algorithm:
Dynamic memory allocation
Linked Lists.
Wild pointers.
Selection sort.
Bucket sort.
The ternary conditional operator
Dynamically allocating structures
Memory leaks.
Pushing at the back.
Sorting algorithms.
Passing pointers as parameters to and from functions
Templated Linked Lists
Chapter 17: Linked Lists.
Dynamically allocating arrays
Insertion sort.
Problems with pointers
A list-size member variable
Protecting pointers.
Dynamically allocating arrays
Selection sort.
Insertion sort.
Pointers as arguments and return values
Class variables and class functions
Dynamic allocation of arrays
Templates.
This.
Dynamic memory allocation
Insertion sort.
Issues with classes.
Dangling pointers.
Counting sort.
5.3 Implementing a Stack Chapter 5 – The Stack.
Selection sort.
Searching and sorting arrays
Data structures: class
An array class: constructor and destructor
Recursive binary search
Algorithms and templates
Presentation transcript:

Node-based storage with arrays

Outline In this presentation, we will cover: The costs of node-based storage Using an array for node-based storage Having multiple linked lists within a single structure Reallocation of memory

The issue A significant issue with linked lists (and later, trees) are that node-based data structures require Q(n) calls to new This requires a call to the operating system requesting a memory allocation

Using an array? Suppose we store this linked list in an array? A E P S list_head = 5; list_tail = 2; 1 2 3 4 5 6 7 A E P S C -1

Using an array? Rather than using, -1, use a constant assigned that value This makes reading your code easier list_head = 5; list_tail = 2; 1 2 3 4 5 6 7 A E P S C NULLPTR

A solution To achieve this, we must create an array of objects that: Store the value Store the array index where the next entry is stored template <typename Type> class Single_node { private: Type node_value; next_node int; public: Type value() const; int next() const; };

A solution Now, memory allocation is done once in the constructor: template <typename Type> class Single_list { private: int list_capacity; int list_head; int list_tail; int list_size; Single_node<Type> *node_pool; static const int NULL; public: Single_list( int = 16 ); // member functions }; const int Single_list::NULLPTR = -1; template <typename Type> Single_list<Type>::Single_list( int n ): list_capacity( std::max( 1, n ) ), list_head( NULLPTR ), list_tail( NULLPTR ), list_size( 0 ), node_pool( new Single_node<Type>[n] ) { // Empty constructor }

A solution Question: how do you track unused nodes? A E P S C We could keep a separate container (a stack) which contains the indices of all unused nodes list_head = 5; list_tail = 2; 1 2 3 4 5 6 7 A E P S C NULLPTR stack_size = 3; 1 2 3 4 5 6 7

A solution The stack would be initialized with all the entries list_head = NULLPTR; list_tail = NULLPTR; 1 2 3 4 5 6 7 stack_size = 8; 1 2 3 4 5 6 7

A solution When pushing onto the list, the entry at the top of the stack is used list_head = NULLPTR; list_tail = NULLPTR; 1 2 3 4 5 6 7 stack_size = 8; 1 2 3 4 5 6 7

A solution Now, push_front( 'O' ) would result in O list_head = 7; list_tail = 7; 1 2 3 4 5 6 7 O NULLPTR stack_size = 7; 1 2 3 4 5 6 7

A solution Suppose we call push_front( 'N' ) O list_head = 7; list_tail = 7; 1 2 3 4 5 6 7 O NULLPTR stack_size = 7; 1 2 3 4 5 6 7

A solution Suppose we call push_front( 'N' ) N O The next node is at index 6 list_head = 6; list_tail = 7; 1 2 3 4 5 6 7 N O NULLPTR stack_size = 6; 1 2 3 4 5 6 7

A solution Suppose we call push_back( 'R' ) N O list_head = 6; list_tail = 7; 1 2 3 4 5 6 7 N O NULLPTR stack_size = 6; 1 2 3 4 5 6 7

A solution Suppose we call push_back( 'R' ) R N O The next node is at index 5 list_head = 6; list_tail = 5; 1 2 3 4 5 6 7 R N O NULLPTR stack_size = 5; 1 2 3 4 5 6 7

A solution Finally, suppose we call pop_front() R N O list_head = 6; list_tail = 5; 1 2 3 4 5 6 7 R N O NULLPTR stack_size = 5; 1 2 3 4 5 6 7

A solution Finally, suppose we call pop_front() R N O The popped node is placed back into the stack list_head = 7; list_tail = 5; 1 2 3 4 5 6 7 R N O NULLPTR stack_size = 6; 1 2 3 4 5 6 7

A better solution Problem: A E P S C Our solution requires Q(N) additional memory In our initial example, the unused nodes are 1, 4 and 7 How about using these to define a second stack-as-linked-list? list_head = 5; list_tail = 2; 1 2 3 4 5 6 7 A E P S C NULLPTR

A better solution Problem: A E P S C Our solution requires Q(N) additional memory In our initial example, the unused nodes are 1, 4 and 7 How about using these to define a second stack-as-linked-list? We only need a head pointer for the stack-as-linked-list The top of the stack is the first node list_head = 5; list_tail = 2; stack_top = 1; 1 2 3 4 5 6 7 A E P S C NULLPTR

A better solution Suppose we call pop_front() A E P S C list_head = 5; list_tail = 2; stack_top = 1; 1 2 3 4 5 6 7 A E P S C NULLPTR

A better solution Suppose we call pop_front() A E P S C The extra node is placed onto the stack list_head = 3; list_tail = 2; stack_top = 5; 1 2 3 4 5 6 7 A E P S C NULLPTR

A better solution Suppose we now call push_back( 'D' ) A E P S C list_head = 3; list_tail = 2; stack_top = 5; 1 2 3 4 5 6 7 A E P S C NULLPTR

A better solution Suppose we now call push_back( 'D' ) A E P D C We pop the node off of the top of the stack list_head = 3; list_tail = 5; stack_top = 1; 1 2 3 4 5 6 7 A E P D C NULLPTR

A better solution Suppose we finally call pop_front() again A E P D C list_head = 3; list_tail = 5; stack_top = 1; 1 2 3 4 5 6 7 A E P D C NULLPTR

A better solution Suppose we finally call pop_front() again A E P D C The node containing 'P' is pushed back onto the stack list_head = 0; list_tail = 5; stack_top = 3; 1 2 3 4 5 6 7 A E P D C NULLPTR

A better solution In this case, our data structure would be initialized to: list_head = NULLPTR; list_tail = NULLPTR; stack_top = 0; 1 2 3 4 5 6 7 NULLPTR

A solution Our class would look something like: template <typename Type> class Single_list { private: int list_head; int list_tail; int list_size; int list_capacity; Single_node<Type> *node_pool; int stack_top; static const int NULL; public: Single_list( int = 16 ); // member functions }; const int Single_list::NULLPTR = -1; template <typename Type> Single_list<Type>::Single_list( int n ): list_head( NULLPTR ), list_tail( NULLPTR ), list_size( 0 ), list_capacity( std::max( 1, n ) ), node_pool( new Single_node<Type>[n] ), stack_top( 0 ) { for ( int i = 1; i < capacity(); ++i ) { node_pool[i - 1].next = i; } node_pool[capacity() - 1] = NULLPTR;

Analysis This solution: Requires only three more member variable than our linked list class It still requires O(N) additional memory over an array All the run-times are identical to that of a linked list Only one call to new, as opposed to Q(n) There is a potential for up to O(N) wasted memory Question: What happens if we run out of memory?

Reallocation of memory Suppose we start with a capacity N but after a while, all the entries have been allocated We can double the size of the array and copy the entries over list_head = 6; list_tail = 4; list_size = 8; list_capacity = 8; stack_top = NULLPTR; 1 2 3 4 5 6 7 C R U T S NULLPTR

Reallocation of memory Suppose we start with a capacity N but after a while, all the entries have been allocated We can double the size of the array and copy the entries over Only the stack needs to be updated and the old array deleted list_head = 6; list_tail = 4; list_size = 8; list_capacity = 16; stack_top = 8; 1 2 3 4 5 6 7 C R U T S NULLPTR 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 C R U T S NULLPTR

Reallocation of memory Now push_back( 'E' ) would use the next location list_head = 6; list_tail = 4; list_size = 8; list_capacity = 16; stack_top = 8; 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 C R U T S NULLPTR

Reallocation of memory Now push_back( 'E' ) would use the next location list_head = 6; list_tail = 8; list_size = 9; list_capacity = 16; stack_top = 9; 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 C R U T S E NULLPTR

Reallocation of memory If at some point, we decide it is desirable to reduce the memory allocated, it might be easier to just insert the entries into a newer and smaller table list_head = 4; list_tail = 5; list_size = 4; list_capacity = 16; stack_top = 7; 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 T D A NULLPTR

Reallocation of memory If at some point, we decide it is desirable to reduce the memory allocated, it might be easier to just insert the entries into a newer and smaller table list_head = 4; list_tail = 5; list_size = 4; list_capacity = 16; stack_top = 7; 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 T D A NULLPTR 1 2 3 4 5 6 7 D A T NULLPTR

Reallocation of memory If at some point, we decide it is desirable to reduce the memory allocated, it might be easier to just insert the entries into a newer, and smaller table Now, delete the old array and update the member variables list_head = 0; list_tail = 3; list_size = 4; list_capacity = 8; stack_top = 4; 1 2 3 4 5 6 7 D A T NULLPTR

Summary In this presentation, we covered Dealing with node-based allocation with arrays Internally, it is still a linked list, only the nodes are contiguous in memory It is no longer necessary to call the operating system for each new node Doubling the memory used is straight-forward To halve the memory used, we just follow the linked list All of this will transfer seamlessly

References Wikipedia, http://en.wikipedia.org/wiki/Search_algorithm These slides are provided for the ECE 250 Algorithms and Data Structures course. The material in it reflects Douglas W. Harder’s best judgment in light of the information available to him at the time of preparation. Any reliance on these course slides by any party for any other purpose are the responsibility of such parties. Douglas W. Harder accepts no responsibility for damages, if any, suffered by any party as a result of decisions made or actions based on these course slides for any other purpose than that for which it was intended.