List, (dynamic) linked list

Post on 12-Jan-2016

186 views 3 download

description

Let’s first forget about ‘classes’, but only a dynamic list. We make lists with ‘classes’ afterwards. List, (dynamic) linked list. A simple list Example: using a dynamic array. concept of a list, e.g. a list of integers Print out info Empty test Search an element - PowerPoint PPT Presentation

Transcript of List, (dynamic) linked list

List, (dynamic) linked list

Let’s first forget about ‘classes’, but only a dynamic list.

We make lists with ‘classes’ afterwards.

A simple list Example: using a dynamic array

concept of a list, e.g. a list of integers Print out info Empty test Search an element Insertion (at head, at end, any position) Deletion …

implemented by a static array (over-sized if necessary)

int list[1000]; int size; by a dynamic array

int list[size]; int size; by a linked list and more …

int main() {

cout << "Enter list size: ";int n;cin >> n;int* A = new int[n];

initialize(A, n, 0);print(A, n);A = addEnd(A,n,5); print(A, n);A = addHead(A,n,5); print(A, n);A = deleteFirst(A,n); print(A, n);selectionSort(A, n);print(A, n);delete [] A;}

How to use a list?

int A[10000];

int n;

Nothing compulsory in programming, only style matters!

Initialize

void initialize(int list[], int size, int value){

for(int i=0; i<size; i++)

list[i] = value;

}

void print(int list[], int size) {

cout << "[ ";

for(int i=0; i<size; i++)

cout << list[i] << " ";

cout << "]" << endl;

}

Print out a list

Delete the first element// for deleting the first element of the arrayint* deleteFirst(int list[], int& size){

int* newList;newList = new int[size-1]; // make new array

if(size){ // copy and delete old arrayfor(int i=0; i<size-1; i++)

newList[i] = list[i+1];delete [] list;

}size--;return newList;

}

Instead of

A = deleteFirst(A,n)

we can also just

deleteFirst(A,n) if we define as a void type function:

void deleteFirst(int*& A, int& size) {

A = newList;

}

Remark:

We can also B = deleteFirst(A,n) if we keep the original intact

Adding Elements// for adding a new element to end of arrayint* addEnd(int list[], int& size, int value){

int* newList;newList = new int [size+1]; // make new array

if(size){ // copy and delete old arrayfor(int i=0; i<size; i++)

newList[i] = list[i];delete [] list;

}newList[size] = value;size++;return newList;

}

// for adding a new element at the beginning of the arrayint* addHead(int list[], int& size, int value){

int* newList;newList = new int [size+1]; // make new array

if(size){ // copy and delete old arrayfor(int i=0; i<size; i++)

newList[i+1] = list[i];delete [] list;

}newList[0] = value;size++;return newList;

}

Add at the beginning:

Linked list: a dynamic list

Motivation list using static array

int myArray[1000]; int n;

We have to decide (to oversize) in advance the size of the array (list)

list using dynamic arrayint* myArray; int n;cin >> n;myArray = new int[n];

We allocate an array (list) of any specified size while theprogram is running

linked-list (dynamic size)size = ??The list is dynamic. It can grow and shrink to any size.

Array naturally represents a (ordered) list,

the link is implicit, consecutive and contiguous!

Now the link is explicit, any places!

20 45 75 85

Data

Link

20

45

75

85Data Link

20 45 75 85

Data Link

0 1 2 array

linked list

Linked Lists: Basic Idea

A linked list is an ordered collection of data Each element of the linked list has

Some data A link to the next element

The link is used to chain the data

Example: A linked list of integers:

20 45 75 85

Data Link

The list can grow and shrink

Linked Lists: Basic Ideas

20 45 75 85

20 45

addEnd(75), addEnd(85)

deleteEnd(85), deleteHead(20), deleteHead(45)

75

Original linked list of integers:

Insertion (in the middle):

Deletion (in the middle)

Linked Lists: Operations

20 45 75 85

20 45 75 85

20 45 75 85

60

old value

deleted item

struct Node{ int data;Node* next;

};

We can also:

typedef Node* NodePtr;

Definition of linked list type:

Linked List Structure Node : Data + Link

Definitionstruct Node {

int data; //contains useful information

Node* next; //points to next element or NULL

};

Create a NodeNode* p;

p = new Node; //points to newly allocated memory

Delete a Nodedelete p;

Access fields in a node(*p).data; //access the data field

(*p).next; //access the pointer field

Or it can be accessed this way

p->data //access the data field

p->next //access the pointer field

Representing and accessing linked lists

We define a pointer

Node* head;

that points to the first node of the linked list. When the linked list is empty then head is NULL.

20 45 75 85Head

Passing a Linked List to a Function

When passing a linked list to a function it should suffice to pass the value of head. Using the value of head the function can access the entire list.

Problem: If a function changes the beginning of a list by inserting or deleting a node, then head will no longer point to the beginning of the list.

Solution: When passing head always pass it by reference (not good!)

or using a function to return a new pointer value

It is roughly the same as for an array!!!

Implementation of an (Unsorted) Linked List

Start the first node from scratch

Node* newPtr;

