Download presentation
Presentation is loading. Please wait.
1
CMSC 341
2
List.H template <class Object> class List { public: List();
List(const List &rhs); ~List(); const List &operator=(const List &rhs); Bool isEmpty() const; void makeEmpty(); void remove (const Object &x); void insert (const Object &x, const listIter<Object> &p);
3
List.H (cont) ListIter<Object> first()const;
ListIter<Object> zeroth()const; void insert (const Object &x, const listIter<Object> &p); ListIter<Object> find(const Object &x); ListIter<Object> findPrevious(const Object &x); private: ListNode<Object> *header; }; Linked list implementation Use header node to avoid special cases such as insertion on empty list
4
ListNode.H template <class Object> class ListNode {
ListNode(const Object &theElement=Object(), ListNode *n=N) :element(theElement), next(n) {} Object element; ListNode *next; friend class List<Object>; friend class ListItr<Object>; }; Note: all fields are private. Accessible to friend classes. Constructor invoked as: ListNode() -- element will be the the object constructed by the no-argument constructor -- next will be NULL ListNode(const Object &theElement) -- element will be theElement ListNode(const Object& theElement, ListNode *n) -- element will theElement -- next will be n Note: no explicit copy constructor, assignment operator, or destructor
5
ListItr.H template <class Object> class ListItr { public:
ListItr():current(NULL) {} bool isPastEnd() const {return current == NULL;} void advance() { if (!isPastEnd()) current = current->next;} const Object &retrieve() const { if (isPastEnd()) throw BadIterator(); return current->element;} private: ListNode<Object> *current; ListItr(ListNode<Object> *theNode):current(theNode){} friendclass List<Object>; } Keeps a pointer to the ListNode it considers the current one Keeps List as a friend so List can use ListItr’s private constructor The private constructor is the one used by List Why is there a public no-arg constructor? Because we want to be able to use Iterator as ordinary objects. For example, we might want to have a list of iterators over List of int List<ListItr<list>> All objects must have a zero arg constructor. For example, the ListNode used by this List of Iterators uses the no-arg constructor to intialize Note that the no-arg constructor of ListItr produces an iterator that is already exhausted (isPastEnd)
6
Linked List Implementation
template <class Object> List<Object>::List() { header = new ListNode<Object>; } List<Object>::isEmpty() { return !(header->next); Constructs the empty list containing just the header node Asymptotic performance of isEmpty() =O(1) for best, worst, average
7
Linked List Implementation (cont)
template <class Object> ListItr<Object List<Object>::zeroth() { return ListItr<Object>(header); } ListItr<Object> List<Object>::first() { return ListItr<Object>(header->next); Uses private constructor for ListItr. It makes the iterator’s notion of current be the header node. Note: return by value. Why? Because the original in in local scope of the zeroth method. Note: this is a valid iterator even on an empty list. It will be pastEnd
8
Linked List Implementation (cont)
template <class Object> void List<Object>::insert(const Object &x, const ListItr<Object> &p) { if (!p.isPastEnd()) //text uses p.current!=NULL p.current->next = new ListNode<Object>(x,p.current->next); } This is the only place a new ListNode is created (except for header node) If p does not belong to this list, will make a mess of some other list Asymptotic performance: O(1) best, worst, average Element of ListNode is stored as an Object, not a reference. Therefore the node stores a copy of the element passed to insert.
9
Linked List Implementation (cont)
template <class Object> ListItr<Object> List<Object>::find(const Object &x const) { ListNode<Object> *p = header->next; while (p!=NULL && p->element !=x) p = p->next; return ListItr<Object>(p); } Asymptotic performance: O(n) avg and worst O(1) best case (when x is the first element)
10
Linked List Implementation (cont)
template <class Object> ListItr<Object> List<Object>::findPrevious(const Object &x const) { ListNode<Object> *p = header; while (p->next!=NULL && p->next->element !=x) p = p->next; return ListItr<Object>(p); } Empty List: p remains pointed at header x not on list: the while loop moves p until it points to the last node on the list. Asymptotic performance O(n): avg and worst case O(1): best case (x is first element)
11
Linked List Implementation (cont)
template <class Object> void List<Object>::remove(const Object &x) { ListItr<Object> p=findPrevious(x); if (p.current->next != NULL){ ListNode<Object> *oldnode = p.current->next; p.current->next = p.current->next->next; delete old node; } Early on, p could refer to 1. The header if list is empty 2. The last node if x not on list 3. The node before x, otherwise The only place where regular (non-header) nodes get deleted. Asymptotic performance: O(n): avg and worst case because of call to findPrevious O(1) in best case (x first on list)
12
Linked List Implementation (cont)
template <class Object> void List<Object>::makeEmpty() { while (!isEmpty()) remove (first().retrieve()); } Asymptotic performance: O(n) but remove is O(n) and it is called n times. How come it’s not O(n^2)? Because findPrevious,in remove, always finds the first node’s previous in O(1). So. In this case, remove (the best case) is O(1) and makeEmpty = O(n). Note -- a new first iterator is constructed in each iteration. Must do that because the previous iteration goes stale when the node is deleted. Perhaps could do: while (!isEmpty()) remove(header->next->element)
13
Linked List Implementation (cont)
template <class Object> void List<Object>::~List() { makeEmpty(); //deletes all nodes except header delete header; } Asymptotic performance: O(n)
14
Linked List Implementation (cont)
template <class Object> const List<Object>& List<Object>::operator= (const List<Object> &rhs) { if (this != &rhs){ makeEmpty(); ListItr<Object> ritr = rhs.first(); ListItr<Object> itr = zeroth(); while (!ritr.isPastEnd()) { insert(ritr.retrieve(), itr); ritr.advance(); itr.advance(); } return *this; Remember: insert stores a copy of the element. Thereform, assignment is deep copy. Asymptotic performance (lhs n1, rsh n2): makeEmpty: O(n1) iteration over rhs: O(n2) = O(n1) + O(n2) = O(max(n1,n2))
Similar presentations
© 2025 SlidePlayer.com. Inc.
All rights reserved.