Linear Structure Linear Structures Chapter 4 CPTR 318 Every non-empty linear structure has A unique element called first A unique element called last Every element except last has a unique successor Every element except first has a unique predecessor 1 2 Implementations Array Contiguous memory Space needed only for data Insertions and deletions may require data movement Linked list Non-contiguous memory Data and links both require space Insertions and deletions require no data movement Array details For an array A of size n, where n > 0: First element: A[0] Last element: A[n 1] Predecessor of A[i], for all i > 0: A[i 1] Successor of A[i], for all i < n 1: A[i + 1] n A 0 i 1 i i + 1 n 1 List details struct Node T data; Node *prev, *next; class List Node<T> *head, *tail; // Methods omitted For a list L, where L.head is not null, and p is a Node<T> pointer to an element in the list: First element: L.head Last element: L.tail Predecessor of p, for all p L.head: p->prev Successor of p, for all p L.tail: p->next Searching Ordered Search O(log n) Insertion O(1) Deletion O(1) Random Access O(1) 1
Searching Ordered Search O(log n) Insertion O(1) Deletion O(1) Random Access O(1) Searching Ordered Search O(log n) Insertion O(1) Deletion O(1) Random Access O(1) Searching Ordered Search O(log n) Insertion O(1) Deletion O(1) Random Access O(1) Searching Ordered Search O(log n) Insertion O(1) Deletion O(1) Random Access O(1) Searching Ordered Search O(log n) Insertion O(1) Deletion O(1) Random Access O(1) Searching Ordered Search O(log n) Insertion O(1) Deletion O(1) Random Access O(1) 2
Specialized Linear Structures Stack LIFO : push, pop, top Queue FIFO : enqueue, dequeue, first Deque Access at both ends : insert_front, insert_back, delete_front, delete_back, front, back Stack LIFO push pop top Applications Depth-first search Recursive evaluation Queue FIFO enqueue dequeue front Applications Breadth-first search Scheduling Simulations Circular Queue Page 131 Encapsulation Good! Data structure encapsulation is also known as data hiding Client need not worry about the internal details Client is concerned with what, not how The author of the encapsulating class is free to change the internals of the encapsulated class without breaking client code Wonderful for software maintenance issues: fixes, optimizations, etc. Encapsulation Bad! The client is insulated from accessing the individual elements of the data structure The client is at the mercy of the access operations provided by the encapsulating class What if I want to print only the items in a list of integers that are even? 17 18 3
Encapsulation Dilemma Iterator How can we provide access to individual elements of an encapsulated data set without exposing the implementation of the data structure? This problem has been encountered many times in the past Fortunately a standard solution has been devised An iterator is an object that provides access to individual elements of a collection in a standard way without exposing the implementation details of that collection The collection can be real or virtual Traversing a list vs. generating a random number 19 20 Design Patterns Iterator is one example of a design pattern A design pattern in software is a design that solves a programming problem The design has application beyond the particular problem at hand Concept originally proposed by Christopher Alexander for architectural design GoF book The iterator concept is one example of a design pattern Structure of a Design Pattern Name Context for its use Problem description Solution 22 23 Iterator Design Pattern Name: Iterator Context for its use: An encapsulated collection of data elements Problem description: Need to provide client-controlled access to individual elements of the collection without exposing the underlying data structure of the collection holding those elements Solution: Provide a separate object that has data structure-specific knowledge of the collection, but that presents to the client generic operations that access elements in an implementation-independent manner The Standard Template Library. The STL provides a collection of data structures and provides some generic algorithms, such as sorting. As its name suggests, the STL makes heavy use of templates. All compiler that implement the standard has the library available. 24 4
Basic Data Structures Data Structure is a representation of data and the operations allowed on that data. Basic Data Structures List Stack Queue Sets Maps Priority Queues STL basic components Containers Iterators Algorithms STL Containers STL Iterators A collection of objects (elements) Must support: bool empty() iterator begin() iterator end() int size() Iterators maintain a notion of a current position in the container and provides basic operations such as the ability to advance to the next position and access the item in the current position Must support: itr++ itr-- (optional) *itr ==!= Each container could define several iterators const_iterator must be used to traverse const containers C++11 Smart Pointers // Implements individual nodes within a singly-linked list struct ListNode // The value of interest in a list element T data; // A link to the next element in the list; null, if this node is the last element shared_ptr<listnode<t>> next; ListNode<T>(const T& elem): data(elem), next(nullptr) } ListNode<T>(const T& elem, shared_ptr<listnode<t>> n): data(elem), next(n) } Singly-linked List // Implements singly-linked list objects. class LinkedList // Points to the first element of this list; null if the list is empty shared_ptr<listnode<t>> head; // Points to the last element of this list; null if the list is empty shared_ptr<listnode<t>> tail; public: // Creates a new, empty list object LinkedList<T>(): head(nullptr), tail(nullptr) } // Inserts new element elem onto the back of this list object void insert(const T& elem) } 5
Reference Counting Insert 12 Insert 4 Insert 22 Insert 7 6
7
Doubly-linked Lists 2 2 2 2 Doubly-linked Lists 2 2 2 2 1 2 2 1 8
Doubly-linked Lists 1 2 2 1 Now what? Weak Pointers (weak_ptr<t>) // Implements individual nodes within a singly-linked list struct ListNode // The value of interest in a list element T data; // A link to the next element in the list; null, if this node is the last element shared_ptr<listnode<t>> next; // A link to the previous element in the list; null, if this node is the first element weak_ptr<listnode<t>> prev; ListNode<T>(const T& elem): data(elem), next(nullptr) } ListNode<T>(const T& elem, shared_ptr<listnode<t>> n): data(elem), next(n) } Weak Pointers (weak_ptr<t>) // Implements individual nodes within a singly-linked list struct ListNode // The value of interest in a list element T data; // A link to the next element in the list; null, if this node is the last element shared_ptr<listnode<t>> next; // A link to the previous element in the list; null, if this node is the first element weak_ptr<listnode<t>> prev; ListNode<T>(const T& elem): data(elem), next(nullptr) } Weak pointers 1 1 1 2 ListNode<T>(const T& elem, shared_ptr<listnode<t>> n): data(elem), next(n) } Weak pointers Weak pointers shared_ptr<listnode<t>> 1 1 1 2 weak_ptr<listnode<t>> 1 1 1 2 Create a strong pointer from a weak pointer using lock 9
Weak pointers Weak pointers 1 1 2 2 p Create a strong pointer from a weak pointer using lock: shared_ptr<listnode<t>> p = tail->prev.lock() 1 2 1 2 p Create a strong pointer from a weak pointer using lock: p = p->prev.lock() 1 1 1 2 1 1 1 2 0 1 1 1 1 1 1 10
0 1 1 1 1 0 1 1 0 11