newPtr = new Node;newPtr->data = 20;newPtr->next = NULL; head = newPtr;

Head

newPtr

20

Headhead = NULL;

Inserting a Node at the Beginning

newPtr = new Node;

newPtr->data = 13;

newPtr->next = Head;

head = newPtr;

Head

newPtr

13

20

Keep going …

Head

newPtr

50 40 13 20

void addHead(Node*& head, int newdata){

Node* newPtr = new Node;

newPtr->data = newdata;newPtr->next = Head;head = newPtr;

}

Adding an element to the head:

Call by reference, scaring!!!

NodePtr&

Node* addHead(Node* head, int newdata){

Node* newPtr = new Node;

newPtr->data = newdata;newPtr->next = Head;

return newPtr;}

Also written (more functionally) as:

Compare it with ‘addHead’ with a dynamic array implementation

(to delete)

Deleting the Head Node

Node* p;

p = head;

head = head->next;

delete p;

head

p

50 40 13 20

void deleteHead(Node*& head){

if(head != NULL){

NodePtr p = head;

head = head->next;

delete p;

}

}

Node* deleteHead(Node* head){

if(head != NULL){

NodePtr p = head;

head = head->next;

delete p;

}

return head;

}

As a function:

Displaying a Linked List

p = head;

p = p->next;

20 45head

p

20 45head

p

void displayList(Node* head){

NodePtr p;

p = head;

while(p != NULL){

cout << p->data << endl;

p = p->next;

} }

A linked list is displayed by walking through its nodes one by one,

and displaying their data fields (similar to an array!).

void displayArray(int data[], int size) { int n=0; while ( n<size ) {

cout << data[i] << endl; n++;

}

}

For an array:

//return the pointer of the node that has data=item//return NULL if item does not exist

Node* searchNode(Node* head, int item){NodePtr p = head;

NodePtr result = NULL;bool found=false;while((p != NULL) && (!found)){

if(p->data == item) {found = true;result = p;}

p = p->next;}return result;

}

Searching for a node (look at array searching first!)

void main() { const int size=8; int data[size] = { 10, 7, 9, 1, 17, 30, 5, 6 };

int value; cout << "Enter search element: ";

cin >> value; int n=0; int position=-1; bool found=false; while ( (n<size) && (!found) ) {

if(data[n] == value) { found=true; position=n;}

n++;}if(position==-1) cout << "Not found!!\n";else cout << "Found at: " << position << endl;

}

Remember array searching algorithm:

It is essentially the same!

Variations of linked lists

Unsorted linked lists

Sorted linked lists

Circular linked lists Doubly linked lists …

Further considerations for the unsorted lists:

Physical copy of list for operators like ‘deleteHead’ and ‘addHead’

‘deleteHead’ should be understood as a decomposition into a sub-list …

Node* deleteHead(Node* head){

// physically copy head into a new one, newhead

// so to keep the original list intact!

Node* newhead=NULL;

Node* temp=head;

while(temp!=NULL) {

newhead=addEnd(newhead,temp->data);

temp=temp->next;

}

if(newhead != NULL){

Node* p = newhead;

newhead = newhead->next;

delete p;

}

return newhead;

}

B = deleteHead(A);

Original linked list of integers:

Add to the end (insert at the end):

More operation: adding to the end

50 40 13 20

50 40 13 20 60

Last element

The key is how to locate the last element or node of the list!

void addEnd(NodePtr& head, int newdata){NodePtr newPtr = new Node;newPtr->data = newdata;newPtr->next = NULL;

NodePtr last = head;if(last != NULL){ // general non-empty list case

while(last->next != NULL) last=last->next;

last->next = newPtr;}else // deal with the case of empty list

head = newPtr;}

Add to the end:

Link new object to last->nextLink a new object to empty list

NodePtr addEnd(NodePtr head, int newdata){NodePtr newPtr = new Node;newPtr->data = newdata;newPtr->next = NULL;

NodePtr last = head;if(last != NULL){ // general non-empty list case

while(last->next != NULL) last=last->next;

last->next = newPtr;}else // deal with the case of empty list

head = newPtr;

return head;}

Add to the end as a function:

Implementation of a

Sorted Linked List

Inserting a Node

Head

cur

20

33

45 75

prev

...

newPtr

1. (a) Create a new node using: NodePtr newPtr = new node;

(b) Fill in the data field correctly.

2. Find “prev” and “cur” such that

the new node should be inserted between *prev and *cur.

3. Connect the new node to the list by using:

(a) newPtr->next = cur;

(b) prev->next = newPtr;

Finding prev and cur

Suppose that we want to insert or delete a node with data value newValue. Then the following code successfully finds prev and cur such that

prev->data < newValue <= cur->data

prev = NULL;

cur = head;

found=false;

while( (cur!=NULL) && (!found) ) {

if (newValue > cur->data) {

prev=cur;

cur=cur->next;

}

else found = true;

}

Prev is necessary as we can’t go back!

It’s a kind of search algo,

prev = NULL;

cur = head;

while( (cur!=NULL) && (newValue>cur->data) ) {

prev=cur;

cur=cur->next;

}

