computer notes - Circular list


Published on

Published in: Technology
  • Be the first to comment

  • Be the first to like this

No Downloads
Total views
On SlideShare
From Embeds
Number of Embeds
Embeds 0
No embeds

No notes for slide

computer notes - Circular list

  1. 1. Data Structures Lecture No. 05___________________________________________________________________Data StructuresLecture No. 05In the previous lecture, we demonstrated the use of the circular list for the resolutionof the Josephus problem. After writing a program with the help of this data structure, aleader among ten persons was selected. You must have noted many things while tryingto solve the problem. These things will help us to understand the usage of datastructures in C++, thus making the programming easy. The code of the program isgiven below. #include "CList.cpp" void main(int argc, char *argv[]) { CList list; int i, N=10, M=3; for(i=1; i <= N; i++ ) list.add(i); list.start(); while( list.length() > 1 ) { for(i=1; i <= M; i++ ); cout << "remove: " << list.get() << endl; list.remove(); } cout << "leader is: " << list.get() << endl; }In the program, we include the file of the class CList and create its object i.e. list. Thenwe solve the problem by using the add, start, length, next, remove and get methods ofthe class CList.In the program, we have included already-defined data structure CList. After definingits different methods, we have an interface of Clist. There is no need to be worry aboutthe nature of the list i.e. whether it is linked list, doubly linked list or an array. For us,it is only a list to be manipulated according to our requirement. You will see that aprogrammer may use different methods of the list object to solve the problem. We addelements to the list by a simple call of add method and go to the first element of the listby start method. Here, the length method is used in the condition of the while loop.Then we remove elements from the list and use the next, get and remove methodsduring this process. We get the current element by using the get method, then removeit by calling the remove method and then go to the next element by the method next.This way, all the elements are removed from the list except one element, called theleader. This one element remains there as we execute the while loop one less than thelength of the list. Page 1 of 10
  2. 2. Data Structures Lecture No. 05___________________________________________________________________In singly linked list, the next returns false when it reaches to the last node due to thefact that the next field of the last node is set to NULL. But in a circularly linked listthere is no NULL. It will be there only when there is no node in the list.The whole process, which we carried out to solve the Josephus problem, can also becarried out with functions in C++. While adopting this way (of writing functions), wehave to write these functions whenever we write another program that manipulates alist. In this method, we define a class of the data structure list and its different methodsfor the purpose of manipulation. This way, this class, obviously its methods too, can beused in any program where the manipulation of a list is needed. Thus there is re-usability of the code. In a class, we encapsulate the data and its methods. This showsthat we are no longer interested in the internal process of the class. Rather, we simplyuse it wherever needed. The circular linked list, earlier used for the resolution of theJosephus problem, can also be employed in other problems. We have a class CList ofthis circular linked list through which any number of objects of data type of circularlinked list can be created. Thus we can assume the class CList as a factory, creating asmany objects of list as needed. This class and its objects in any program can be used tosolve the problems with the help of its interface. The interface of this class consists ofsome methods like add, remove, next, back, get and some other simple ones. Whilecarrying out programming, we will see that these classes (objects) help us very muchto solve different problems.Benefits of using circular listWhile solving the Josephus problem, it was witnessed that the usage of circular linkedlist helped us make the solution trivial. We had to just write a code of some lines thatsolved the whole problem. In the program, we included the class CList (which is of ourdata structure i.e. circular linked list) and used all of its methods according to therequirements. There was no problem regarding the working of the methods. We justcalled these methods and their definition in the class CList worked well.Now we will see what happens if we solve the Josephus problem by using an arrayinstead of the class in our program. In this case, we have to define an array and writecode to move back and forth in the array and to remove different elements properly ina particular order. A programmer needs to be very careful while doing this, to reachthe solution of the problem. Thus our code becomes very complex and difficult forsomeone to understand and modify it. Moreover we cannot use this code in some otherproblem. Note that here we are talking about the use of an array in the main program,not in the class that defines the CList data structure. There is no need to be worriedwhether an array, singly linked list, doubly linked list is used or circular linked listbeing employed internally in implementing the list in defining the class of list data type.We only want that it should create objects of list. The usage of the class of a datastructure simplifies the code of the program. We can also use this class whereverneeded in other programs. This shows that the choice of appropriate data structurescan simplify an algorithm. It can make the algorithm much faster and efficient. In thiscourse, we will see that there are different data structures, which makes the algorithmsvery easy to solve our problems. Later, we will see how some elegant data structureslie at the heart of major algorithms. There is also a course dedicated to study differentalgorithms and recipes that can be used to solve host of complex problems. Moreover, Page 2 of 10
  3. 3. Data Structures Lecture No. 05___________________________________________________________________we will study different data structures in detail and see that with the use of a properdata structure, we can solve a problem efficiently. A properly constructed datastructure will always help in the solution of problems.Abstract Data TypeA data type is a collection of values and a set of operations on those values. Thatcollection and these operations form a mathematical construct that may beimplemented with the use of a particular hardware or software data structure. The termabstract data type (ADT) refers to the basic mathematical concept that defines the datatype. We have discussed four different implementations of the list data structure. Incase of implementation of the list with the use of an array, the size of the array givesdifficulty if increased. To avoid this, we allocate memory dynamically for nodes beforeconnecting these nodes with the help of pointers. For this purpose, we made a singlylinked list and connected it with the next pointer to make a chain. Moving forward iseasy but going back is a difficult task. To overcome this problem, we made a doublylinked list using prev and next pointers. With the help of these pointers, we can moveforward and backward very easily. Now we face another problem that the prev pointerof first node and the next pointer of the last node are NULL. Therefore, we have to becareful in case of NULL pointers. To remove the NULL pointers, we made the circularlink list by connecting the first and last node.The program employing the list data structure is not concerned with itsimplementation. We do not care how the list is being implemented whether through anarray, singly linked list, doubly linked list or circular linked list. It has been witnessedthat in these four implementations of the list, the interface remained the same i.e. itimplements the same methods like add, get, next, start and remove etc. This provesthat with this encapsulation attained by making a class, we are not concerned with itsinternal implementation. The implementation of these abstract data types can bechanged anytime. These abstract data types are implemented using classes in C++. Ifthe list is implemented using arrays while not fulfilling the requirements, we can changethe list implementation. It can be implemented with the use of singly-link list or doublylink list. As long as the interface is same, a programmer can change the internalimplementation of the list and the program using this list will not be affected at all. Thisis the abstract data type (ADT). What we care about is the methods that are availablefor use, with the List ADT i.e. add, get, and remove etc methods. We have not studiedenough examples to understand all the benefits of abstract data types. We will followthis theme while developing other ADT. We will publish the interface and keep thefreedom to change the implementation of ADT without effecting users of the ADT.The C++ classes provide a programmer an ability to create such ADTs. What benefitscan we get with the help of these ADTs and classes? When we develop an ADT or aclass or factory then the users of this factory are independent of how this factoryworks internally. Suppose that we have ordered the car factory (car class) to produce anew car and it replies after a long time. If we ordered the remove method to removeone node and we are waiting and it keeps on working and working. Then we mightthink that its implementation is not correct. Although, we are not concerned with theinternal implementation of this ADT yet it is necessary to see whether this ADT isuseful for solving our problem or not. It should not become a bottleneck for us. If themethod we are using is too much time consuming or it has some problem in terms of Page 3 of 10
  4. 4. Data Structures Lecture No. 05___________________________________________________________________algorithm used. On one side, we only use the interfaces provided by these ADTs,classes, or factories as long as they do what they promise. We are not concerned withthe internal details. On the other hand, we have to be careful that these factories ormethods should not take too much time so that these will not be useful for theproblem.This distinction will always be there. Sometimes, the source code of classes is notprovided. We will be provided libraries, as standard libraries are available with thecompiler. These classes are in compiled form i.e. are in object form or in binary form.On opening these files, you will not see the C++ code, rather binary code. When youread the assembly language code, it will give some idea what this binary code is about.You can view the interface methods in the .h file. As an application programmer, youhave to see that the ADTs being used are written in a better way. The point to beremembered here is that you should not worry about the internal implementation ofthese ADTs. If we want to change the internal implementation of the ADTs, it can bedone without affecting the users of these ADTs. W hile writing a program, you shouldcheck its performance. If at some point, you feel that it is slow, check the ADTs usedat that point. If some ADT is not working properly, you can ask the writer of the ADTto change the internal implementation of that ADT to ensure that it works properly.StacksLet s talk about another important data structure. You must have a fair idea of stacks.Some examples of stacks in real life are stack of books, stack of plates etc. We can addnew items at the top of the stack or remove them from the top. We can only access theelements of the stack at the top. Following is the definition of stacks. Stack is a collection of elements arranged in a linear orderLet s see an example to understand this. Suppose we have some video cassettes. Wetook one cassette and put it on the table. We get another cassette and put it on the topof first cassette. Now there are two cassettes on the table- one at the top of other.Now we take the third cassette and stack it on the two. Take the fourth cassette andstack it on the three cassettes.Now if we want to take the cassette, we can get the fourth cassette which is at the topand remove it from the stack. Now we can remove the third cassette from the stackand so on. Suppose that we have fifty cassettes stacked on each other and want toaccess the first cassette that is at the bottom of the stack. What will happen? All thecassettes will fell down. It will not happen exactly the same in the computer. Theremay be some problem. It does not mean that our data structure is incorrect. As we seein the above example that the top most cassette will be removed first and the newcassette will be stacked at the top. The same example can be repeated with the books.In the daily life, we deal with the stacked goods very carefully.Now we will discuss how to create a stack data structure or a factory, going to createstack object for us. What will be the attributes of this object? During the discussion onthe list, we came to know that a programmer adds values in the list, removes valuesfrom the list and moves forward and backward. In case of a stack too, we want to add Page 4 of 10
  5. 5. Data Structures Lecture No. 05___________________________________________________________________things and remove things. We will not move forward or backward in the stack. Newitems can be added or removed at the top only. We can not suggest the removal of themiddle element of the stack.Let s talk about the interface methods of the stacks. Some important methods are: Method Name Description push(x) Insert x as the top element of the stack pop() Remove the top element of the stack and return it. top() Return the top element without removing it from the stack.The push(x) method will take an element and insert it at the top of the stack. Thiselement will become top element. The pop() method will remove the top element of thestock and return it to the calling program. The top() method returns the top-most stackelement but does not remove it from the stack. The interface method names that wechoose has special objective. In case of list, we have used add, remove, get, set as thesuitable names. However, for stack, we are using push, pop and top. We can depict theactivity from the method name like push means that we are placing an element on thetop of the stack and pushing the other elements down.The example of a hotel s kitchen may help understand the concept of stacks in acomprehensive manner. In the kitchen, the plates are stacked in a cylinder having aspring on the bottom. When a waiter picks a plate, the spring moves up the otherplates. This is a stack of plates. You will feel that you are pushing the plates in thecylinder and when you take a plate from the cylinder it pops the other plates. The topmethod is used to get the top- most element without removing it.When you create classes, interfaces and methods, choose such names which depictswhat these method are doing. These names should be suitable for that class or factory.Let s discuss the working of stack with the help of a diagram. Page 5 of 10
  6. 6. Data Structures Lecture No. 05___________________________________________________________________ top 1 top 7 7 top 5 5 5 top 2 2 2 2 push(2) push(5) push(7) push(1) top 21 top 7 7 top 7 5 5 5 top 5 2 2 2 2 top 2 1 pop() push(21) 21 pop() 7 pop() 5 pop()At the start, the stack is empty. First of all, we push the value 2 in the stack. As aresult, the number 2 is placed in the stack. We have a top pointer that points at the topelement. Then we said push(5). Now see how 2 and 5 are stacked. The number 5 isplaced at the top of number 2 and the pointer top moves one step upward. Then wepushed the number 7 which is placed on the top and the number 2 and 5 are below.Similarly, we push number 1. The last figure in the first row shows the stacked valuesof the numbers- 1, 7, 5 and 2.Let s pop the elements from the stack. The first figure of second row shows the popoperation. As a result, the number 1 is popped. Than again we push the number 21 onthe stack. The number 7, 5, and 2 are already in the stack and number 21 is pushed atthe top. If we pop now, the number 21 is popped. Now number 7 is at the top. If wepop again, the number 7 is popped. Pop again and the number 5 is popped and number2 remains in the stack. Here with the help of this diagram, we are proving that thevalues are added at the top and removed at the top in a stack.The last element to go into the stack is the first to come out. That is why, a stack isknown as LIFO (Last In First Out) structure. We know that the last element pushed inthe stack is at the top which is removed when we call pop. Let s see some otherscenarios. What happens if we call pop() while there is no element? One possible way-out is that we have isEmpty() function that returns true if stack is empty and falseotherwise. This is a boolean function that returns false if there is no element in thestack. Otherwise, it will return true. The second option is this that when we call pop onan empty stack, it throws an exception. This is a concept of advanced C++. Exceptionis also a way to convey that some unusual condition has arisen or something has gonewrong. Suppose, if we have a division method and try to divide some number with Page 6 of 10
  7. 7. Data Structures Lecture No. 05___________________________________________________________________zero. This method will throw division by zero exception. Currently we will not throwan exception but use the isEmpty() method. The user who is employing the stack isresponsible to call the isEmpty() method before calling the pop. Call the pop method ifisEmpty() returns false . Otherwise, there will be a problem.Stack Implementation using arrayLet s discuss the implementation of the stack. Suppose we implement the stack usingthe arrays. The stack shown in the above diagram may be considered as an array. Herethe array is shown vertically. We can implement the stack using array. The interfacewill remain as push and pop methods. The user of the stack does not need to knowthat the stack is internally implemented with the help of array. The worst case forinsertion and deletion from an array may happen when we insert and delete from thebeginning of the array. We have to shift elements to the right for insertion and left forremoval of an element. We face the same problem while implementing the list with theuse of the array. If we push and pop the elements from the start of the array for stackimplementation, this problem will arise. In case of push, we have to shift the stackelements to the right. However, in case of pop, after removing the element, we haveto shift the elements of stack that are in the array to the left. If we push the element atthe end of the array, there is no need to shift any element. Similarly as the pop methodremoves the last element of the stack which is at the end of the array, no element isshifted. To insert and remove elements at the end of the array we need not to shift itselements. Best case for insert and delete is at the end of the array where there is noneed to shift any element. We should implement push() and pop() by inserting anddeleting at the end of an array. top 1 2 5 7 1 7 5 0 1 2 3 4 2 top = 3In the above diagram, on the left side we have a stack. There are four elements in thestack i.e. 1, 7, 5 and 2. The element 1 is the extreme-most that means that it is insertedin the end whereas 7, 5, and 2 have been added before. As this is a LIFO structure sothe element 1 should be popped first. On the right side we have an array with positions0, 1, 2, 3 and so on. We have inserted the numbers 2, 5, 7 and 1. We have decided thatthe elements should be inserted at the end of the array. Therefore the most recentelement i.e. 1 is at position 3. The top is the index representing the position of the mostrecent element. Now we will discuss the stack implementation in detail using array.We have to choose a maximum size for the array. It is possible that the array may fill-up if we push enough elements. Now more elements cannot be pushed. Now whatshould the user of the stack do? Internally, we have implemented the stack using arraywhich can be full. To avoid this, we write isFull() method that will return a boolean Page 7 of 10
  8. 8. Data Structures Lecture No. 05___________________________________________________________________value. If this method returns true, it means that the stack (array) is full and no moreelements can be inserted. Therefore before calling the push(x), the user should callisFull() method. If isFull() returns false, it will depict that stack is not full and anelement can be inserted. This method has become the part of the stack interface. So wehave two more methods in our interface i.e. isEmpty() and isFull().Now we will discuss the actual C++ code of these operations. These methods are partof stack class or stack factory. We have an array named A while current is its index.The code of pop() method is as: int pop() { return A[current--]; }In this method, the recent element is returned to the caller, reducing the size of thearray by 1.The code of push method is: void push(int x) { A[++current] = x; }We know that ++current means that add one to the current and then use it. That alsoshows that element x should be inserted at current plus one position. Here we are nottesting that this current index has increased from the array size or not. As discussedearlier that before using the push method, the user must call isFull() method. Similarlyit is the responsibility of the user to call the isEmpty() method before calling the popmethod. Therefore there is no if statement in the push and pop method.The code of the top() method is: int top() { return A[current]; }This method returns the element at the current position. We are not changing the valueof current here. We simply want to return the top element. int isEmpty() { return ( current == -1 ); }This method also tests the value of the current whether it is equal to -1 or not. Initiallywhen the stack is created, the value of current will be -1. If the user calls the isEmpty() Page 8 of 10
  9. 9. Data Structures Lecture No. 05___________________________________________________________________method before pushing any element, it will return true. int isFull() { return ( current == size-1); }This method checks that the stack is full or not. The variable size shows the size of thearray. If the current is equal to the size minus one, it means that the stack is full and wecannot insert any element in it.We have determined the cost and benefit of all the data structures. Now we will seehow much time these methods take. A quick examination shows that all the fiveoperations take constant time. In case of list, the find method takes too much time as ithas to traverse the list. Whereas the add and remove methods are relatively quick. Themethods of stack are very simple. There is no complexity involved. We insert elementat one side and also remove from that side not in the middle or some other place.Therefore we need not to carry out a lot of work. During the usage of the array, thestack methods push, pop, top, isFull and isEmpty all are constant time operations.There is not much difference of time between them.The complete code of the program is:/* Stack implementation using array */#include <iostream.h>/* The Stack class */class Stack{ public: Stack() { size = 10; current = -1;} //constructor int pop(){ return A[current--];} // The pop function void push(int x){A[++current] = x;} // The push function int top(){ return A[current];} // The top function int isEmpty(){return ( current == -1 );} // Will return true when stack is empty int isFull(){ return ( current == size-1);} // Will return true when stack is full private: int object; // The data element int current; // Index of the array int size; // max size of the array int A[10]; // Array of 10 elements};// The main methodint main(){ Stack stack; // creating a stack object Page 9 of 10
  10. 10. Data Structures Lecture No. 05___________________________________________________________________ // pushing the 10 elements to the stack for(int i = 0; i < 12; i++) { if(!stack.isFull()) // checking stack is full or not stack.push(i); // push the element at the top else cout <<"n Stack is full, cant insert new element"; } // pop the elements at the stack for (int i = 0; i < 12; i++) { if(!stack.isEmpty()) // checking stack is empty or not cout << "n The popped element = " << stack.pop(); else cout <<"n Stack is empty, cant pop"; }}The output of the program is:Stack is full, cant insert new elementStack is full, cant insert new elementThe popped element = 9The popped element = 8The popped element = 7The popped element = 6The popped element = 5The popped element = 4The popped element = 3The popped element = 2The popped element = 1The popped element = 0Stack is empty, cant popStack is empty, cant popHowever, a programmer finds the size-related problems in case of an array. Whatshould we do when the array is full? We can avoid the size limitation of a stackimplemented with an array by using a linked list to hold the stack elements. Furtherdiscussion on this issue will be made in the next lecture. Page 10 of 10