>> Well so now we want to talk about abstract data types
and stacks of queues. Another very interesting topic. Actually this is a lot more
interesting than templates. Templates you can see the power
in them, but it's sort of dull. Abstract queues and abstract data types,
however can be far more interesting. So allow me by start off by telling
you what an abstract data type is. It consists of a collection of data and
some operations on that data or subsets of the data and some axioms
or rules or behavior govern and interact with the operations. Now over this, the end of this course
and your next course in data structures, you're going to look at stack and queue. We're going to look at
that this semester. List, Vector, Deque, priority
queue, tables, or maps, associative array, set,
graph, and digraph. I've got a few more that
I don't care to name here. But there are going to be examples of
abstract data types that we will study over the next several months. The 2, arguably the 2 most
important are stack and queue because they are the most widely used. They are the first invented and they are
absolutely completely embedded in to all of computing top to bottom. Okay this is going to be
your introduction to stacks. So the stack. I'm sorry data type, this is just
an example of an actual data type. Now it's got, well a collection
is just elements of some type. And the operations are push, pop, top. I'll just name them a push,
pop, top, empty, size, and constrictors destructors. So the push operation will
put an element onto the stack. Pop removes the top element
from the stack. Top returns the top element
without changing the stack. Empty of course turns true if we
have, there's nothing in the stack. Size returns the actual number
of elements in the stack. The way push, and pop, and top
interact with each other is if you push an element out
of the stack it's on the top. And when you pop a stack the
top element gets removed. And so the physical model
for a stack is, I don't know a stack of
dishes in a salad bar. You may go to a salad bar
sometimes that have dishes that go down into a spring-loaded
hole in the counter. And that's a stack of dishes. There's always one on the top. When you put a dish onto the
stack, it's the top of the stack. When you remove the dish, you're
removing the top of the stack. An interesting property of stacks
is that the first item pushed onto the stack is the last one removed. In the case of a salad bar, you probably
don't want the last plate in there, because it's been there a month. But never the less, that's
what a stack does. And these axioms, this slide is
actually well a slide that I will reuse in data structures and we'll talk
about axioms in a little more detail. It basically says a few things
that are kind of obvious. Size, empty, and push
are always defined. Pop and top are always
dependent on non empty stacks. Let's see. S.push followed by pop
leaves a stack unchanged. After a push top returns
the element you just pushed. Push increases size by 1,
pop decreases size by 1. That's all 8 axioms and those 8 axioms
literally characterize what a stack is. So no matter how you implement
it any 2 stacks are what we would call isomorphic. You'll run across that term in
the discreet path [inaudible]. But because stacks have these
axioms associated with them, no matter how you implement them,
any 2 stacks are isomorphic. And this is what makes
stacks to useful in computing because they're independent
of implementation. Their behavior is independent
of implementation. So here's a stack model and
pictures of [inaudible]. [Inaudible] Okay well [inaudible] pop up that comes
on this computer in the classroom. So we're now talking
about uses of stack. And I'm going to go into some
of these in a little more detail in subsequent slides but we
use it in step first search which is a search process that is used. We'll talk about it some today
and it will be used many, many times in your career both in
school, and when you're a professional. We'll show how it's used
to evaluate closed fix to evaluated postfix expressions. How you convert infix to postfix. S is going to be part of
that stack that's covered in your project 2 assignment. Stacks are used in function calls in
the c plus plus runtime environment. We'll talk about that. And stacks are used to
implement recursion in programming and we'll
talk about that. Before I go into the
details on using stacks, I'm going to talk about what a queue is. A queue looks very similar to a stack. It's a collection of
elements of some type with operation line and it's got axioms. The operations for queue are almost
the same names as for stack push, pop, instead of top we have front,
and then we've got empty, size, and constrictor, destructors. So even through push and pop have the
same name, they have different behavior and with that, the behavior
of stack is last in first out. As in a stack of dishes,
the last one put on the stack is the first one removed. A queue is first come first serve. So the first one entering the
queue is the first one out. The last one entering the
queue is the last one out. So a queue kind of enters, you enter
a queue in one hand and you come out of the queue on the opposite end. So staying in line for
movie ticket is a queue and of course that's typically a
word I guess used probably more often than anyone but to me the way to
line queue up to get a ticket. The queue model is first in first out. The top of this here now end up. So I'm not going to be able to move my
mouse into that area, but you can see that the model is for a queue the items
enter at the back of the queue and are, so when you push an item onto
the queue it goes to the back. When you pop the item from the
queue it's popped from the front. So you can see in the first slide we
get push A and push B in that order. And then we push C and push
D. So now a queue of A, B, C, D. We pop and A comes off the front. We pop again and B comes off the front. And pop again and C comes off the front. Actually I guess we pop twice
and also push D and so the queue at that point is left having
A as it's only element. So queue is used in buffers. Queue is at the heart
of [inaudible] object. It's used in another kind of search
algorithm called breadth first search. And it's used in a variety of simulation
environments that are very important in engineering and other
modeling circumstances. So let's talk about depth first search. So the problem is to discover
a path from start to goal. So in our picture we have
start oh let me see here. Okay so maybe we can figure
out a way to get that virus to not show its ugly face
on this classroom machine. It's the system people are
going to have to work on that. But in any case so what
is depth first search? So let's just take a look at this graph. We have colored red a start
location and green a goal location and what the depth first search
and breadth first search seem to do is find a path from
the start to the goal. Here's the way that's, here's the way
that search works and it uses a stack as a control mechanism for the search. So it starts by pushing
the start onto a stack. I'm going to draw the stack
from left to right this time. The arrow indicating where
the top of the stack is. So the stack starts off empty. You push the start onto the stack
so the stack now has 1 on it. You find an unvisited location that's
accessible from the top of the stack and that location is 2 so
I push that onto the stack. And 2 I find an unvisited location
that I can get to from 2 which is 5 so I push 5 on the stack and
5, I push 6 on the stack. From 6 I push 8 on the stack. And so at this point my stack consists
of 1, 2, 5, 6, and 8 with 8 on the top. And when I get to 8 there's no place
to go that hasn't already been visited. So I go back to 6 and the way you
go back to 6 is by popping stacks. So popping removes 8 now 6
is at the top of the stack. Can I go anyplace else from
6 that hasn't been visited? And the answer to that is no because 5
and 2 have both been visited, well 5, 2, and 8 all 3 have been visited. And so you pop 6 and
go back to 5 and so on. You end up back at 1 because
you've exhausted the search on that branch from the base. So you're still at 1. So now we're going to try 3. That's another unvisited from 1 from
3 I can get to 9, that's unvisited. From 9 to 7 so that's a
push 3, push 9, push 7. From 7 I can get no unvisited. So I'll pop back to 9. From 9 I can get to another
unvisited and it's 12. So I push 12 and 12 I can get to
another unvisited 10, so I push 10. And 10 I can get to another
unvisited 11. And when I get there I
realize, oh that's the goal. So I've reached the goal. And furthermore what's in
the stack is a solution path. From 1, to 3, to 9, to 12, to 10, to 11
is a path from the start to the goal. Now you can think of
that as being a rat. We plot the map of the rat
down at this red location and the rad said okay I'm going to
explore this way, and then I'm going to explore this way, and
then I explore this way, and then I explore here,
oops dead end here. Well I've been down both of those,
so I'll just get back to the end. And then of course the rat
can tell by smell that he or she has been there before. So we get back here and try a
different untested route and so on. We can name a route using his sense of smell finally finding
the cheese at the green dot. So here's the same way and we're going
to illustrate breadth first search. You know breadth first
search you can use paper or rate analogy for breadth
first search. Only this time they say committee
of rats or a pack of rats. So what you do is you have an
entire pack of rats starting out at the red start location. You divide them into
3 sub-packs, 3 teams. Each to explore one of the
branches emanating from red. So you send and this
is done concurrently. So you send one team to 2, one
team to 3, and one team to 4. Well when you get to 2, you
break that team up into sub-team and send one to 5, and one to 6. The 3 teams breaks into sub-teams
send one to 10 and one to 9. Okay so the 6 team just
sends a delegation to 8 because there's no branching on there. And the 6 team gets a
sub-team to 8 and at that point they've exhausted
their possibilities. But the 9 team sends a delegation
to 7 and now it's 12 the delegation to 7 can't go any further, but the 9,
the 12 delegation can go to the 10 spot, which can then go to the 11 spot. So the advantage to breadth first
search is you're doing things in a sort of concurrent fashion. The disadvantage is it takes
more resources to do it. You have a whole pack of rats
involved instead of a single one. But how's that work in terms
of a computer algorithm? Well where you have a queue and
we draw a queue or an anomaly with the front of the queue to the left. So just like for stack we push on
the right, but unlike stack we pop on the left, not the right. So the way queue start, the way
that breadth first search works with the queue is again you
push the start onto the queue. Then what you do is you look at from
the front of the queue which is 1 in this case, you look at all the
places you can get to directly from 1. Let's think of those
as being children on 1. You push all the children or
the direct neighbors of 1. Let's call it neighbors. You push the neighbors
of 1 onto the queue and pop the queue which takes one off. So what you see left there
highlighted are all the neighbors of 1. Okay now 2 is at the front
of the queue so let's look at the neighbors of 2 which are 5 and 6. Of course 1 we don't count
because 1's already been visited. So we push the unvisited
neighbors of 2 onto the queue. That's 5 and 6. And we pop the queue which
removes 2 from the front. So you keep doing that. There's 4. Four has no unvisited neighbors
and so you just pop it. Five, 5 has no unvisited
neighbors so we'll just pop it. Six has no I'm sorry 6 has 1 unvisited
neighbor that'd be 8 so we push that on the queue and then pop it. Pop 6 off. Now we've got 9 there. Nine has 2 unvisited neighbors 7 and 12. You push them on the
queue and pop 9 off. Ten comes to the front. Ten has 2 unvisited neighbors
10, 12, 1, and 12. I'm sorry I guess 12 is already there
so 10's only unvisited neighbor is 11 and then you pop the queue and that's. So again now everything's visited so
there's 3 or 4 more pops and you get down to the front of
the queue being the goal and you know you have found the goal. Unlike depth first search with a stack,
however, we are now left with the path, the solution path in the queue. So if you want the solution path in
breadth first search you have to, whenever you make a discovery,
whenever you push unvisited neighbors onto the queue you have to make a record
of where those neighbors came from. So you just make that into
a little notation somewhere. We're going to think of
those as being pointers. So for example when I push the
neighbors 2, 3, and 4 onto the queue, they all had 1 for a parent. When I push 9 and 10 onto the queue
they all, they both had 3 for a parent. And when I pushed 7 and 12 onto the
queue they both had 9 for a parent. And when I pushed 11 onto the
queue had 10 for a parent. So just using those parent pointers
is how you reconstruct the path. So 11 has 10 for a parent, which has 3
for a parent, which had 1 for a parent. So that following those
parental pointers back to the beginning gives you a path that
you would discover from start to goal. Breadth first search is more costly because you need a whole
pack of rats as I said. On the other hand you get more out of
it because not only do you find a path, but you find a shortest path. So this path we can prove is a
shortest path from start to goal, which you have found with
breadth first search. That's not true for depth first search. In fact you may recall our
path was somewhat sequitous, which was amplified with
depth first search. So those are 2 classic algorithms. One uses stack, the other uses queue. Depth first search uses
stack for controlling. Breadth first search uses queue
for a controlling and both of them are very often use search
algorithms that have a role in web search and all
kinds of modern technology. You will of course learn about those in a lot more detail in
your array of courses. The last algorithm I will mention here
is evaluating postfix expressions. And here's an example of how that works. Here's a postfix expression 1,
2, 3, plus 4 star plus 5 plus. Postfix meaning you see
operants before the operator. So but that's a little bit cryptic
how you would evaluate that expression and only be evaluating it can we
probably convince ourselves that's it actually a valid expression. So let's take a look at this. So we evaluate it with a stack
and the way it works it you push when it's an operant you
push it onto the stack. So 1 is operant you push, 2 is operant
you push, 3 is operant you push. So after those 3 pushes
I start stack up top. When you get to an operator what
you do is you pop exactly the number of times required to get the right
number of arguments for that operator. So in case of a plus
operator you need 2 arguments. So you pop twice, which
pops 2 and 3 off of a stack. Then you apply the operator
to those 2 arguments. Then push that back onto the stack. So 2, 3 get popped. Then they get added together and the
result gets pushed onto the stack. Okay then I'll have to
[inaudible] that plus up there. Okay the next thing we're going to
encounter is a 4 that's an operant so we push it puts 4 on the stack. The next thing we see is an operator
times operator multiplication. That takes 2 arguments. So we pop 2 arguments
off the stack, 5 and 4. Multiply them because it's
a multiplication operator and push the result on the stack. Next thing we encounter
is another operator. So we do the same thing. We [inaudible] from here we pop 2
things off of the stack, add them up and push the result onto the stack. So that was that operator and you
see our [inaudible] is an operant so we push it onto the stack. And finally the last thing's an operator so we pop 2 things off
of the stack 5 and 21. Add them you get push the
result onto the stack. You've run out of expression
and the stack only has 1 line it in mainly a number and so you
[inaudible] expression is syntactically correct and computed as
[inaudible] mainly 26. And this is a famous algorithm
embedded in the early '60s as a way to evaluate expressions in general in
a programming language and then by one of the famous algorithm
people [inaudible]. These algorithms, this algorithm right
there is important enough so that in modern CPUs there is a hardware
implemented stack that is used precisely to store algorithms like this to
evaluate expressions [inaudible]. So that appears in chain
or [inaudible] code. And is evaluated be hardware
support in a stack, which is just on CPU of a computer. Converting from end fix to postfix, so to gently evaluate a whole
stack is another problem that is typically handled in software
and that maybe part of a compile for example to convert a normal
end fix notation expression into postfix notation expression. The reason postfix expressions are
good for computers to evaluate is that they are parenthesis free. So you just start off and each time
you get a new thing there's one, and only one thing to do depending
on the rules of engagement. So and when you get to the
end you make a calculation. Converting from a human readable form to that form however is a calculation
problem and that's often done in software and that algorithm
and an algorithm that can be used for that conversion process
also [inaudible] is discussed in your project 2 assignment so
you'll have some experience in that. And finally I want to mention
the runtime stack and recursion. So in programming languages A plus, C
plus plus, Java, Pascal and a whole slew of more modern languages than those
all have a runtime environment that uses a stack, a bunch
of calls to run a program. So just an add in running
a C plus plus program. Well first things that runtime
environment does is look for a function called main and it
pushes main onto the runtime stack. That, it mains, once [inaudible] pushed on that stack your programming
is running. Okay so then main, whatever's at the top to the runtime stack is what is
currently executed in your program. So the first thing that's
executed if function main. Function main, particularly
making another function call. And then that function call, that function would be pushed
onto the runtime stack. So main would no longer be
at the top of the stack. This new function would be at the
top of the stack that is now running. That new function may
make another function call so that another function will get
pushed onto the runtime stack. So that it now has 3 functions on it. Main at the bottom, the first
function that got called and then the second function
that got called. If that second function, when
that second function returns and it stops running returns value or whatever what will happen is
its popped off of the runtime stack and then execution is returned to
the previous function that was on top of the stack before this one got called. And it will pick up running
where it left off. And so that is the way a
running program executes. Pushing function calls onto the
runtime stack, popping them off when they finish running and then
the program ultimately terminates when main pops off the stack. Then the program run is over. That's the end of execution. That modern runtime environment with
stack recover is like I said part of virtually every modern program is in which it is literally a stack
that's part of the runtime environment. So stacks are everywhere. Queues are everywhere. Whenever you send a message across
the Internet to another machine, that incoming message
is stored in a queue so that the receiving machine can
read the message on its own time. So the machine can't
possibly be expected to read a message on the Internet time. It never knows when a
message coming in is coming in and it might be busy
doing something else. So it gets stored in a queue
on that receiving machine. Typically that queue is called a buffer
in that circumstance, but it's a queue and then the receiving end process
can read that message on its own time. So without queues you
couldn't have the Internet. Without stacks you couldn't
have a running program. They're absolutely fundament to
everything within the modern computing.