 Hello and welcome to this session in which we will look at stack which is a particular kind of sequences with certain operations defined on them which occur quite frequently in computer science. So, in general there are many data types whose value is a sequence of values of certain type. So, there are different based on the operations that you perform on these sequences. So, all these data types where value is only a sequence of elements of certain kind, but they differ based on which operations are allowed to be performed on that sequence. So, restricting the allowed operations prevents us from using those values in an improper way. So, you are only allowed to do certain things to that sequence. So, anyone reading your program would also understand what you are going to do with that because once you declare that your variable is of that type it defines what are the operations that you are going to perform on it and then you know that only those operations are going to be performed and nothing else. So, although the values of these data types are all sequences of certain type, but the operations performed are different and therefore, they are considered to be different abstract data types. So, stack is one of the simplest example whose value is a sequence of type. So, similarly a stack can contain elements of any type. So, it is a stack of type T is also a sequence of type T. So, the value of a stack is always a sequence of type T, but only certain operations are defined for a stack. On a sequence you can define your own operations in any way using the way we have done it define it for the empty sequence and assuming it is defined for the empty sequence define it for the sequence obtained by pushing an element in it. But for a stack you are only given certain operations that are allowed and you are supposed to use only those in of operations and nothing else not define your own extra operations for that. So, this allows efficient implementation of this restricted set of operations. So, somebody who knows that you only want to use a stack knows what are the operations you want to use on this data type and therefore, they can give a better implementation of that data type and this also allows clearer descriptions of other functions as we will see and a stack is the simplest sort of data type whose value is a sequence of elements of certain type. So, what are the operations allowed for a stack? So, remember stack is also a sequence of certain type. So, to define the operations we still have to follow the same methodology define it for the empty sequence and define it for a sequence obtained by pushing an element x into it. So, the first operation is called the empty operation it just says whether the stack is empty or not. So, empty for the sequence pi returns true, but for empty for any sequence obtained by pushing an element x into a sequences is false no matter what x is. So, this defines the empty operation then similarly we have a push operation on stacks. So, note that this is different from the push operation on a sequence although in the case of stack this does essentially the same thing as what a push operation on a sequence does. So, remember push x s, s is a stack here and we are pushing an element x into it whereas previously s was an arbitrary sequence and we were pushing an element in that to get a new sequence. So, push x from a pi to push an element x into a sequence of stack. So, this gives a new sequence whose value obtained by pushing x into the empty sequence. So, on the right hand side we are defining the value of the sequence, but on the left hand side we are performing the push operation on a stack, but a stack itself is a sequence. So, the stack could be empty or it could be obtained by pushing some element y into another stack. So, this says that if I push x into an empty stack the sequence that I get is push x into the empty sequence. If I push an element x into the stack obtained by pushing y in a sequence s then I get the element of sequence obtained by pushing s into the sequence obtained by pushing y in s. So, this distinction should be clear we are defining this operation for stack, but the push operation originally we had defined was for sequences although in this case there is no difference between the two they do the same. Now, here is another operation called pop on a stack. So, pop for an empty stack is undefined. So, if the sequence is empty the pop operation is undefined. What this means is that if you try to execute it it will give an error. So, you have to make sure that whenever you define any operation using stack whenever a pop operation is applied to a stack you have to ensure that the stack is not empty. If it is empty then what you are defining is incorrect it will be not be properly defined because pop is not defined for the empty stack. On the other hand if pop is defined for a stack which has been obtained by pushing an element x into it. In this case pop of push of x comma s is the sequence s itself or the stack s itself. In this case the pop operation gives you its value is the sequence s. Similarly, there is another operation called top, top of the empty stack is again undefined. So, you should make sure you never use top with an empty stack. Top of push of x comma s is the element x itself. And now these are the only operations allowed for a variable of stack time. So, just like when you declare an int variable in a program you know what are the operations allowed for an integer. When you declare a variable of type stack you know that these four operations are the only ones that are allowed for a stack. And these are the ones that you should use for defining other functions. You can use stacks, but only these operations on stack. So, let us look at an example of writing a program using stack or instead of saying programming let us say think of defining a function using stack. So, this function takes a sequence of opening and closing parenthesis as the input. And we want to write a function that returns true if the sequence is balanced and it should return false otherwise. So, what do we mean by a balanced sequence that every opening parenthesis or every left parenthesis has a corresponding closing parenthesis and it should be properly nested inside each other. So, the first sequence in the last line which is left, left, right, left, right, right is a balanced parenthesis string. But the other one left, right, right, left is not balanced parenthesis string because the last left bracket has no corresponding matching right bracket which follows after it. So, we want to write a function which says whether a given sequence of brackets of parenthesis is balanced or not. So, here is how we define a function balanced S where S is a sequence of parenthesis. So, instead of directly defining this we will define another function which takes two parameters S1 and S2. S1 is a sequence of parenthesis that we want to test whether it is balanced or not while S2 will be a stack of opening parenthesis. So, the value of S2 will be a stack whereas the value of S1 will be a sequence of parenthesis string. And in a sense the stack is used to remember which what are the left parenthesis seen so far in S1. So, how do we define this function? So, we define valid of 5 comma S2. So, if my sequence of parenthesis strings is empty but the stack is something else S2. We return true if and only if the stack is empty. So, if the parenthesis string is empty and S2 is not empty then the result is false. Otherwise, if the string is true if the S1 is empty and S2 is also empty then the result is true. Now, if S1 has been obtained by pushing a left bracket. So, if push left bracket S1 comma S2 then what do we do? We push that left bracket so, we have seen a new left bracket we push that left bracket into the stack S2 and check whether this remaining sequence S1 is valid. So, whenever I get a left parenthesis in my sequence of parenthesis I will push that into the stack S2 and now check whether the remaining sequence S1 is valid taking into account this stack new stack S2 obtained by pushing this into S2. So, note that we are assuming that valid S1 S2 has been defined for all possible stacks S2. So, the first statement defines valid 5 S2 for all possible values of the stack S2 and now assuming we have defined valid S1 S2 for all possible stacks S2 we are defining valid push left bracket S1 S2 for all stacks S2 and now when I get a right bracket in S1. So, when S1 is obtained by pushing a right bracket into S1 then I will define it to be false if the stack is S2. So, what does this mean? If stack S2 is empty and I have got a right bracket in sequence S1 it essentially means that I have got a right bracket for which there was no matching left bracket found earlier. The stack has no left brackets remaining but I have got a right bracket in my sequence S1. All the left brackets obtained earlier have been already matched. So, if the stack is empty and I get a right bracket then I return false otherwise I apply the pop operation to the stack S2. So, I remove one left bracket from the stack S2 which matches with the right bracket that we have in S1 and now check whether the remaining sequence S1 matches with the remaining left brackets that remain in the stack S2. And the definition of the balance function for a sequence S is it is defined to be valid of S comma 5. So, I start with the empty stack and call the function valid with the sequence S and 5. So, if this returns true then balance test is true, this returns false then balance test is false. So, note that this clarifies that we will never get an error in this way of doing things because whenever we are applying the pop operation to the stack we are making sure that it is not empty. So, the third definition if S2 is empty then we are directly returning false and only if S2 is not empty we are called applying the pop operation to the stack. So, whenever you use a pop operation on a stack you have to make sure that the stack is not empty. This will guarantee that your program will never have any errors and if you define things like this where if the stack is empty you do something and if it is not empty only then you do apply a pop operation it will ensure that you never use any undefined operations in your program. So, note that in this case for the stack we are using only allowed operations empty push and pop here we did not use the top operation, but using this simplifies the description of other operations on sequences. So, here we wanted a operation on sequences to test whether a sequence is balanced or not, but we can use a stack to define that operation. So, here is another example of reversing a sequence using a stack. So, we had seen one definition of reverse which use the concatenate operation and so on. Here we will see another way of defining this function. Again we will use a different function they call help which is sometimes called a helper function also. So, another function help S1, S2 that uses a stack S2 to reverse the sequence S1. So, here help of empty S2 is defined to be S2 itself. So, remember S2 itself is a sequence a stack is also a sequence. So, help of pi comma S2 is the sequence S2. If I have a sequence help apply push XS1 S2, the S1 is obtained by pushing X into S1. Then I simply define it to be the help sequence obtained by help applied S1 by pushing the element X into the stack S2. So, in this case the push XS2 operation on the right hand side is the push operation on the stack S2, the pushing the element into the stack whereas the push on the left is the way push operation used to construct a new sequence from S1. So, we constructed a new sequence push XS1 with a stack S2 then the help operation for that is the same as help applied to S1 pushing X into S2. And the reverse of S1 is nothing but the result of help applied to the sequence S1 with an empty stack. So, what this essentially means is if I want to reverse a sequence I take one element from it push it into a stack, take the next element push it into the stack, take the third element push it into the stack. The sequence that results in the stack is nothing but the reverse of the original sequence. So, you are essentially adding the sequence in an empty stack. So, help S1, 5 means you are just pushing the elements of the sequence one by one into an empty stack and generating a new sequence the value of the stack will be the value of the reverse of the original sequence. So, try to show that this gives the same reverse function as the one we had defined earlier using the concatenate operation. So, this gives an alternative way of implementing the reverse function using a stack. So, here are some exercises to understand properties of stack. So, is it true that push of top of S comma pop of S is always equal to S for all stacks S. If I pop S and push the element top S into that is the result always equal to the stack S same stack S for all S if not under what conditions will this property be true. And now although we do not have a bottom function allowed as part of the stack we can write a bottom function for stack using the allowed operations on the stack. So, strictly speaking this is not needed but suppose you need to do it how will you write such a bottom function that returns the bottom element of a stack S. Note that in this case you do not need to define the bottom function the way we have been defining earlier that defining it for phi and defining it for push phi S. You can assume that S is a stack and use the operations defined for stack to return the bottom element of the stack. So, you can say check whether the stack is empty and if it is empty of course the bottom element is undefined and if it is not empty then you can use pop or top operations on the stack and define the bottom function using that. So, in this case you can get a simpler way of defining using the given operations on the stack S. So, we look at more data structures which are all of the sequence type but they differ only in terms of the operations that can be performed on them. Thank you.