Logical AND (&&) is short-circuited, sequential, i.e. if the first part is false, the second part will not be executed.

Finally, it is equivalent to:

//insert item into linked list according to ascending orderNode* insertNode(Node* head, int item){

NodePtr newp, cur, pre; newp = new Node;newp->data = item;

pre = NULL;cur = head;while( (cur != NULL) && (item>cur->data)){

pre = cur;cur = cur->next;

}

if(pre == NULL){ //insert to head of linked listnewp->next = head;head = newp;

} else {pre->next = newp;new->next = cur;

}

return head;}

If the position happens to be the head

General case

// not recommended void type functionvoid insertNode(NodePtr& head, int item){

NodePtr newp, cur, pre; newp = new Node;newp->data = item;

pre = NULL;cur = head;while( (cur != NULL) && (item>cur->data)){

pre = cur;cur = cur->next;

}

if(pre == NULL){ //insert to head of linked listnewp->next = head;head = newp;

} else {pre->next = newp;new->next = cur;

}}

(to delete)

Deleting a Node To delete a node from the list

1. Locate the node to be deleted(a) cur points to the node.

(b) prev points to its predecessor

2. Disconnect node from list using: prev->next = cur->next;

3. Return deleted node to system: delete cur;

Head

cur

20 45 75 85

prev

...

Node* deleteNode(Node* head, int item){NodePtr prev=NULL, cur = head;while( (cur!=NULL) && (item > cur->data)){

prev = cur;cur = cur->next;

}

if ( cur!==NULL && cur->data==item) {

if(cur==head)head = head->next;

elseprev->next = cur->next;

delete cur; }

return head;}

Delete an element in a sorted linked list:

If the element is at the head

General case

We can delete only if the element is present!

If (cur==NULL || cur->data!=item) Item is not in the list!

Get the location

void deleteNode(NodePtr& head, int item){NodePtr prev=NULL, cur = head;while( (cur!=NULL) && (item > cur->data)){

prev = cur;cur = cur->next;

}

if ( cur!==NULL && cur->data==item) {

if(cur==Head)Head = Head->next;

elseprev->next = cur->next;

delete cur; }}

// in a void function, not recommended

If the element is at the head

General case

We can delete only if the element is present!

If (cur==NULL || cur->data!=item) Item is not in the list!

Get the location

Example of a (dynamic) class: linked list class

bool listEmpty(NodePtr head) {

}int getHead(NodePtr head) {

}NodePtr getRest(NodePtr head) {

}NodePtr addHead(NodePtr head, int newdata) {

}void delHead(NodePtr& Head){

}

linked lists: definition

struct Node{ int data;Node* next;

};

typedef Node* NodePtr;

NodePtr head;

void main(){

NodePtr Head1=NULL, Head2 = NULL, Head;

addHead(Head1, 50);

addHead(Head1, 40);

addHead(Head1, 30);

addHead(Head1, 20);

cout << "List 1: " << endl;

DisplayList(Head1);

cout << "Length of Head1 list: " << length(Head1) << endl;

cout << "Recursive length of Head1 list: " << lengthRec(Head1) << endl;

if(isPalindrome(Head1))

cout << "Head1 list is palindrome" << endl;

else

cout << "Head1 list is not palindrome" << endl;

addHead(Head2, 25);

addHead(Head2, 35);

addHead(Head2, 45);

addHead(Head2, 35);

addHead(Head2, 25);

cout << "List 2: " << endl;

DisplayList(Head2);

cout << "Length of Head2 list: " << length(Head2) << endl;

cout << "Recursive length of Head2 list: " << lengthRec(Head2) << endl;

if(isPalindrome(Head2))

cout << "Head2 list is palindrome" << endl;

else

cout << "Head2 list is not palindrome" << endl;

Head = mergeLists(Head1, Head2);

cout << "Merged List: " << endl;

DisplayList(Head);

cout << "Length of Merged list: " << length(Head) << endl;

cout << "Recursive length of Merged list: " << lengthRec(Head) << endl;

if(isPalindromeRec(Head))

cout << "Merged list is palindrome" << endl;

else

cout << "Merged list is not palindrome" << endl;

cout << "check the list again:" << endl;

DisplayList(Head);

}

Usage:

Make an Abstract Data Type

One more example of ADT: integer linked list using class

A class with dynamic objects: Copy constructor Destructor

struct Node{ public:

int data;Node* next;

};typedef Node* Nodeptr;

class list {public:

list(); // constructorlist(const list& list1); // copy constructor~list(); // destructor

bool empty() const; // boolean functionint headElement() const; // access functions

void addHead(int newdata); // add to the headvoid deleteHead(); // delete the head

int length() const; // utility functionvoid print() const; // output

private:Nodeptr head;

};‘old’ operations

‘new’ member functions

