> 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!"> > 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!">

Presentation is loading. Please wait.

Presentation is loading. Please wait.

List, (dynamic) linked list Let’s first forget about ‘classes’, but only a dynamic list. We make lists with ‘classes’ afterwards.

Similar presentations


Presentation on theme: "List, (dynamic) linked list Let’s first forget about ‘classes’, but only a dynamic list. We make lists with ‘classes’ afterwards."— Presentation transcript:

1 List, (dynamic) linked list Let’s first forget about ‘classes’, but only a dynamic list. We make lists with ‘classes’ afterwards.

2 A simple list Example: using a dynamic array * concept of a list, e.g. a list of integers n Print out info n Empty test n Search an element n Insertion (at head, at end, any position) n Deletion n … * implemented n by a static array (over-sized if necessary) int list[1000]; int size; n by a dynamic array int list[size]; int size; n by a linked list and more …

3 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!

4 Initialize void initialize(int list[], int size, int value){ for(int i=0; i<size; i++) list[i] = value; }

5 void print(int list[], int size) { cout << "[ "; for(int i=0; i<size; i++) cout << list[i] << " "; cout << "]" << endl; } Print out a list

6 Delete the first element // for deleting the first element of the array int* deleteFirst(int list[], int& size){ int* newList; newList = new int[size-1]; // make new array if(size){// copy and delete old array for(int i=0; i<size-1; i++) newList[i] = list[i+1]; delete [] list; } size--; return newList; }

7 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

8 Adding Elements // for adding a new element to end of array int* addEnd(int list[], int& size, int value){ int* newList; newList = new int [size+1]; // make new array if(size){// copy and delete old array for(int i=0; i<size; i++) newList[i] = list[i]; delete [] list; } newList[size] = value; size++; return newList; }

9 // for adding a new element at the beginning of the array int* addHead(int list[], int& size, int value){ int* newList; newList = new int [size+1]; // make new array if(size){// copy and delete old array for(int i=0; i<size; i++) newList[i+1] = list[i]; delete [] list; } newList[0] = value; size++; return newList; } Add at the beginning:

10 Linked list: a dynamic list

11 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 array int* myArray; int n; cin >> n; myArray = new int[n]; We allocate an array (list) of any specified size while the program is running * linked-list (dynamic size) size = ?? The list is dynamic. It can grow and shrink to any size.

12 Array naturally represents a (ordered) list, the link is implicit, consecutive and contiguous! Now the link is explicit, any places! 2045 7585 Data Link 20 45 75 85 Data Link 20457585 Data Link 0 1 2 array linked list

13 Linked Lists: Basic Idea * A linked list is an ordered collection of data * Each element of the linked list has n Some data n A link to the next element * The link is used to chain the data Example: A linked list of integers: 20457585 Data Link

14 * The list can grow and shrink Linked Lists: Basic Ideas 20457585 2045 addEnd(75), addEnd(85) deleteEnd(85), deleteHead(20), deleteHead(45) 75

15 * Original linked list of integers: * Insertion (in the middle): * Deletion (in the middle) Linked Lists: Operations 20457585 20457585 20457585 60 old value deleted item

16 struct Node{ int data; Node* next; }; We can also: typedef Node* NodePtr; Definition of linked list type:

17 Linked List Structure * Node : Data + Link n Definition struct Node { int data;//contains useful information Node* next;//points to next element or NULL }; n Create a Node Node* p; p = new Node;//points to newly allocated memory n Delete a Node delete p;

18 n 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

19 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. 20457585 Head

20 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!!!

21 Implementation of an (Unsorted) Linked List

22 Start the first node from scratch Node* newPtr; newPtr = new Node; newPtr->data = 20; newPtr->next = NULL; head = newPtr; Head newPtr 20 Head head = NULL;

23 Inserting a Node at the Beginning newPtr = new Node; newPtr->data = 13; newPtr->next = Head; head = newPtr; Head newPtr 13 20

24 Keep going … Head newPtr 50401320

25 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&

26 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

27 (to delete) Deleting the Head Node Node* p; p = head; head = head->next; delete p; head p 50401320

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

29 Displaying a Linked List p = head; p = p->next; 2045 head p 2045 head p

30 void displayList(Node* head){ NodePtr p; p = head; while(p != NULL){ cout 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:

31 //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!)

32 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!

33 Variations of linked lists * Unsorted linked lists * Sorted linked lists * Circular linked lists * Doubly linked lists * …

34 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 …

35 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);

36 * Original linked list of integers: * Add to the end (insert at the end): More operation: adding to the end 50401320 50401320 60 Last element The key is how to locate the last element or node of the list!

37 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->next Link a new object to empty list

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

39 Implementation of a Sorted Linked List

40 Inserting a Node Head cur 20 33 4575 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;

41 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 data

42 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,

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

44 //insert item into linked list according to ascending order Node* 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 list newp->next = head; head = newp; } else { pre->next = newp; new->next = cur; } return head; } If the position happens to be the head General case

45 // not recommended void type function void 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 list newp->next = head; head = newp; } else { pre->next = newp; new->next = cur; }

46 (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 20457585 prev...

47 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; else prev->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

48 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; else prev->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

49 Classes with dynamic objects List as a (dynamic) class

50 int x; void f() { x=10; } main() { x=0; f(); } ‘Class’ matters! int x; void f() { int x; x=10; } main() { x=0; f(); } class A { public int x; void f(); } main() { A a; a.x = 0; a.f(); } localglobalpublic

51 class A { public: void f(); int x; private: int y; } void A::f() { x=10; y=100; } int main() { A a; a.f(); cout << a.x << endl; cout << a.y << endl; // no!!! a.x = 1000; a.y = 10000; // no!!! } Public or private?

52 class A { public: A(); int f(); private: int x; } A::A() { x=0; } int A::f() { return x; } int main() { A a; cout << a.f(); // not a.x!!! A b(a); A c; c = a; // member-wise copy } Objects are calling member funtions (public interface) Member functions are using member variables (private data) Objects are not directly dependent on implementation (private data) Abstract Data Type: public function, private data

53 Static variables (objects)Dynamic variables (objects) A (direct) named memory locationA static part (pointer) + (indirect) nameless memory location (dynamic part) int a; a = 20; int* pa; pa = new int; *pa = 20; 20 a pa static dynamic Static and dynamic

54 Only static member variables At least one pointer variable (I.e. dynamic variable) class A { public: A(); private: int x; } class B { public: B(); B(const B& b); ~B(); private: int* px; } (Must) a defaut value constructor A() for ‘copy constructor’ automatically privided No destructor ‘assignment’ automatically provided (Must) a defaut value constructor for (Must) redefine ‘copy constructor’ (Must) the destructor (Must) redefine ‘assignment’ (do later) initialisation Creation by new + initialiation classes

55 class B { public: B(); B(const B& b); ~B(); private: int* px; } B::B() { px = new int; *px = 0; } B::B(const B& b) { px = new int; (*px) = *(b.px); } B::~B() { delete px; }

56 All constructors/destructor have automatic behavior, they are called ‘implicitly’!!! (that’s why they have standard names!) Constructors are called when creating variables and other occasions: Destructors are called when the variables go out of scope A a; // implicitly call the default value constructor A::A(); A b(a); // implicitly call the copy constructor A::A(const A& a); Automatic behavior of constructors/destructor void somefunction() { B b; };

57 Example of a (dynamic) class: linked list class

58 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;

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

60 Make an Abstract Data Type l One more example of ADT: l integer linked list using class l A class with dynamic objects: l Copy constructor l Destructor

61 struct Node{ public: int data; Node* next; }; typedef Node* Nodeptr; class list { public: list(); // constructor list(const list& list1); // copy constructor ~list(); // destructor bool empty() const; // boolean function int headElement() const; // access functions void addHead(int newdata); // add to the head void deleteHead(); // delete the head int length() const; // utility function void print() const; // output private: Nodeptr head; }; ‘old’ operations ‘new’ member functions

62 void main(){ list L; // constructor called automatically here for L L.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

63 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

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

65 Destructor: deallocation function list::~list(){ Nodeptr cur; while(head!=NULL){ cur = head; head = head->next; delete cur; }

66 void list::addHead(int newdata){ Nodeptr newPtr = new Node; newPtr->data = newdata; newPtr->next = head; head = newPtr; } Adding an element to the head:

67 void list::deleteHead(){ if(head != NULL){ Nodeptr cur = head; head = head->next; delete cur; } Deleting the head:

68 void list::print() const{ cout << "{"; Nodeptr cur = head; while(cur != NULL){ cout data << " "; cur = cur->next; } cout << "}" << endl; } Print the list:

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

70 struct Node{ public: int data; Node* next; }; typedef Node* Nodeptr; class list { public: list(); // constructor list(const list& list1); // copy constructor const list& operator=(const list& list1); // assigment, l = l1; ~list(); // destructor bool empty() const; // boolean function int head() const; // access functions list remaining() const; // the list with the head removed void insert(int d); // insertion void delete(int d); // deletion int length() const; // utility function void print() const; // private: Nodeptr head; }; Interface functions An almost ideal list class

71 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

72 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

73 Doubly Linked List

74 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

75 * list using an array: n Knowledge of list size n Access is easy (get the ith element) n Insertion/Deletion is harder * list using ‘singly’ linked lists: n Insertion/Deletion is easy n Access is harder n But, can not ‘go back’!

76 Doubly Linked Lists In a Doubly Linked-List each item points to both its predecessor and successor n prev points to the predecessor n next points to the successor 1070205540 Head Cur Cur->nextCur->prev

77 struct Node{ int data; Node* next; Node* prev; }; typedef Node* NodePtr; Doubly Linked List Definition

78 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)

79 Deleting a Node  Delete a node Cur (not at front or rear) (Cur->prev)->next = Cur->next; (Cur->next)->prev = Cur->prev; delete Cur; 1070205540 Head Cur

80 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, …

81 Inserting a Node  Insert a node New before Cur (not at front or rear) 1070205540 Head New Cur New->next = Cur; New->prev = Cur->prev; Cur->prev = New; (New->prev)->next = New;

82 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.

83 Many different linked lists … * singly linked lists n Without ‘dummy’ n With dummy n circular * doubly linked lists n Without ‘dummy’ n With dummy Using ‘dummy’ is a matter of personal preference! + simplify codes (not that much - Less logically sounds

84 20 Head 1020407055 Rear 1020407055 70205540 Head 10 70205540 Head 10 Dummy singly linked list (singly) circular linked list (regular) doubly linked list doubly circular linked list with dummy

85 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

86 Idea of ‘dummy’ object l Instead of pointing to NULL, point to the ‘dummy’!!! l Skip over the dummy for the real list 70205540 Head 10 Dummy Head Node ‘dummy object’ is also called a ‘sentinel’, it allows the simplification of special cases, but confuses the emptyness NULL!

87 Head Dummy Head Node Empty list: Head->next = head; compared with head=NULL;

88 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; operationsDoubly linked with dummySingly linked creation Empty test Start from reference

89 void print(NodePtr head){ NodePtr cur=head->next; while(cur != head){ cout data << " "; cur = cur->next; } Print the whole list:

90 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

91 Deleting a Node  Delete a node Cur at front 70205540 Head 10 Dummy Head Node Cur (Cur->prev)->next = Cur->next; (Cur->next)->prev = Cur->prev; delete Cur;

92  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 10 Dummy Head Node 205540 Cur

93  Delete a node Cur at rear (Cur->prev)->next = Cur->next; (Cur->next)->prev = Cur->prev; delete Cur;// same as delete front and middle! 70205540 Head 10 Dummy Head Node Cur

94 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!

95 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

96  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; 70205540 Head 10 Dummy Head Node Cur New

97  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 10 Dummy Head Node 20 Cur 40 New

98  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

99 void insertNode(NodePtr head, int item){ NodePtr newp, cur; newp = new Node; newp->data = item; cur = head->next; while ((cur != head)&&(!(item 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

100 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

101 Stacks and Queues

102 struct Node{ double data; Node* next; }; class List { public: List(); // constructor List(const List& list); // copy constructor ~List(); // destructor List& operator=(const List& list); // assignment operator bool empty() const; // boolean function void addHead(double x); // add to the head double 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 end double 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 x void insertNode(double x); // insert x in a sorted list void deleteNode(double x); // delete x in a sorted list … void print() const; // output int length() const; // count the number of elements private: Node* head; }; More complete list ADT

103 Stack Overview * Stack ADT * Basic operations of stack n Pushing, popping etc. * Implementations of stacks using n array n linked list

104 Stack * A stack is a list in which insertion and deletion take place at the same end n This end is called top n The other end is called bottom * Stacks are known as LIFO (Last In, First Out) lists. n The last element inserted will be the first to be retrieved

105 Push and Pop * Primary operations: Push and Pop * Push n Add an element to the top of the stack * Pop n Remove the element at the top of the stack top empty stack A top push an element top push another A B top pop A

106 Implementation of Stacks * Any list implementation could be used to implement a stack n Arrays (static: the size of stack is given initially) n Linked lists (dynamic: never become full) * We will explore implementations based on array and linked list

107 class Stack { public: Stack(); // constructor Stack(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

108 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

109 struct Node{ public: double data; Node* next; }; class Stack { public: Stack(); // constructor Stack(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 lists double top() const; // keep the stack unchanged void print() const; private: Node* top; }; Stack using linked lists

110 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)

111 Implementation based on ‘existing’ linked lists * Optional to learn * Good to see that we may ‘re-use’ linked lists

112 * 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; };

113 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

114 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 - 1 int top;// current top of stack double* values;// element array };

115  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

116 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

117 * void push(const double x); n Push an element onto the stack n 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; }

118 * double pop() n Pop and return the element at the top of the stack n 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--]; }

119 * double top() n Return the top element of the stack n Unlike p op, 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]; }

120 * void print() n Print all the elements void Stack::print() { cout "; for (int i = top; i >= 0; i--) cout << "\t|\t" << values[i] << "\t|" << endl; cout << "\t|---------------|" << endl; }

121 Stack Application: Balancing Symbols * To check that every right brace, bracket, and parentheses must correspond to its left counterpart n e.g. [( )] is legal, but [( ] ) is illegal * How? n Need to memorize n Use a counter, several counters, each for a type of parenthesis …

122 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 stack ii. If it is a closing symbol, then if the stack is empty, report an error iii. 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

123 Stack Application: postfix, infix expressions and calculator * Postfix expressions n a b c * + d e * f + g * + n Operands are in a stack * Convert infix to postfix n a+b*c+(d*e+f)*g  a b c * + d e * f + g * + n Operators are in a stack * Calculator n Adding more operators …

124 Stack Application: function calls and recursion * Take the example of factorial! And run it. #include 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; }

125 Stack Application: function calls and recursion * Take the example of factorial! And run it. #include 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; }

126 Assume the number typed is 3. fac(3): has the final returned value 6 3<=1 ? No. product 3 = 3*fac(2) product 3 =3*2=6, return 6, fac(2): 2<=1 ? No. product 2 = 2*fac(1) product 2 =2*1=2, return 2, fac(1): 1<=1 ? Yes. return 1 Tracing the program …

127 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

128 Static (non-dynamic) and dynamic objects * ‘static variables’ reside in the system Stack * ‘dynamic variables’ reside in the system Heap (seen later …)

129 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! n For array implementation, the operations are performed in very fast constant time

130 Queue Overview * Queue ADT * Basic operations of queue n Enqueuing, dequeuing etc. * Implementation of queue n Linked list n Array

131 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. n Like customers standing in a check-out line in a store, the first customer in is the first customer served.

132 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

133 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!)

134 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

135 Using Queue int 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; }

136 Struct Node { double data; Node* next; } class Queue { public: Queue(); Queue(Queue& queue); ~Queue(); bool empty(); void enqueue(double x); double dequeue(); // bool full(); // optional void print(void); private: Node* front;// pointer to front node Node* rear;// pointer to last node int counter;// number of elements }; Queue using linked lists

137 class Queue { public: Queue() {// constructor front = 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 node Node* rear;// pointer to last node int counter;// number of elements, not compulsary }; Implementation of some online member functions …

138 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 newNode 5 58

139 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

140 Printing all the elements void Queue::print() { cout "; Node* currNode=front; for (int i = 0; i < counter; i++) { if (i == 0) cout << "\t"; elsecout << "\t\t"; cout data; if (i != counter - 1) cout << endl; else cout << "\t<-- rear" << endl; currNode=currNode->next; }

141 Queue using Arrays * There are several different algorithms to implement Enqueue and Dequeue * Naïve way n 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

142 * Naïve way (cont’d) n 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() front rear 9 rear = -1 front

143 * A better way n When enqueued, the rear index moves forward. n 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.

144 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. n OOOOO7963  4OOOO7963 (after Enqueue(4)) * How to detect an empty or full queue, using a circular array algorithm? n Use a counter of the number of elements in the queue.

145 class Queue { public: Queue(int size = 10);// constructor Queue(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 index int rear;// rear index int counter;// number of elements int maxSize;// size of array queue double* values;// element array }; full() is not essential, can be embedded

146  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

147 Queue constructor * Queue(int size = 10) n Allocate a queue array of size. By default, size = 10. n front is set to 0, pointing to the first element of the array n 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; }

148 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; elsereturn false; } bool Queue::full() { if (counter < maxSize)return false; elsereturn true; }

149 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 item values[rear]= x; // update counter counter++; // return true; } Or ‘bool’ if you want

150 Dequeue double Queue::dequeue() { double x; if (empty()) { cout << "Error: the queue is empty." << endl; exit(1); // return false; } else { // retrieve the front item x= values[front]; // move front front= (front + 1) % maxSize; // update counter counter--; // return true; } return x; }

151 Printing the elements void Queue::print() { cout "; for (int i = 0; i < counter; i++) { if (i == 0) cout << "\t"; elsecout << "\t\t"; cout << values[(front + i) % maxSize]; if (i != counter - 1) cout << endl; else cout << "\t<-- rear" << endl; }

152 Using Queue int 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; }

153 Results Queue implemented using linked list will be never full! based on array based on linked list

154 Queue applications * When jobs are sent to a printer, in order of arrival, a queue. * Customers at ticket counters …


Download ppt "List, (dynamic) linked list Let’s first forget about ‘classes’, but only a dynamic list. We make lists with ‘classes’ afterwards."

Similar presentations


Ads by Google