Linked List Example
• Polynomials– Interested in representing and manipulating
polynomials – Polynomial defined as:
• Y = coef_n * xexp_n + coef_n-1 * xexp_n-1 + … coef_0 * x0
– Examples:3x14 + 2x8 + 18x14 – 3x10 + 10x6
Polynomials
• For each component of the polynomial, need to store coefficient and exponent
class Term {
public:void Term(int c, int e) { coef = c; exp =
e;}private:
int coef;int exp;
};
Polynomials
• Polynomial itself implemented by a templated LinkedList of Terms
class Polynomial{
private:LinkedList<Term> poly;
};
Polynomials
• Adding Polynomials:– Iterate through both lists– If exponent1 == exponent2,
• CoefficientSum = Coefficient1 + Coefficient2• If CoefficentSum != 0, add term to new polynomial
representing answer
– Else, • Find higher exponent• Add term to new polynomial representing answer
Polynomials
• The “Answer” polynomial is where LinkedLists have another win. There is now way beforehand to know how may terms there will be in the answer polynomial – Number of terms is anywhere from (the size of
the largest list) to (the size of the largest list plus the size of the smallest list)
– With an array representation, would have to overallocate to handle large answers
– With LinkedLists, just grab a new Node when need it.
Circular Lists
• Another improvement on LinkedLists:– Use link pointer of last node to point to the first node.
• Changes to implementation:– Check to see if at last node:
test “if (current -> link == first)”instead of “if (current->link == 0)”
– Insertion and deletion need to preserve property that last nodes link is always set equal to first
Circular List Implementation
Retrofitting our LinkedList to make circular:• Don’t want to have just a head pointer: Why?
– If inserting at tail, have to traverse the whole list from head to get to tail to update the tails link
– If inserting at head, need to find last node to point its link to new head - this also traverses the whole list
• Circular linked lists are much more efficient if use tail (last) pointer as the pointer for the list.
Circular Linked List
void insertAtFront(Type data){
LinkedListNode* toAdd = new LinkedListNode(data);if (tail == 0) { // empty list,
tail = toAdd; toAdd-> next = toAdd; // point to yourself}else{
toAdd->next = tail->next; // point new head next to old head
tail->next = toAdd; // point tail to new head}
}
insertAtRear() only requires adding tail = toAdd in the else statement (to update rear to new node)
Doubly Linked Lists
• Biggest problem with linked lists as we’ve used so far: – Can only navigate in one direction– Requires traversals from front to a position,
even if currently located right behind where want to be
– Requires “trailing pointer” if want to easily get access to previous node for current node
• When update current to current->next, update prev to current
Doubly Linked Lists
• Work around these issues by storing both the left and right side neighbors of a linked list
• Makes add, delete, and other array manipulation operators more complicated as have to preserve doubly linked property
Doubly Linked Lists
• Definition:
class DblListNode{
friend class DblList;private:
int data;DblListNode *right, *left;
};
Doubly Linked Lists
class DblList{
public:// list manipulation
private: DblListNode* head; // DblListNode* tail; // maybe use this if circular?
};
Doubly Linked Lists
LEFT DATA RIGHT
LEFT 10 RIGHT
LEFT 15 RIGHT
LEFT 29 RIGHT
Example Node
3 Node CircularDoubly Linked List
Doubly Linked List
• Note that, given a pointer p, p = p->left->right = p->right->left
• Going back and forth is equally easy.
Doubly Linked List
void DblList::Insert(DblListNode *new, DblListNode *current)
{// insert new after xnew->left = current; new->right = current->right;current->right->left = new;current->right = new;
}
10L
current
R12LR
R11L
new
Doubly Linked List
void DblList::Delete(DblListNode *toDelete)
{// delete node pointed to by toDeletetoDelete->left->right = toDelete->right;toDelete->right->left = toDelete->left;delete toDelete;
}10L R12LR11L
toDelete
Generalized Lists
• Current implementation of lists:– Finite sequence of zero or more atomic
elements
A = (a0, a1, a2 …. an-1)
– Elements of list are restricted to atoms:• Individual pieces of information
– An integer, A Rectangle
• Only structural property of a list is position – Given position, can easily access data
• Very much like arrays
Generalized Lists
• Relax the assumption of atomic elements:– Lists can now be composed of atoms or lists– Definition:
A generalized list A is a finite sequence of zero or more elements a0, a1, …, an-1 where ai is either an atom or a list. Elements that are not atomic are called sublists of A.
Generalized Lists
• Conventions for Generalized Lists:– List names are represented in capital letters– Atom names are lowercase letters
• Definitions for Generalized Lists:Length – Number of elements (atoms or sublists) in the list
If length > 0Head – First element of the listTail – List composed of all elements except first of list
Generalized Lists
• Examples of Generalized Lists:– D = ( ) Length 0, Null List– A = (a, (b,c)) Length 2
Head = a Tail = ((b,c))– B = (A, A, ()) Length 3
Head = A Tail = (A,())– C = (a, C) Length 2
Head = a Tail = (C)
Generalized Lists
• 2 Tricky Things Generalized Lists Allow:– Sharing of lists among lists
• B = (A, A, ( ))• Just like having the number 1 held in the list twice – how do
we represent efficiently and “safely”?
– Recursive definitions of lists• C = (a,C) actually represents the infinite list
(a, (a, (a, (a, (a, (a, (a, (a, … ))))))))
– May need to concern ourselves with these when implementing generalized list operations
Generalized Lists
• Need a new underlying representation:– A node in the list needs to be able to contain
either an atomic piece of information or point to another list
Tag = TRUE/FALSE Data/DLink Link (Next)
Generalized Lists
• Tag = represents type of node– Tag = false (0) means holds atomic data– Tag = true (1) means holds list data
• Data– If Tag = false, hold real data– If Tag = true, pointer to head of data list
• Link– Pointer to next item in list
Generalized Listsclass GenListNode {
friend class GenList;private: bool tag; GenListNode *link;union {
char data; // or any other type of interestGenListNode* dlink; }
}
class GenList {private: GenListNode *front; // using name front because
// we will use the term head// as something like a function
}
Generalized Lists
• Union definition:– User-defined data type that, at any given time,
contains only one object from its list of members (although that object can be an array or a class type). The member-list of a union represents the kinds of data the union can contain. A union requires enough storage to hold the largest member in its member-list.
union NumericType {
int iValue; long lValue; double dValue;
};
Generalized Lists
• Example representations– D = ( ) Length 0, Null List
D.front = 0;
– A = (a, (b,c)) Length 2
Head = a Tail = ((b,c))
false a true 0
false b false c 0
A.front
Generalized Lists
• B = (A, A, ( ) ) {where A is defined previously}
false a true 0
false b false c 0
A ->
B -> true true
true 0 0
Generalized Lists
• Recursive class definition (lists within lists): Recursive operation definitions are probably the best way to go
• Recursive functions for generalized lists will have two parts:– A public driver which starts the recursive calls off at
the right point– A private workhorse which does the actual recursion
and work
Generalized List Copy
• Copying a list:– Assume list is not recursive, no shared sublists
(as these are problematic)
– Essential properties of algorithm:• If data is an element, need to just copy the data
=> Requires a tag check to see if element or list• Otherwise, if data is a sublist, need to copy the sublist, then
copy the rest of the current list
=> Use recursion (calling copy again) on sublist and current node link
Generalized List Copy// Drivervoid GenList::Copy(const GenList &rhs) { first = Copy(rhs.first); }
// WorkhorseGenListNode* GenList::Copy(GenListNode* p){
GenListNode* q = 0;if (p != 0) {
q = new GenListNode();q-> tag = p->tag;if (q->tag == false) q-> data = p->data;else q->dlink = Copy(p->dlink);q->link = Copy(p->link);
}return q;
}
Generalized List Copy
• Verification of copy algorithm:– Works on empty list? Yes – returns q = 0 if
empty– Works on all data list? – Yes – copies data
elements since all tags will be false, then copies rest of list until next = 0
– Works on data and sublists? – Yes, copies data elements for false tags, copies sublists and returns pointer for sublists into dlink, and copies rest of original list until next = 0
Generalized List Equality
• Test for Equality– Requires:
• Same list structure (placement of atoms and sublists)
• Same list data
• Essential properties of algorithm:– Check equality of tags– If equal
• If data elements, check equality for data type• If list elements, recursively check equality on sublist
Generalized List Equalityint operator==(const GenList& l, const GenList& r){ return equal(l.first, r.first); }
int equal(GenListNode* s, GenListNode* t){
int x;if ((!s) && (!t)) return 1; // both emptyif (s && t && (s->tag == t->tag)) // data in lists, same { // type in this position
// check data if not sublistsif (s->tag == 0) { if (s->data == t->data) x = 1; else x = 0;
}// check recursively on sublists otherwiseelse x = equals(s->dlink, t->dlink);// if equal so far, recurse on next nodesif (x != 0) return equals(s->link, t->link);
}return 0; //otherwise return false
}
Generalized List Depth
• Define depth of a list l to be:
• If l is empty, depth(l) = 0;
• Else, for a component s, Depth(s) = 0 if s is an atom
Depth(s) = 1 + max {depth(x1),…,depth(xn)} if s is the non-empty list (x1, …, xn)
Generalized List Depth
Example:
A = (‘a’,’b’,’c’,’d’)
depth(A) = 1;
B = (‘a’,(‘c’,’d’),(‘b’,(‘e’,’c’)));
depth(B) = 3;
Generalized List Depthint GenList::depth(){ return depth(first); }
int GenList::depth(GenListNode *s){
if (s == 0) return 0;GenListNode* p = s; int m = 0;while (p != 0){ if (p->tag == true) // sublist
{ int n = depth(p->dlink); // check depth of sublist
if (m < n) m = n; // if > than current max, set as max
}p = p->link; // continue until end of list
}return m + 1; // include 1 for yourself
}
Generalized Lists
• Previous algorithms avoided the possibility of shared lists and recursive lists
• Potential Problems:– When sharing lists, changes to the shared list need to
be seen in the lists that make use of it
B = (A, A, ( ))
If A.delete() is called, removing A’s head, the two pointers for positions 0 and 1 in B need to be updated to point to the new head of the A.
*Same problem if add a node at the front of A.
Generalized Lists
• Could maintain a list of all things that point to a list and ensure that all are updated when the list is changed.
• Better to work around problem by adding a head node to each list that doesn’t hold data.
Tag is false.
Data/dlink field contains a reference count, indicating how many things are pointing to the list.
Link field points to real first item in the list
Generalized Lists
• Example representations– A header for a list with no internal elements
D -> false 1 0
Generalized Lists
• A = (a, (b, c)) B = (A, A, ( ) )
false a true 0
false b false c 0
A ->
B -> true true
true 0
false 3
false 1
false 1
false 1 0
Generalized Lists
• Why are head nodes useful?– Any changes to the front of lists don’t have to
be propagated to lists that are sharing the original list. The “head” node is always present and at the same address and points to whatever the real first data component is.
– Reference counts can be used for determining when memory can be freed.