void main(){list L; // constructor called automatically here for LL.print(); { }L.addHead(30);L.print(); { 30 }L.addHead(13);L.print(); { 13 30 } L.addHead(40);L.print(); { 40 13 30 }L.addHead(50);L.print(); { 50 40 13 30 }list N(L);N.print(); { 50 40 13 30 }

list R;R.print(); { }if(R.empty())

cout << "List R empty" << endl;L.deleteHead();L.print(); { 40 13 30 }L.deleteHead();L.print(); { 13 30 }if(L.empty())

cout << "List L empty" << endl;else{

cout << "List L contains " << L.length() << " nodes" << endl;cout << "Head element of list L is: " << L.headElement() << endl;

}} // destructor called automatically here for L

How to use it

list::list(){head = NULL;

}

bool list::empty() const{if(head==NULL)

return true;else

return false;}

int list::headElement() const {if(head != NULL)

return head->data;else{

cout << "error: trying to find head of empty list" << endl;

exit(1);}

}

Some simple member functions:

Implementation

list::list(const list& list1) {

head = NULL;

Nodeptr cur = list1.head;

while(cur != NULL) {

// addEnd(cur->data);

addHead(cur->data); // inverse list order

cur = cur->next;

}

}

(explicitly defined) copy constructor:

Destructor: deallocation function

list::~list(){

Nodeptr cur;

while(head!=NULL){

cur = head;

head = head->next;

delete cur;

}

}

void list::addHead(int newdata){

Nodeptr newPtr = new Node;

newPtr->data = newdata;

newPtr->next = head;

head = newPtr;

}

Adding an element to the head:

void list::deleteHead(){

if(head != NULL){

Nodeptr cur = head;

head = head->next;

delete cur;

}

}

Deleting the head:

void list::print() const{cout << "{";Nodeptr cur = head;while(cur != NULL){

cout << cur->data << " ";

cur = cur->next;}

cout << "}" << endl;}

Print the list:

int list::length() const{

int n=0;Nodeptr cur = head;while(cur != NULL){

n++;cur = cur->next;

}return n;

}

Computing the number of elements of a given list:

struct Node{ public:

int data;Node* next;

};typedef Node* Nodeptr;

class list {public:

list(); // constructorlist(const list& list1); // copy constructorconst list& operator=(const list& list1); // assigment, l = l1;~list(); // destructor

bool empty() const; // boolean functionint head() const; // access functionslist remaining() const; // the list with the head removed void insert(int d); // insertionvoid delete(int d); // deletion

int length() const; // utility functionvoid print() const; //

private:Nodeptr head;

};

Interface functions

An almost ideal list class

list::list(const listClass& list1) {

head = NULL;

Nodeptr cur = list1.head;

while(cur != NULL) {

// addEnd(cur->data);

addHead(cur->data); // inverse list order

cur = cur->next;

}

}

copy constructor:

