c s d c s d linked list - michigan state universitycse232/weekly/week13/readings/... · 2021. 1....
TRANSCRIPT
4/21/17
1
COMPUTER SCIENCE DEPARTMENT
Linking Data Structures
COMPUTER SCIENCE DEPARTMENT
A linked list is a sequence data structure that consists of Nodes:• Node is a data structure that carries
some information (likely templated).• A Node also contains information
about a successor Node, and potentially a predecessor Node• could actually contain information about
lots of other Nodes
Linked List
COMPUTER SCIENCE DEPARTMENT
An example
Data ptrNode
Data ptrNode
Data ptrNode
head
COMPUTER SCIENCE DEPARTMENT
• each node in the list was (more than likely) dynamically allocated
• the memory each node uses is not contiguous• there is no one solid chunk of memory
but lots of little chunks of memory with pointers
each node dynamically allocated
4/21/17
2
COMPUTER SCIENCE DEPARTMENT
two kinds of dynamic memory
Data ptrNode
Data ptrNode
Data ptrNode
head
vs
ary
COMPUTER SCIENCE DEPARTMENT
• O(1) index lookup• why is that?
• O(n) insertion• why is that?
• O(n) to grow the array size (to dynamically allocate a larger array)• again, why is that?
array
COMPUTER SCIENCE DEPARTMENT
• O(n) indexing• why?
• O(1) insertion, deletion and Nodemovement
• O(1) growing size of the list• can grow or shrink on demand easily
list
COMPUTER SCIENCE DEPARTMENT
• You typically do not do array indexing of a list as this implies a O(n) search• starting from head, traverse the list to
find the nth element.
• Has no capacity (no hunk of memory) but does grow and shrink
Disadvantages
4/21/17
3
COMPUTER SCIENCE DEPARTMENT
STL list
Example 18.1
COMPUTER SCIENCE DEPARTMENT
Not surprisingly, the STL has a list container.
list does not support operator[] nor the .at member function. It does have a set of special member functions that other containers do not
STL list
COMPUTER SCIENCE DEPARTMENT
• Could do a operator[] on a list. STL did not because it is not efficient!
• STL provides efficiency guarantees on its operators an does not implement operators that are inherently efficient• use the right container for the job!
STL is about efficiency
COMPUTER SCIENCE DEPARTMENT
• lst.push_front(val) : append to the front of the list
• lst.pop_front() : remove from the front
• lst.sort() : sort the list in place. Not a generic algorithm, a member function
• lst.reverse() : reverse the list in place. Again, a member function not a generic algorithm
interesting ops (1)
4/21/17
4
COMPUTER SCIENCE DEPARTMENT
• lst.remove(val) : member function that removes all examples of a value from the list.
• lst.merge(lst2) : takes two sorted lists and merges them into one list. lst grows, lst2 is depleted (elements are moved, not copied)
• lst.unique() takes a sorted list and removes consecutive, equal elements
interesting ops(2)
COMPUTER SCIENCE DEPARTMENT
• lst.splice(itr, lst2) : moves the contents of lst2 into lst starting at the position of itr (of lst).
• lst.insert(itr, val) : inserts the value into the list at the location before itr (of lst).
interesting ops(3)
COMPUTER SCIENCE DEPARTMENT
list does not support operator[] so it makes sense that it also doesn't support iterators at a particular location.
bi-directional moves you can move forward(itr++) or backward(itr--) but cannot assign (itr = itr + 3)
only bi-directional iterators
COMPUTER SCIENCE DEPARTMENT
These operations are more efficient because data does not have to be copied!
Instead, pointers to the data can be moved around (very cheap) to achieve the result (sorted, reversed, etc.)
why more efficient?
4/21/17
5
COMPUTER SCIENCE DEPARTMENT
Again, if you want to do linked list stuff, you should use the STL container.
We will cover how to build our own, but the STL is:
• more efficient
• proven
• does memory management.
STL wins
COMPUTER SCIENCE DEPARTMENT
The objects being placed in the list need certain capabilities for all the list member functions to work
• copy constructor. For example, an object inserted into a list is copied.
• operator<. For sort, the object must have a compare operator
• operator==. For unique or remove, must be able to find equal objects
list object capabilities
COMPUTER SCIENCE DEPARTMENT
Make our own
Ex 18.2
COMPUTER SCIENCE DEPARTMENT
Remember this?
Data ptrNode
Data ptrNode
Data ptrNode
head
Singly Linked List
4/21/17
6
COMPUTER SCIENCE DEPARTMENT
This data structure is called a singly linked list:
• singly linked because each Node only knows who its successor is
• has a head pointer that points to the first element of the list
Singly linked list
COMPUTER SCIENCE DEPARTMENT
template <typename T>
class Node{
private:
Node *next_ =nullptr;
T data_ =T();
public:
Node()=default;
Node(T d) : data_(d), next_(nullptr) {};
ostream& print_node(ostream&);
friend class SingleLink<T>;
};
A Node
COMPUTER SCIENCE DEPARTMENT
A singly linked list Node has two private data members:• a T data_ member, the "payload" the
Node carries• a Node<T> *next_ pointer. It can
point to another Node<T> . It is used to point to the successor of this Node
two data members
COMPUTER SCIENCE DEPARTMENT
The process is to link Nodes together by having each Node.next_ point to the next/successor element.
By starting at the beginning, you can traverse the entire list by following the successive next_ pointers.
Linked together
4/21/17
7
COMPUTER SCIENCE DEPARTMENT
Note that we also declare the (as yet not seen) SingleLink class as a friend of the Node class.• we need access to those next_ ptrs.
friend class
COMPUTER SCIENCE DEPARTMENT
Alternatives also exist:
• Node could be a struct that is all public• it is just a “payload”
• Node is a private class of the encompassing list• can’t even see it at the programmer
level, all hidden
struct or private class
COMPUTER SCIENCE DEPARTMENT
To declare the SingleLink as a friend of Node, we have to put a "dummy" class called SingleLink in the file.
It gets over-ridden later by the full def'nbut acts as a place holder here.
"dummy" declarationtemplate<typename T>
class SingleLink{
private:
Node<T> *head_ = nullptr;
Node<T> *tail_ = nullptr;
…
SingleLink
4/21/17
8
COMPUTER SCIENCE DEPARTMENT
To make some operations easier, the SingleLink class maintains two private pointers:• head_, a Node<T> ptr, the first Node
• tail_, a Node<T> ptr, the last Node
Traditionally, SingleLink has only the head_ ptr, but tail_ allows append_back with the same efficiency as append_front
Our linked list
COMPUTER SCIENCE DEPARTMENT
Our SingleLink
data_ next_Node
head_
SingleLink
data_ next_Node
data_ next_Node
tail_
public:SingleLink()=default;SingleLink(Node<T> n) : head_(&n), tail_(&n) {};SingleLink(T d);SingleLink(const SingleLink&);SingleLink& operator=(SingleLink);~SingleLink();void append_front(Node<T> &n);void append_front(T dat);void append_back(Node<T> &n);void append_back(T dat);Node<T>* find(T dat);void insert_after(Node<T> &n, Node<T> *ptr);void insert_after(T dat, Node<T> *ptr);friend ostream& operator<<(ostream& out,
SingleLink<T>& sl){sl.print_list(out);return out;
};};
COMPUTER SCIENCE DEPARTMENT
There is a special pointer in C++ called the nullptr. It is used to represent a pointer that points to nothing.
• you cannot take the address of it
It corrects a problem with old C. Previously used was a constant NULLwhich was equivalent to 0. • int value confused some function
overloads. This is better
nullptr
4/21/17
9
COMPUTER SCIENCE DEPARTMENT
SingleLink() =default;SingleLink(Node<T> n) : head_(&n), tail_(&n) {};
Default respects the default values, both pointers to nullptr
1 param makes a list of 1 Node, both head_ and tail_ point to that Node
Constructors
COMPUTER SCIENCE DEPARTMENT
template<typename T>
SingleLink<T>::SingleLink(T d){
Node<T>* ptr = new Node<T>(d);
head_ = ptr;
tail_ = ptr;
};
constructor for data
COMPUTER SCIENCE DEPARTMENT
template<typename T>
void SingleLink<T>::append_front(Node<T> &n){
if (head_ != nullptr){
n.next_ = head_;
head_ = &n;
}
else {
head_=&n;
tail_=&n;
}
}
append_front
COMPUTER SCIENCE DEPARTMENT
append_front
data_ next_Node
head_
data_ next_Node
…data_ next_Node n
4/21/17
10
COMPUTER SCIENCE DEPARTMENT
n.next_ = head_
data_ next_Node
head_
data_ next_Node
…data_ next_Node n
COMPUTER SCIENCE DEPARTMENT
head_ = &n
data_ next_Node
head_
data_ next_Node
…data_ next_Node n
COMPUTER SCIENCE DEPARTMENT
data_ next_Node
head_
data_ next_Node
…data_ next_Node n
COMPUTER SCIENCE DEPARTMENT
Have to get the order right• assign n.next_ to what head_ points to
• then change head_ to &n
• reverse it, doesn't work
if head_==nullptr, the list is empty• assign both head_ and tail_ to &n
order matters
4/21/17
11
COMPUTER SCIENCE DEPARTMENT
This is a constant time operation as it only involved some pointer manipulation
• doesn't depend on the size of the list
operation is O(1)
COMPUTER SCIENCE DEPARTMENT
template<typename T>void SingleLink<T>::append_front(T dat){
Node<T>* n = new Node<T>(dat);append_front(*n);
}
convenience overload, append data
COMPUTER SCIENCE DEPARTMENT
template<typename T>
void SingleLink<T>::append_back(Node<T> &n){
if (tail_ != nullptr){
tail_->next_ = &n;
tail_ = &n;
}
else {
head_=&n;
tail_=&n;
}
}
append_back
COMPUTER SCIENCE DEPARTMENT
Normally, this operation is an O(n) operation if you only have a head_pointer
• you have to traverse from the beginning to the end
• then the constant overhead of the pointer manipulation
tail_ pointer makes this O(1)
normally O(n)
4/21/17
12
COMPUTER SCIENCE DEPARTMENT
What does tail_->next_ mean?
• tail_ points to the last Node.
• *tail_ is that node
• (*tail).next_ is what tail has as its .next_ value (should be nullptr, but it can be an lvalue and set!)
• tail_->next_ is shortcut for former
remember this?
COMPUTER SCIENCE DEPARTMENT
append_back
data_ next_Node
data_ next_Node
tail_
data_ next_Node n
nullptr…
nullptr
COMPUTER SCIENCE DEPARTMENT
tail_->next_ = &n
data_ next_Node
data_ next_Node
tail_
data_ next_Node n
nullptr
COMPUTER SCIENCE DEPARTMENT
tail_ = &n
data_ next_Node
data_ next_Node
tail_
data_ next_Node n
4/21/17
13
COMPUTER SCIENCE DEPARTMENT
data_ next_Node
data_ next_Node
tail_
data_ next_Node n
…
COMPUTER SCIENCE DEPARTMENT
template<typename T>void SingleLink<T>::append_back(T dat){
Node<T>* n = new Node<T>(dat);append_back(*n);
}
convenience overload, append data
COMPUTER SCIENCE DEPARTMENT
insert n into the list after ptr.• can't insert before head_, hence append_front
Time to find ptr is not included as part of insert.
• find would be O(n) time
Therefore insert is also O(1), just pointer manipulation
insert(Node<T> &n, Node<T> *ptr)
COMPUTER SCIENCE DEPARTMENT
template<typename T>void SingleLink<T>::insert(Node<T> &n, Node<T> *ptr){if (ptr != nullptr){n.next_ = ptr->next_;ptr->next_ = &n;if (ptr == tail_)tail_ = &n;
}}
4/21/17
14
COMPUTER SCIENCE DEPARTMENT
data_ next_Node
data_ next_Node
… …
data_ next_Node n
ptr
COMPUTER SCIENCE DEPARTMENT
n.next_ = ptr->next_
data_ next_Node
data_ next_Node
… …
data_ next_Node n
ptr
COMPUTER SCIENCE DEPARTMENT
ptr->next_=&n
data_ next_Node
data_ next_Node
… …
data_ next_Node n
ptr
COMPUTER SCIENCE DEPARTMENT
data_ next_Node
data_ next_Node
… …data_ next_Node n
4/21/17
15
COMPUTER SCIENCE DEPARTMENT
If ptr points to the same Node as tail_, we must also update tail_
COMPUTER SCIENCE DEPARTMENT
template<typename T>Node<T> * SingleLink<T>::find(T dat){
Node<T> *result = nullptr;for(Node<T> *n = head_;
n != nullptr; n = n->next_){
if (n->data_ == dat){result = n;break;
} // of if} // of forreturn result;
}
find
COMPUTER SCIENCE DEPARTMENT
Node<T> * n = head_
data_ next_Node
head_
SingleLink
data_ next_Node
data_ next_Node
tail_n
nullptr
COMPUTER SCIENCE DEPARTMENT
n->data_ == dat
data_ next_Node
head_
SingleLink
data_ next_Node
data_ next_Node
tail_n
nullptr
4/21/17
16
COMPUTER SCIENCE DEPARTMENT
n = n->next
data_ next_Node
head_
SingleLink
data_ next_Node
data_ next_Node
tail_n
nullptr
COMPUTER SCIENCE DEPARTMENT
Singly linked list can only be traversed in one direction
• you can only iterate from the beginning through the elements
• can be a restriction• STL has a forward_list that is
forward iteration only
forward traversal only
COMPUTER SCIENCE DEPARTMENT
template<typename T>void SingleLink<T>::insert_after(
T dat, Node<T> *ptr){Node<T> *n = new Node<T>(dat);insert_after(*n, ptr);
}
convenience overload, insert data
COMPUTER SCIENCE DEPARTMENT
template<typename T>
SingleLink<T>::SingleLink(constSingleLink& sl){
if (sl.head_ == nullptr){
head_ = nullptr;
tail_ = nullptr;
}
…
copy constructor
4/21/17
17
else{
head_ = new Node<T>(sl.head_->data_);
tail_ = head_;
Node<T>* sl_ptr = sl.head_->next_;
Node<T>* new_node;
while (sl_ptr != nullptr){
new_node = new Node<T>(sl_ptr->data_);
tail_->next_ = new_node;
sl_ptr = sl_ptr->next_;
tail_ = new_node;
} // of while
} // of else
} // of constructor
copy (2)data_ next_
Node
sl.head_
SingleLink parameter sl to copy
data_ next_Node
data_ next_Node
sl.tail_
head_ = new Node<T>(sl.head_->data_);tail_ = head_;
data_ next_Node
head_ tail_
data_ next_Node
sl.head_
data_ next_Node
data_ next_Node
sl.tail_
Node<T>* sl_ptr = sl.head_->next_;while (sl_ptr != nullptr){
new_node = new Node<T>(sl_ptr->data_);tail_->next_ = new_node;
data_ next_Node
head_ tail_
data_ next_Node
new_node
sl_ptr
data_ next_Node
sl.head_
data_ next_Node
data_ next_Node
sl.tail_
sl_ptr = sl_ptr->next_;tail_ = new_node;
} // of while
data_ next_Node
head_ tail_
data_ next_Node
new_node
sl_ptr
4/21/17
18
COMPUTER SCIENCE DEPARTMENT
Copy and Swaptemplate<typename T>
SingleLink<T>& SingleLink<T>::operator=
(SingleLink sl){
swap(head_, sl.head_);
swap(tail_, sl.tail_);
}
Call tocopy ctor
sl destroyed atend of call (cleansup old memory)
COMPUTER SCIENCE DEPARTMENT
template<typename T>SingleLink<T>::~SingleLink(){Node<T>* to_del = head_;while (to_del != nullptr){
head_ = head_->next_;delete to_del;to_del = head_;
}head_ = nullptr;tail_ = nullptr;
}
Destructor
data_ next_Node
head_
data_ next_Node
data_ next_Node
tail_
Node<T>* to_del = head_;while (to_del != nullptr){
to_del
data_ next_Node
head_
data_ next_Node
data_ next_Node
tail_
// one passhead_ = head_->next_;delete to_del;to_del = head_;
to_del
X
4/21/17
19
COMPUTER SCIENCE DEPARTMENT
Doubly linked list
Example 18.4
COMPUTER SCIENCE DEPARTMENT
A doubly linked list Node has two pointers:
• a successor pointer
• a predecessor pointer
This is what the STL list has, and allows for movement forward and backwards through the list.
• hence bi-directional iterators
two pointers
COMPUTER SCIENCE DEPARTMENT
two pointers
head
Doubly Linked List
Dataptr
Node
ptrData
ptrNode
ptrData
ptrNode
ptr
COMPUTER SCIENCE DEPARTMENT
template <typename T>
class Node{
private:
Node *next_;
Node *prev_;
T data_;
string tail_4(Node<T> *ptr);
public:
Node() : next_(nullptr), prev_(nullptr) {};
Node(T d) : data_(d), next_(nullptr), prev_(nullptr) {};
ostream& print_node(ostream&);
friend class DoubleLink<T>;
};
Node
4/21/17
20
COMPUTER SCIENCE DEPARTMENT
private function to print the last 4 values (in hex) of an address
• makes it easier to read
• allows you to print more out
• just a detail
tail_4 // insert new node n after *ptr
template<typename T>
void DoubleLink<T>::insert(Node<T> &n,Node<T> *ptr){
if (ptr != nullptr){
n.next_ = ptr->next_;
n.prev_ = ptr;
(ptr->next_)->prev_ = &n;
ptr->next_ = &n;
if (ptr == tail_)
tail_ = &n;
}
};
insert
COMPUTER SCIENCE DEPARTMENT
ptr
Datanext
Node
prevData
nextbefore
prevData
nextafter
prev
Datanext
Node n
prev
COMPUTER SCIENCE DEPARTMENT
n.next_=ptr->next_
ptr
Datanext
Node
prevData
nextbefore
prevData
nextafter
prev
Datanext
Node n
prev
4/21/17
21
COMPUTER SCIENCE DEPARTMENT
n.prev_=ptr
ptr
Datanext
Node
prevData
nextbefore
prevData
nextafter
prev
Datanext
Node n
prev
COMPUTER SCIENCE DEPARTMENT
We need to find the prev_ pointer of the after node.
• we need to have after.prev_ point at n
In stages:
• ptr, points to the before node
• ptr->next_, the after node
• (ptr->next_)->prev_, the after node's prev_ pointer
(ptr->next_)->prev_= &n
COMPUTER SCIENCE DEPARTMENT
(ptr->next_)->prev_= &n
ptr
Datanext
Node
prevData
nextbefore
prevData
nextafter
prev
Datanext
Node n
prev
COMPUTER SCIENCE DEPARTMENT
ptr->next_= &n
ptr
Datanext
Node
prevData
nextbefore
prevData
nextafter
prev
Datanext
Node n
prev