linked list containers - department of computer...

47
Linked List Containers

Upload: duongkhuong

Post on 07-May-2019

217 views

Category:

Documents


0 download

TRANSCRIPT

Linked List Containers

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 exponentclass 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:

3x3 + 2x + 1 +

2x3 + 3x2 + 5=================

5x3 + 3x2 + 2x + 6

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

3 3 2 1 1 0

2 3 3 2 5 0

5 3 3 2 2 1 6 0

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 Lists

List1 Data1 List1 Data2 List1 Data3

tailhead (AKA first)

Circular Linked Listvoid 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 headtail->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 prevto 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 Listsclass 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 Listvoid 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

elementsA = (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 ListD.front = 0;

– A = (a, (b,c)) Length 2Head = 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

• C = (a, C)

false a true 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 sublistsand 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 atomDepth(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 sublistif (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 itB = (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 elementsD -> 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

• C = (a,C)

false a true 0false 2C ->

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.