Const list& operator=(const list& list1) {

if (this != &list1) {

head = NULL;

Nodeptr cur = list1.head;

while(cur != NULL) {

// addEnd(cur->data);

addHead(cur->data); // inverse list order

cur = cur->next;

}

return *this;

}

Operator assignment, ‘deep copy’

Delete[] head;

Big three: copy constructor, operator=, destructor

list l1, l2;

l1.addEnd(5);

list l3(l1);

l3 = l2;

node* head1, head2;

head1 = NULL; head2 = NULL

addEnd(head1,5);

node* head3 = NULL;

copylist(head1, head3);

head3 = head2;

Usage difference

Doubly Linked List

Motivation

Doubly linked lists are useful for playing video and sound files with “rewind” and instant “replay”

They are also useful for other linked data where “require” a “fast forward” of the data as needed

list using an array: Knowledge of list size Access is easy (get the ith element) Insertion/Deletion is harder

list using ‘singly’ linked lists: Insertion/Deletion is easy Access is harder

But, can not ‘go back’!

Doubly Linked Lists

In a Doubly Linked-List each item points to both its predecessor and successor prev points to the predecessor next points to the successor

10 7020 5540

HeadCur Cur->nextCur->prev

struct Node{

int data;

Node* next;

Node* prev;

};

typedef Node* NodePtr;

Doubly Linked List Definition

Doubly Linked List Operations insertNode(NodePtr& Head, int item)//add new node to ordered doubly linked //list

deleteNode(NodePtr& Head, int item) //remove a node from doubly linked list

SearchNode(NodePtr Head, int item)

Print(nodePtr Head, int item)

Deleting a Node

Delete a node Cur (not at front or rear)

(Cur->prev)->next = Cur->next; (Cur->next)->prev = Cur->prev;

delete Cur;

10 7020 5540

HeadCur

void deleteNode(NodePtr& head, int item) {

NodePtr cur;

cur = searchNode(head, item);

if (head==NULL) { …

}

else if (cur->prev == NULL) { …

}

else if (cur->next==NULL) { …

}

else {

(cur->prev)->next = cur->next;

(cur->next)->prev = cur->prev;

delete cur;

}

}

Empty case

At-the-beginning case

At-the-end case

General case

A systematic way is to start from all these cases, then try to simply the codes, …

Inserting a Node

Insert a node New before Cur (not at front or rear)

10 7020 55

40Head

New

Cur

New->next = Cur;

New->prev = Cur->prev;

Cur->prev = New;

(New->prev)->next = New;

void insertNode(NodePtr& head, int item) {

NodePtr cur;

cur = searchNode(head, item);

if (head==NULL) { …

}

else if (cur->prev == NULL) { …

}

else if (cur->next==NULL) { …

}

else {

blablabla …}

}

Many special cases to consider.

Many different linked lists … singly linked lists

Without ‘dummy’ With dummy circular

doubly linked lists Without ‘dummy’ With dummy

Using ‘dummy’ is a matter of personal preference!

+ simplify codes (not that much - Less logically sounds

20Head 10 20 40 7055

Rear

10 20 40 7055

7020 5540

Head

10

7020 5540

Head

10Dummy

singly linked list

(singly) circular linked list

(regular) doubly linked list

doubly circular linked list with dummy

Doubly Linked Lists with Dummy Head Node

To simplify insertion and deletion by avoiding special cases of deletion and insertion at front and rear, a dummy head node is added at the head of the list

The last node also points to the dummy head node as its successor

Idea of ‘dummy’ object

Instead of pointing to NULL, point to the ‘dummy’!!! Skip over the dummy for the real list

7020 5540

Head

10Dummy Head Node

‘dummy object’ is also called a ‘sentinel’, it allows the simplification of special cases, but confuses the emptyness NULL!

Head

Dummy Head Node

Empty list:

Head->next = head; compared with head=NULL;

void createHead(NodePtr& head){

head = new Node;

head->next = head;

head->prev = head;

}

NodePtr head;

createHead(head);

NodePtr cur=head;

cur=cur->next;

cur=head; // dummy head

NodePtr head=NULL;

NodePtr cur=head;

cur=head;

cur=NULL; // or head=NULL;

operations Doubly linked with dummy Singly linked

creation

Empty test

Start from

reference

void print(NodePtr head){

NodePtr cur=head->next;

while(cur != head){

cout << cur->data << " ";

cur = cur->next;

}

}

Print the whole list:

NodePtr searchNode(NodePtr head, int item){

NodePtr cur = head->next;

while ((cur != head) && (item != cur->data)) cur=cur->next;

if (cur == head) cur = NULL; // we didn’t find

return cur;

}

Searching a node

(returning NULL if not found the element):

End of the list, empty

Deleting a Node

Delete a node Cur at front

7020 5540

Head

10Dummy Head Node

Cur

(Cur->prev)->next = Cur->next;

(Cur->next)->prev = Cur->prev;

delete Cur;

Delete a node Cur in the middle

(Cur->prev)->next = Cur->next;

(Cur->next)->prev = Cur->prev;

delete Cur; // same as delete front!

70

Head

10Dummy Head Node

20 5540

Cur

Delete a node Cur at rear

(Cur->prev)->next = Cur->next;

(Cur->next)->prev = Cur->prev;

delete Cur; // same as delete front and middle!

7020 5540

Head

10Dummy Head Node

Cur

void deleteNode(NodePtr head, int item){

NodePtr cur;

cur = searchNode(head, item);

if(cur != NULL){

cur->prev->next = cur->next;

cur->next->prev = cur->prev;

delete cur;

}

}

If we found the element, it does not mean any emptyness!

Inserting a Node Insert a Node New after dummy node and

before Cur

Head

Dummy Head Node

Cur

20

New->next = Cur;

New->prev = Cur->prev;

Cur->prev = New;

(New->prev)->next = New;

10

New

Insert a Node New at Rear (with Cur pointing to dummy head)

New->next = Cur;

New->prev = Cur->prev;

Cur->prev = New;

(New->prev)->next = New;

7020 5540

Head

10Dummy Head Node

Cur New

Insert a Node New in the middle and before Cur

New->next = Cur;

New->prev = Cur->prev;

Cur->prev = New;

(New->prev)->next = New;

55

Head

10Dummy Head Node

20

Cur

40

New

Insert a Node New to Empty List (with Cur pointing to dummy head node)

Head

Dummy Head Node

New

20

New->next = Cur;

New->prev = Cur->prev;

Cur->prev = New;

(New->prev)->next = New;

Cur

void insertNode(NodePtr head, int item){ NodePtr newp, cur; newp = new Node;

newp->data = item; cur = head->next; while ((cur != head)&&(!(item<=cur->data)))

cur = cur->next;

newp->next = cur; newp->prev = cur->prev; cur->prev = newp;

(newp->prev)->next = newp;}

It is similar to, but different from SearchNode!

(it returns NULL if no element)

creation

location

insertion

void main(){ NodePtr Head, temp; createHead(Head); insertNode(Head, 3); insertNode(Head, 5); insertNode(Head, 2); print(Head); insertNode(Head, 7); insertNode(Head, 1); insertNode(Head, 8); print(Head); deleteNode(Head, 7); deleteNode(Head, 0); print(Head); temp = searchNode(Head, 5); if(temp !=NULL) cout<<" Data is contained in the list"<<endl; else cout<<" Data is NOT contained in the list"<<endl; }

Result is

2 3 5

1 2 3 5 7 8

1 2 3 5 8

Data is contained in the list

Stacks and Queues

struct Node{ double data;Node* next;

};

class List {public:

List(); // constructorList(const List& list); // copy constructor~List(); // destructorList& operator=(const List& list); // assignment operator

bool empty() const; // boolean functionvoid addHead(double x); // add to the headdouble deleteHead(); // delete the head and get the head element// List& rest(); // get the rest of the list with the head removed// double headElement() const; // get the head element

void addEnd(double x); // add to the enddouble deleteEnd(); // delete the end and get the end element// double endElement(); // get the element at the end

bool searchNode(double x); // search for a given xvoid insertNode(double x); // insert x in a sorted listvoid deleteNode(double x); // delete x in a sorted list

void print() const; // outputint length() const; // count the number of elements

private:Node* head;

};

More complete list ADT

Stack Overview

Stack ADT Basic operations of stack

Pushing, popping etc.

Implementations of stacks using array linked list

Stack

A stack is a list in which insertion and deletion take place at the same end This end is called top The other end is called bottom

Stacks are known as LIFO (Last In, First Out) lists. The last element inserted will be the first to be retrieved

Push and Pop

Primary operations: Push and Pop Push

Add an element to the top of the stack

Pop Remove the element at the top of the stack

top

empty stack

Atop

push an element

top

push another

A

Btop

pop

A

Implementation of Stacks

Any list implementation could be used to implement a stack Arrays (static: the size of stack is given initially) Linked lists (dynamic: never become full)

We will explore implementations based on array and linked list

class Stack {public:

Stack(); // constructorStack(const Stack& stack); // copy constructor~Stack(); // destructor

bool empty() const; void push(const double x); double pop(); // change the stack

double top() const; // keep the stack unchanged

// bool full(); // optional// void print() const;

private:…

};

Stack ADT

Compare with List, see that it’s ‘operations’ that define the type!

inspection, access

Using Stack

int main(void) {Stack stack;stack.push(5.0);stack.push(6.5);stack.push(-3.0);stack.push(-8.0);stack.print();cout << "Top: " << stack.top() << endl;

stack.pop();cout << "Top: " << stack.top() << endl;while (!stack.empty()) stack.pop();stack.print();return 0;

}

result

struct Node{ public:

double data;Node* next;

};

class Stack {public:

Stack(); // constructorStack(const Stack& stack); // copy constructor~Stack(); // destructor

bool empty() const; void push(const double x); double pop(); // change the stack

bool full(); // unnecessary for linked listsdouble top() const; // keep the stack unchanged

void print() const;

private:Node* top;

};

Stack using linked lists

void List::addHead(int newdata){

Nodeptr newPtr = new Node;

newPtr->data = newdata;

newPtr->next = head;

head = newPtr;

}

void Stack::push(double x){

Node* newPtr = new Node;

newPtr->data = x;

newPtr->next = top;

top = newPtr;

}

From ‘addHead’ to ‘push’

Push (addHead), Pop (deleteHead)

Implementation based on ‘existing’ linked lists

Optional to learn Good to see that we may ‘re-use’ linked lists

Now let’s implement a stack based on a linked list To make the best out of the code of List, we implement Stack

by inheriting the List To let Stack access private member head, we make Stack

as a friend of List

class List {public:

List() { head = NULL; } // constructor~List(); // destructor

bool empty() { return head == NULL; }Node* insertNode(int index, double x);int deleteNode(double x);

int searchNode(double x);void printList(void);

private:Node* head;friend class Stack;

};

class Stack : public List {public:

Stack() {}~Stack() {}double top() {

if (head == NULL) {cout << "Error: the stack is empty." << endl;return -1;

}else

return head->data;}void push(const double x) { InsertNode(0, x); }double pop() {

if (head == NULL) {cout << "Error: the stack is empty." << endl;return -1;

}else {

double val = head->data;DeleteNode(val);return val;

}}

void printStack() { printList(); }};

Note: the stack implementation based on a linked list will never be full.

from List

Stack using arrays

class Stack {public:

Stack(int size = 10); // constructor~Stack() { delete [] values; } // destructor

bool empty() { return top == -1; }void push(const double x);double pop();

bool full() { return top == maxTop; }double top();void print();

private:int maxTop; // max stack size = size - 1int top; // current top of stackdouble* values; // element array

};

Attributes of Stack maxTop: the max size of stack top: the index of the top element of stack values: point to an array which stores elements of stack

Operations of Stack empty: return true if stack is empty, return false otherwise full: return true if stack is full, return false otherwise top: return the element at the top of stack push: add an element to the top of stack pop: delete the element at the top of stack print: print all the data in the stack

Allocate a stack array of size. By default, size = 10.

Initially top is set to -1. It means the stack is empty. When the stack is full, top will have its maximum value, i.e.

size – 1.

Stack::Stack(int size /*= 10*/) {values = new double[size];top = -1;

maxTop = size - 1;}

Although the constructor dynamically allocates the stack array, the stack is still static. The size is fixed after the initialization.

Stack constructor

void push(const double x); Push an element onto the stack Note top always represents the index of the top

element. After pushing an element, increment top.

void Stack::push(const double x) {if (full()) // if stack is full, print error

cout << "Error: the stack is full." << endl;else

values[++top] = x;}

double pop() Pop and return the element at the top of the stack Don’t forgot to decrement top

double Stack::pop() {if (empty()) { //if stack is empty, print error

cout << "Error: the stack is empty." << endl;return -1;

}else {

return values[top--];}

}

double top() Return the top element of the stack Unlike pop, this function does not remove the top

element

double Stack::top() {if (empty()) {

cout << "Error: the stack is empty." << endl;return -1;

}else

return values[top];}

void print() Print all the elements

void Stack::print() {cout << "top -->";for (int i = top; i >= 0; i--)

cout << "\t|\t" << values[i] << "\t|" << endl;cout << "\t|---------------|" << endl;

}

Stack Application: Balancing Symbols

To check that every right brace, bracket, and parentheses must correspond to its left counterpart e.g. [( )] is legal, but [( ] ) is illegal

How? Need to memorize Use a counter, several counters, each for a type of

parenthesis …

Balancing Symbols using a stack

Algorithm(1)   Make an empty stack.(2)   Read characters until end of file

i.    If the character is an opening symbol, push it onto the stackii.   If it is a closing symbol, then if the stack is empty, report an erroriii.  Otherwise, pop the stack. If the symbol popped is not the corresponding opening symbol, then report an error

(3)   At end of file, if the stack is not empty, report an error

Stack Application: postfix, infix expressions and

calculator Postfix expressions

a b c * + d e * f + g * + Operands are in a stack

Convert infix to postfix a+b*c+(d*e+f)*g a b c * + d e * f + g * + Operators are in a stack

Calculator Adding more operators …

Stack Application: function calls and recursion

Take the example of factorial! And run it.

#include <iostream>using namespace std;

int fac(int n){int product;if(n <= 1) product = 1;else product = n * fac(n-1);return product;

}

void main(){int number;cout << "Enter a positive integer : " << endl;;cin >> number;cout << fac(number) << endl;

}

Stack Application: function calls and recursion

Take the example of factorial! And run it.

#include <iostream>using namespace std;

int fac(int n){int product;if(n <= 1) product = 1;else product = n * fac(n-1);return product;

}

void main(){int number;cout << "Enter a positive integer : " << endl;;cin >> number;cout << fac(number) << endl;

}

Assume the number typed is 3. fac(3): has the final returned value 6

3<=1 ? No.

product3 = 3*fac(2) product3=3*2=6, return 6,

fac(2):2<=1 ? No.

product2 = 2*fac(1) product2=2*1=2, return 2,

fac(1):1<=1 ? Yes.return 1

Tracing the program …

fac(3) prod3=3*fac(2)

prod2=2*fac(1)fac(2)

fac(1) prod1=1

Call is to ‘push’ and return is to ‘pop’!

top

Array versus linked list implementations

push, pop, top are all constant-time operations in both array and linked list implementation Caveat: insertNode and deleteNode have to

be done at the beginning of the list! For array implementation, the operations are

performed in very fast constant time

Queue Overview

Queue ADT Basic operations of queue

Enqueuing, dequeuing etc.

Implementation of queue Linked list Array

Queue

A queue is also a list. However, insertion is done at one end, while deletion is performed at the other end.

It is “First In, First Out (FIFO)” order. Like customers standing in a check-out line in a

store, the first customer in is the first customer served.

Enqueue and Dequeue

Primary queue operations: Enqueue and Dequeue Like check-out lines in a store, a queue has a front

and a rear. Enqueue – insert an element at the rear of the

queue Dequeue – remove an element from the front of

the queue

Insert (Enqueue)

Remove(Dequeue) rearfront

Implementation of Queue

Just as stacks can be implemented as arrays or linked lists, so with queues.

Dynamic queues have the same advantages over static queues as dynamic stacks have over static stacks

(“static” should be interpreted as “non-dynamic” here!)

class Queue {public:

Queue();Queue(Queue& queue);~Queue();

bool empty();void enqueue(double x);double dequeue();

void print(void);// bool full(); // optional

private:…

};

Queue ADT

‘physical’ constructor/destructor

‘logical’ constructor/destructor

Using Queueint main(void) {

Queue queue;cout << "Enqueue 5 items." << endl;for (int x = 0; x < 5; x++)

queue.enqueue(x);cout << "Now attempting to enqueue again..." << endl;queue.enqueue(5);queue.print();double value;value=queue.dequeue();cout << "Retrieved element = " << value << endl;queue.print();queue.enqueue(7);queue.print();return 0;

}

Struct Node {double data;Node* next;

}

class Queue {public:

Queue();Queue(Queue& queue);~Queue();

bool empty();void enqueue(double x);double dequeue();

// bool full(); // optionalvoid print(void);

private:Node* front; // pointer to front nodeNode* rear; // pointer to last nodeint counter; // number of elements

};

Queue using linked lists

class Queue {public:

Queue() { // constructorfront = rear = NULL;counter = 0;

}~Queue() { // destructor

double value;while (!empty()) dequeue(value);

}bool empty() {

if (counter) return false;else return true;

}void enqueue(double x);double dequeue();

// bool full() {return false;};void print(void);

private:Node* front; // pointer to front nodeNode* rear; // pointer to last nodeint counter; // number of elements, not compulsary

};

Implementation of some online member functions …

Enqueue (addEnd)void Queue::enqueue(double x) {

Node* newNode = new Node;newNode->data = x;newNode->next = NULL;

if (empty()) {front = newNode;

}else {

rear->next = newNode;}

rear = newNode;counter++;

}

8

rear

rear

newNode

5

58

Dequeue (deleteHead)double Queue::dequeue() {

double x;if (empty()) {

cout << "Error: the queue is empty." << endl;exit(1); // return false;

}else {

x = front->data;Node* nextNode = front->next;delete front;front = nextNode;counter--;

}return x;

}

front

583

8 5

front

Printing all the elements

void Queue::print() {cout << "front -->";Node* currNode = front;for (int i = 0; i < counter; i++) {

if (i == 0) cout << "\t";else cout << "\t\t"; cout << currNode->data;if (i != counter - 1)

cout << endl;else

cout << "\t<-- rear" << endl;currNode = currNode->next;

}}

Queue using Arrays There are several different algorithms to

implement Enqueue and Dequeue Naïve way

When enqueuing, the front index is always fixed and the rear index moves forward in the array.

front

rear

Enqueue(3)

3

front

rear

Enqueue(6)

3 6

front

rear

Enqueue(9)

3 6 9

Naïve way (cont’d) When dequeuing, the front index is fixed, and the

element at the front the queue is removed. Move all the elements after it by one position. (Inefficient!!!)

Dequeue()

front

rear

6 9

Dequeue() Dequeue()

front

rear

9

rear = -1

front

A better way When enqueued, the rear index moves forward. When dequeued, the front index also moves forward

by one element

XXXXOOOOO (rear) OXXXXOOOO (after 1 dequeue, and 1 enqueue)OOXXXXXOO (after another dequeue, and 2 enqueues)OOOOXXXXX (after 2 more dequeues, and 2 enqueues)

(front)

The problem here is that the rear index cannot move beyond the last element in the array.

Using Circular Arrays

Using a circular array When an element moves past the end of a circular

array, it wraps around to the beginning, e.g. OOOOO7963 4OOOO7963 (after Enqueue(4))

How to detect an empty or full queue, using a circular array algorithm? Use a counter of the number of elements in the queue.

class Queue {public:

Queue(int size = 10); // constructorQueue(const Queue& queue);

~Queue() { delete [] values; } // destructor

bool empty(void);void enqueue(double x); // or bool enqueue();double dequeue();

bool full();void print(void);

private:int front; // front indexint rear; // rear indexint counter; // number of elementsint maxSize; // size of array queuedouble* values; // element array

};

full() is not essential, can be embedded

Attributes of Queue front/rear: front/rear index counter: number of elements in the queue maxSize: capacity of the queue values: point to an array which stores elements of the queue

Operations of Queue empty: return true if queue is empty, return false otherwise full: return true if queue is full, return false otherwise enqueue: add an element to the rear of queue dequeue: delete the element at the front of queue print: print all the data

Queue constructor

Queue(int size = 10) Allocate a queue array of size. By default, size = 10. front is set to 0, pointing to the first element of the

array rear is set to -1. The queue is empty initially.

Queue::Queue(int size /* = 10 */) {values = new double[size];maxSize = size;front = 0;rear = -1;counter = 0;

}

Empty & Full Since we keep track of the number of elements

that are actually in the queue: counter, it is easy to check if the queue is empty or full.

bool Queue::empty() {if (counter==0) return true;else return false;

}bool Queue::full() {

if (counter < maxSize) return false;else return true;

}

Enqueue

void Queue::enqueue(double x) {if (full()) {

cout << "Error: the queue is full." << endl;exit(1); // return false;

}else {

// calculate the new rear position (circular)rear = (rear + 1) % maxSize; // insert new itemvalues[rear] = x;// update countercounter++;// return true;

}}

Or ‘bool’ if you want

Dequeue

double Queue::dequeue() {double x;if (empty()) {

cout << "Error: the queue is empty." << endl;exit(1); // return false;

}else {

// retrieve the front itemx = values[front];// move front front = (front + 1) % maxSize;// update countercounter--;// return true;

}return x;

}

Printing the elements

void Queue::print() {cout << "front -->";for (int i = 0; i < counter; i++) {

if (i == 0) cout << "\t";else cout << "\t\t"; cout << values[(front + i) % maxSize];if (i != counter - 1)

cout << endl;else

cout << "\t<-- rear" << endl;}

}

Using Queueint main(void) {

Queue queue;cout << "Enqueue 5 items." << endl;for (int x = 0; x < 5; x++)

queue.enqueue(x);cout << "Now attempting to enqueue again..." << endl;queue.enqueue(5);queue.print();double value;value=queue.dequeue();cout << "Retrieved element = " << value << endl;queue.print();queue.enqueue(7);queue.print();return 0;

}

Results

Queue implemented using linked list will be never full!

based on array based on linked list

Queue applications

When jobs are sent to a printer, in order of arrival, a queue.

Customers at ticket counters …