Sunteți pe pagina 1din 9

Lecture 11: Linked Lists

PIC 10B Todd Wittman

Data Structures
For the rest of this course, we'll study different ways to store and organize data. Data structures

Vectors Dynamic Arrays Linked Lists Stacks

Queues Binary Search Trees Hash Tables Heaps

We learn different data structures, because each has its advantages and disadvantages. We'll template our data structures, so they can hold different types of data.

Erasing in a Vector
Dynamic arrays and vectors allocate sequential memory blocks. This is good for indexing. If the 10 block below is at address 1201, then the 40 block is at address 1204. But this is bad for inserting and erasing. If we want to erase the 20 block in the picture below, we have to shift all the elements over to the right.
10 20 30 40 10 30 40

Inserting and erasing in a vector is an O(N) operation.

Sec 12.1: Linked Lists


Instead of allocating sequential blocks of memory, we can allow the blocks to be anywhere in the memory heap as long as we have pointers from one block to the next.
20 10 30 40 NULL

This data structure is called a linked list. Each block is called a node. The pointers are called links. A node has two parts: the data and the link to the next node. The last node points to NULL, so we know where the list ends.

Why Should We Use Linked Lists?


Suppose we want to erase the block indicated by the arrow. A pointer that marks our position in the list is called an iterator.
iterator 20 10 30 40 NULL 10 30 40 NULL 20

All we have to do is rearrange the pointers. Then we can safely delete the 20 block, freeing up that block for future use. Erasing is an O(1) operation! Inserting is just as easy!

Why Shouldn't We Use Linked Lists?


In arrays and vectors, the fact that the blocks are sequential allows us to look up the Nth block very quickly. We just add N to the address of the first block. So look-ups in an array/vector are O(1). In a linked list, to get to the Nth block we have to start at the first block and trace a path through N blocks.
20 10 20 30 40 10 30 40 NULL

So look-ups in a linked list are O(N)!

LinkedList vs. Vector


Vector / Array: Look-ups are fast, but inserting and deleting is slow. LinkedList: Look-ups are slow, but inserting and deleting is fast.

Vector Data access Insert / Delete O(1) O(N)

Linked List O(N) O(1)

So we need to ask which operations were going to be performing more often: accessing or changing the data.

Types of Linked Lists


(Singly) Linked List
10 20 30 40 NULL

Doubly Linked List


NULL 10 20 30 40 NULL

Circularly Linked List


10 20 30 40

Today well start building a templated doubly linked list...

The 3 Classes
Our linked list will be built on 3 inter-dependent classes. Node -- Stores one block of data. Private: T data, Node<T>* prev, Node<T>* next
prev data next

LinkedList -- A list of nodes. Private: Node<T>* first, Node<T>* last


first NULL 10 position 20 last 30 NULL

Iterator -- Marks our current position in the list. Private: Node<T>* position

Sec 12.2: The Node Class


To create a list of nodes, we first need to build a Node class. Nodes in a doubly linked list have two pointers: prev and next. template <typename T> class Node { public: prev next Node(T newData); //Constructor data template <typename T> friend class LinkedList; template <typename T> friend class Iterator; private: T data; Node* prev; //Pointer to previous node in list. Node* next; //Pointer to next node in list. }; But how will our LinkedList class access the Nodes data?

The Node Constructor


The Node constructor just needs to set the data to the passed value. Well initialize both pointers to NULL. template <typename T> Node<T>::Node(T newData) { data = newData; prev = NULL; next = NULL; NULL newData NULL } Now our LinkedList class can create a Node. Or just a pointer to a Node using the new command. Node<T>* nodePtr = new Node<T>(value);

The Iterator Class


We will use the Iterator class to move through our list: iter = myList.begin(); iter.forward(); iter.backward();
first NULL position 10 20 last 30 NULL

We will also use the Iterator to mark the position of a node we want to erase or insert. When we erase a node, we will move the iterator to next position in list. myList.erase(iter); When we insert, we will insert the given value before the iterator position. myList.insert(iter, 25);

What results when we apply these 2 operations to the list above?

The Iterator Class


The LinkedList class needs to know the Iterator position for its insert and erase functions. template <typename T> class Iterator { position public: Iterator(); T get() const; //Returns data value we are pointing to. void forward(); //Moves position forward one node. void backward(); //Moves position backward one node. bool isNull(); //Returns true if iterator points to NULL. template <typename T> friend class LinkedList; private: Node<T>* position; };

The -> Operator


But if we create a Node pointer, how do we get at the data fields? If you have a pointer to an object, you can access member functions or private variables with the -> operator. Node<int> *p = new Node(42); p->data = 25; //Same as saying: (*p).data = 25; Well use the -> arrow a lot, because we need to move the Node pointers around. Just for fun, what would these commands do to a list? p->next = NULL; p->next = p->prev; (p->next)->prev = p->next;

The Iterator Class


template <typename T> Iterator<T>::Iterator() { position = NULL; } template <typename T> T Iterator<T>::get() const { return position->data; } template <typename T> bool Iterator<T>::isNull { return (position==NULL); }

template <typename T> void Iterator<T>::forward() { position = position->next; } template <typename T> void Iterator<T>::backward() { position = position->prev; }

Sec 16.2: The LinkedList Class


Were using dynamically allocated memory, so we need to define the Big 4. template <typename T> first last class LinkedList { NULL 10 20 30 NULL public: LinkedList(); ~LinkedList(); ***Well add more functions: insert, erase, operators*** private: Node<T>* first; //Pointer to first node in list. Node<T>* last; //Pointer to last node in list. };

LinkedList Constructor
The default constructor just sets up an empty list. template <typename T> LinkedList<T>::LinkedList( ) { first = NULL; last = NULL; } The template allows us to store lists of ints, strings, etc. LinkedList<string> words; LinkedList<double> numbers; LinkedList<Matrix<double>> markovChains; Well discuss the destructor ~LinkedList() later, because it will use our erase() function.

Setting Begin and End


template <typename T> Iterator<T> LinkedList::begin() const { Iterator<T> iter; iter.position = first; return iter; } Now we can say: Iterator<int> starting = myList.begin(); The end() function would be similar.

S-ar putea să vă placă și