10 Common Coding Interview Problems - Solved!
Aug 15, 2023
10 Common Coding Interview Problems - Solved!
Preparing for coding interviews? Competitive programming? Learn to solve 10 common coding problems and improve your problem-solving skills. 💻 Code: https://gist.github.com/syphh/173172e … ✏️ Course developed by Inside code. Check out their YouTube channel: / @insidecode ⌨️ (0:00:00) Introduction ⌨️ (0:00:37) Valid anagram ⌨️ (0:05:10) First and last index in sorted array ⌨️ (0:13:44) Kth largest element ⌨️ (0:19:50) Symmetric tree ⌨️ (0:26:42) Generate parentheses ⌨️ (0:37:03) Gas station ⌨️ (0:50:06) Course schedule ⌨️ (1:06:50) Kth permutation ⌨️ (1:20:13) Minimum window substring ⌨️ (1:47:46) Largest rectangle in histogram ⌨️ (2:10:30) Conclusion 🎉 Thanks to our Champion and Sponsor supporters: 👾 Raymond Odero 👾 Agustín Kussrow 👾 aldo ferretti 👾 Otis Morgan 👾 DeezMaster — Learn to code for free and get a developer job: https://www.freecodecamp.org Read hundreds of articles on programming: https://freecodecamp.org/news
Content
0.08 -> In this course you'll learn how to solve
10 Very popular coding interview problems,
5.04 -> and you'll learn the theory behind the solutions,
so you'll be better equipped to solve other types
10.64 -> of problems as well. We'll come to the 10
popular coding interview problems course 10
16.32 -> well chosen problems that cover different
algorithms and data structures topics
20.72 -> to increase your knowledge
and prepare for interviews.
23.92 -> Please try to solve before watching the solution
prepare yourself and see you in the first lecture
36.16 -> Welcome to this video, where we will solve
the first problem of this course valid anagram
40.96 -> acquired easy problem compared to Knucks wants we
are given two strings s one and S two and we are
47.92 -> asked to check if their anagrams. Two strings are
anagrams if they're made of the same characters
54.08 -> with the same frequency just in a different order.
For example, with a string standard and garden,
59.76 -> we take one of them will rearrange its
characters and we get the other one.
65.6 -> How are we going to solve this problem? We know
the two strings are anagrams if they have the
70.48 -> same characters with the same frequency. So what
we can do is to calculate the frequency of each
75.36 -> character and as one, calculate the frequency of
each character in s two and compare the results.
82.16 -> But what structure do we use to do so we can use
an array where each cell represents the number
87.2 -> of frequencies, they all start at zero, then for
each character in the string will increment the
92.48 -> corresponding cell. This solution is suitable
when the alphabet is small, while the number of
98.8 -> possible characters is not big. For example,
if the strings can only be made of lowercase
104.08 -> alphabetical letters, would need an array of 26
elements, it's fine. But it's not necessarily
110.24 -> the case. They can contain all the characters.
And we have 1000s and 1000s of existing possible
117.12 -> characters, the array would take a lot of
time and space to create it and traverse it.
122.48 -> The best structure for our problem is the hash
table structure that maps unique keys to values.
128.8 -> In our case, the key will be the coke
to other values number of occurrences.
134 -> For example, if you have nameless and salesman
whose name was we get this hash table.
138.96 -> And the salesman we get this one. Do they have the
same keys with the same values so they're equal?
145.76 -> It means that nameless and salesmen are anagrams.
152.08 -> Encode who first showed that both strings
have the same length, because if they're not,
156.32 -> it's impossible for them to be anagrams. Then who
grid our two hash tables, and we start to our C
164 -> for each character and as one if the key is
already existing. And for one who does increment
169.6 -> else, we created a new set its value to one than
for us to same logic, but with freq. Two. And
177.12 -> now that we filled them with each other, they
have the same values. For each cane frog one,
182 -> if it doesn't exist in frog two, or for
quality isn't able to frog to off key,
186.48 -> return false. It means that either the character
of us one doesn't exist in our stew, or they don't
191.92 -> appear the same number of times after the loop,
if we didn't find any difference, we return true.
201.04 -> And in Python, we don't ever need to
write all this because we have a class
204.88 -> named counter in collections module that builds
the table of occurrences just by passing the
209.28 -> string as an argument. And we can compare
dictionaries with the equality operator.
214.8 -> So in reality, who does return count
off as one equal to count off as two.
221.84 -> For the time complexity, in the worst case, as
well as to have the same length. Let's name it
227.28 -> as we're traversing N characters at most three
times, and Sujan. Inserting in a hash table
233.12 -> costs are one in average, got off, amp was off and
was off and we'll give some of amp on complexity.
240.88 -> And for the space complexity, we have an hour
for each table because they can contain up to n
245.92 -> keys each we got an often space complexity.
We still have another solution to discuss.
253.68 -> You have to know the two anagrams how
the same lexicographically sorted string,
258.32 -> for example, with nameless and salesman. If we
sold nameless, we get a ELMN s s. I will salesman
266 -> same thing. So in the second solution, who
do salt posturings and compare the results
272.96 -> in code, after the equal condition,
returns sorted as one is equal to sorted
279.072 -> as two. But sorting a string of characters
because of n log n time we're doing it twice
285.04 -> was after comparing. We've got an off n log n
time complexity. And for the space complexity,
291.2 -> we have often for the sorted result twice,
we'll get an off air space complexity.
298.64 -> Who's the end of this video? whistle
the valid underground problem,
302.64 -> I hope that you understood the ball
solutions and seen the next one
313.44 -> Welcome back to the course in this lecture
rule. So the first and last position problem,
318.56 -> you are given a sorted array of integers are an
integer target. And we're asked to find the index
324 -> of the first and last position of target and our
if Target can't be found in our return minus one
330.16 -> minus one. For example, if our is 245-555-5799,
and target is five, the author's would be to six
339.68 -> because the first position of target
is to, and his last position is six.
345.2 -> First of all, because the array is sorted,
all the elements with the same value will
349.44 -> be ages into each other. For example, here all
positions of the value five are consecutive.
355.84 -> It means that the first possible solution is to
start traversing the array from the beginning
360.48 -> for the first position of target, and keep
walking until finding the last position.
365.44 -> With our example, we have two four,
then five who found the first position
370.48 -> we can walk in, we have 5555. This one is the
last one, we found the N position, we turn them
379.6 -> in code, we start traversing indexes of our and if
our phi is equal to target, it means that we found
385.68 -> the start position. Now we keep walking while the
next element exists and is equal to target y plus
392.56 -> one is smaller than length of our and our five
plus one is equal to target increment i. After
399.12 -> the while loop. INR represents the position of
the last occurrence. This is why we return start i
406.96 -> and if the fall ends without
having returned the result,
409.84 -> it means that we didn't find target in
our at all return minus one minus one.
416.8 -> Know that it's possible to have sought position
equal to and position it happens when there
421.2 -> is only one occurrence of targeting our for
the time complexity on target exists, which
427.68 -> covers the part befores first position, third
sequence of occurrences and where it doesn't,
433.12 -> which covers a whole array. In both cases, which
covers almost add elements, where n is the number
438.88 -> of elements of our we've got a time complexity
of add a cost and space complexity because
445.68 -> we're just using n variables. This solution uses
linear sir, to give an off on time complexity.
455.44 -> Both the resorted so we can think of using binary
search. Let's try to use binary search to find the
461.76 -> start position. With binary search, you can find
the position of an element in a sorted array.
467.84 -> But here, we're not searching for any position
of target, we're searching for the first one.
474 -> R is the first position of target
if r is equal to target, obviously,
478.56 -> but also r of i minus one has to be smaller
than target smaller because the array is sorted.
485.52 -> So we will use binary search normally, but
to add a second condition before returning.
492.16 -> Let's try it with our example. Left and right
solid first and last element of our as usual.
498.24 -> Man, his left password divided by two, we get four
here is true that our four is equal to target,
504.56 -> but it's not enough our four minus one is
not smaller than target. So med is not the
509.52 -> starting position of target. But should you
go to the left part or to the right part now,
515.92 -> our have made is not created on target. So the
first position can only be in the left part
521.92 -> we continue made is now zero plus
three divided by two we get one,
526.4 -> our phone is smaller than target. So the starting
position can only be in the right part we continue
532.72 -> made is now two plus three divided by two which is
to have meat is equal to target and our male minus
538.64 -> one is smaller than target. So meta represents
the first position of target, we return it.
545.28 -> In code, we're going to first have an early
exit condition for the case where the first
549.12 -> element is equal to target with DACA know that
zero is the starting position returned zero.
555.68 -> By the way, in solutions based on binary
search, put as much early exit conditions
560.32 -> as possible to handle edge cases
and avoid out of bounds problems.
565.6 -> After it we initialize left and right
at zero and minus one respectively.
570.16 -> Zero and r minus one are the indexes of the
first and last element of R. Now while of is
575.84 -> smaller than or equal to right, we calculate mid
index is left plus rho divided by two. Afterward,
582.4 -> we have three cases the case where both conditions
are respected. where alpha is equal to target on
588.08 -> our beta minus one is smaller than target. It
means that made is a sort of position we turn it
595.12 -> second case are made is smaller than
target. It means that we're still before
599.36 -> the starting point. position. This is why we
take left and mid plus one to start again in
603.92 -> the right part. Else, it means that we exceeded
the consecutive sequence, or we're excited but
610.4 -> not other beginning. So start can only be in the
left part, we take rato mid minus one of the wall
619.36 -> didn't return the result is means the target
doesn't have an existing our return minus one.
626.72 -> Now we found this thought in the experts,
we still need to find the end index,
631.28 -> we can think of just walking starting from
start until we find the last position of target.
636.4 -> But it would ruin everything we
did, because in the worst case,
639.68 -> we need to traverse the whole array, which
results in an old Anton complexity same as
644.16 -> the first solution you guys did to find the
end index, we will also use binary search.
651.6 -> But the condition is a bit different from the
first time when searching for the start position
656.64 -> are of men how to be equal to target and our male
minus one smaller than target. And for the end
662.64 -> position, our med has to be equal to target and
our mid plus one has to be greeted on target.
669.12 -> Next element has to be greater because it
would mean that from the right, we're not
672.88 -> in consecutive Target Elements anymore, so the
actual position is the last position of target.
680.24 -> In code, we just change a few things, the only
exit condition occurs now when the last element
685.76 -> is equal to talk, it means that the last position
of target is in the last index of our n minus one
691.52 -> without a return. Then in the three cases, the
cases where we will turn mid is when our made is
697.76 -> equal to target, and our met plus one is greater
than target. For the two remaining ones, he won
704.4 -> our main is greater than target, then we exceeded
the consecutive sequence, we'll go to the left
709.36 -> part, right becomes mere minus one else, it means
that either will be for the consecutive sequence
715.76 -> or incited but not at the end, so and can only
be in the right part, left becomes mid plus one.
723.6 -> Now we have our fine salt function,
we have our Find and function,
727.44 -> we can move to the main solution function. First
of all, we have some early exit conditions,
734.24 -> who can identify at least three cases
where we can't find target role.
738.08 -> When the array has no elements, one target is
smaller than the first element. And when target
742.96 -> is greater than the last element in the last two
ones. Because the array is sorted, who can deduce
748.72 -> the target is not equal to all other elements. If
at least one of these conditions is true, without
759.28 -> the return minus one minus one health who co
found sought to give the stock position we
760.72 -> call font and to find the end position and wait
on start. And if Target doesn't exist in our
767.36 -> sorted annual have returned minus one
bath, we still got the expected result.
774.4 -> For the time complexity, we're using binary
search twice. And binary search has an
779.2 -> awful lot and time complexity because
we keep dividing the input size by two,
783.52 -> two times of work and gives us
all of log n time complexity.
788.72 -> Add for the space complexity, you'll get all
one because we're just using eight variables.
795.6 -> Before ending this lecture, I want to tell you
that if you're not comfortable with binary search,
800.32 -> you should really work on it because
it's a fundamental algorithm technique
803.84 -> that appears in many problems on this one, find
peek first bad version, and many other ones.
812.48 -> Lose Hello this lecture, I hope that you
understood the solutions and seen the next one.
824.32 -> Welcome back to the course in this lecture, we
will solve the cliff largest element problem,
829.12 -> you are given an array of integers and
an integer k. And we're asked to find
833.12 -> the K eighth largest element. For example,
if you have are equal to four to 9756713
840.72 -> and k equal to four, the output would be
six because the largest element is nine,
845.6 -> the second largest is seven, the third
largest is seven and the fourth largest is six
853.36 -> Firstpost possible solution that we may think of
is to remove the maximum element k minus one times
859.04 -> because after doing so, the next maximum
represents the K eighth largest element.
864.16 -> For example with our array K is four. So
we remove the maximum element three times
870.48 -> first iteration Max is mine we remove it, second
iteration Max is seven we move it third iteration
878 -> Max is seven we remove it now that we finished
the three iterations the maximum in the remaining
884 -> elements is the fifth largest element of the
original array, it six here who returned it.
891.44 -> In code, we have a follow various repeated k
minus one times one remove the maximum element
897.36 -> after the loop, return max of all But for the
time complexity suited for the maximum cost of n,
904.88 -> where n is the number of elements and removing
it from the array cost, and in the worst case,
909.68 -> because we may need to shift all the n minus
one elements, and our loop is repeated k minus
915.92 -> one times we'll have k minus one times to add,
plus add for finding the final largest element.
922.32 -> In total, we have k minus one times two n
plus n, which is two times k times n minus n,
928 -> which gives us off k times our time complexity,
where n is the number of elements of our
935.12 -> own, although in reality during the
iterations, and will decrease because
938.48 -> we have less and less elements, but we get the
sample complexity. And for the space complexity,
944.08 -> we're not using input size related variables,
we have a constant space complexity.
949.92 -> This solution is a bit slow. Let's move to the
next one. The idea of the second solution is to
955.44 -> start by sorting the array because by doing so, we
know that the largest element is other last cell,
960.8 -> the second largest element just before it, the
third largest just before it, and so on with our
966.8 -> array was sorted, and k is four. So return
the fourth element starting from the end,
972.8 -> in a gentle way, we sold our and we turn our
n minus k, and minus k represents the index
978.8 -> of the key elements starting from the end. For
the time complexity, we have both analog and for
984.96 -> sorting the array and all one for accessing,
we've got an O of n log n time complexity.
990.72 -> And for the space complexity, it depends on
the space complexity or the sorting function.
996.72 -> This solution is way faster than the first
one except in some cases, but we still have
1000.88 -> an all the solution to discover. In the first
solution we didn't have to solve to it is good,
1007.12 -> but searching for the maximum cost at each
iteration, which slows down the process.
1012.4 -> What if instead of getting the maximum cost log
out only yours is possible by using a priority
1018.32 -> queue. A priority queue is a queue where the next
element to be popped is not the first one that
1023.44 -> entered with the one with the highest priority.
And it's usually implemented with a heap. If you
1028.8 -> don't know about heaps, and priority queues,
you should really watch my YouTube video on
1032.88 -> the subject, you will find the link below or you
can just search for inside code heaps on YouTube.
1040.16 -> And after building our priority queue, popping
the next element because of work out holy,
1045.2 -> so we just have to pay the cost of
building the priority queue, which is add.
1049.84 -> For example, with our array, this is what our
heap would look like, you can see that the element
1054.8 -> at the top is the greatest one who extracted
and it costs of work and to rearrange notes.
1061.36 -> second iteration will extract the root cause
of work and to rearrange to maintain the order.
1067.36 -> third iteration, same thing. Now that we do the k
minus one iterations, the next extracted node is
1073.92 -> the eighth largest element, we'll turn it here
it's six. In Python, we have the hip Q module
1081.04 -> with a problem is that it's implemented with a
min heap, not a max heap. So the top will find
1086 -> the smallest element not the greatest to counter
that will just multiply values by minus one to
1091.44 -> reverse the order. So we start by replacing our by
minus ln for each element in our after doing so,
1098.48 -> we heapify our to make it respect to heap
property. And now we really do start extracting,
1104.24 -> we extract from it k minus one times. After
the loop, we extract the last time and return
1110.72 -> the result multiplied by minus one, obviously,
to get the original number though was in our,
1116.32 -> for the time complexity, who have earned to build
a new array with reverse values, and to heapify
1121.68 -> it, go check the video to know why. And then
we have k minus one iterations, each iteration
1127.6 -> because of work and to extract after the loop,
we have all worked out to extract one more time.
1134.64 -> In total, we have two n plus k minus one times
log n plus log n, which is two n plus k log n,
1141.12 -> which gives a time complexity of of amples k
log n, which is a bit better than the one of
1145.84 -> the previous solution, because k can't exceed
add. For this solution, stop giving interesting
1152.56 -> time performance difference only when n is
huge and case small. Because when k is close
1157.2 -> to an off campus, k log n is close to O of n log
n, the time complexity of the previous solution.
1157.819 -> Pause When k is close to zero, O of n plus k log
n is close to O of n for the space complexity
1171.68 -> who have unfold the priority queue who got an
offer and space complexity. We reached the end
1178.24 -> of this video I hope that you understood the
three solutions and see what the next one will
1189.44 -> come back to the course in this lecture we will
solve the symmetric tree problem. We have a binary
1195.52 -> tree and we want to see if it's symmetric.
In other words, if it's a mirror of it Self.
1201.36 -> For example, this tree is symmetric, because
if we take his left part, reverse it, who that
1206.48 -> is right part. And vice versa, if we take its
right part, we reverse it who got his left part.
1215.12 -> But this one is not symmetric. For example, if
we reverse its left part, we don't get the right
1219.84 -> part. Okay how to solve this problem? To check
if a tree is symmetric, what we really need
1227.6 -> to check is that left and right sub trees are
symmetric to each other. Who will focus on that
1234.8 -> you need to know that for tree problems,
the solution is usually don't coercively
1239.12 -> we process the root, then we call the function
on both sub trees and we combine the results.
1244.64 -> For example, to get the sum of elements of a
binary tree, we have the value of the root,
1249.52 -> then recursively call the function on both
sub trees to get the sum of their elements.
1253.92 -> After doing so, we return root value plus some of
left subtree plus some of right subtree. This way
1260.48 -> of traversing is called depth first search, and
it's how we solved the majority of tree problems.
1266.8 -> Let's go back to our problem, we have two
trees, route one and route two and want
1271.2 -> to check if they're symmetric to each other.
First case, both trees are empty. In that case,
1276.96 -> we return true because they're still symmetric
to each other, there is nothing that breaks the
1280.88 -> condition. Second case, won't we exist,
but the other one doesn't. In that case,
1286.88 -> without let it use it and not symmetric
because the node of the tree that exists
1291.2 -> doesn't even exist in the other
one, it's empty, we return false.
1297.12 -> Third case, both trees exist, but the
roots don't have the same value. Here also,
1301.76 -> they're not symmetric, because in symmetric
trees, the roots must have the same value,
1306.64 -> because when we reverse the tree, the root
position doesn't change. And last case, both trees
1312.96 -> exist, and they have the same root value. In this
case, we still can say that they're symmetric,
1318.72 -> because Okay, the roots have the same value.
But we still need to check their sub trees,
1323.6 -> having the same root value is not
enough to serve their symmetric.
1328.48 -> And if we take two symmetric trees, we can
notice that they have the same root value,
1332.88 -> though the left subtree of the first one is
symmetric to the right subtree of the second one,
1337.04 -> and on the right subtree of the first one is
symmetric to the left subtree of the second one.
1343.84 -> We verified that root values are equal. So
we need to try the remaining two conditions.
1349.04 -> And how are we going to do so we're going to do
so recursively, because if you think about it,
1354.08 -> we're building a function that checks
if two binary trees are symmetric,
1357.92 -> which is exactly what we need. This
is why we'll use the same function.
1362.88 -> It may sound hard to understand,
but this is what recursion about
1366.16 -> a function calling itself. If you're not familiar
with recursion, you should really have a look at
1371.84 -> it before continuing this course because
we will use it again in a further problem.
1376.8 -> If you want to learn recursion, you come
to the course I made on the subject.
1380.8 -> It's a complete and well appreciated course
that will let you be comfortable with recursion.
1386.96 -> Let's go back to our problem. We saw that we use
the function we're making, we saw that we use the
1392.4 -> function we're making. We call it on route one
dot left and route two dot right we're called
1396.72 -> on route 1.1 and route dot left and we tell you
at both calls return true, it means that both
1402 -> conditions are verified, the left subtree of route
one is symmetric to the right subtree of route
1407.6 -> to other the rights of drove route one is
symmetric to the left of Jove route two.
1412.88 -> Let's quickly see an example. We want
to check if this tree is symmetric,
1417.52 -> so we check if it's sub trees
are symmetric to each other.
1422 -> They have the same root value, so we check the
left subtree of root one with white socks off root
1424.125 -> two, same root value, who took the left subtree
of root one with right subtree of root two, same
1432.32 -> root value, we check left and right, same root
value. Other children are both normal they will
1438.24 -> turn true. Same with value and symmetric children.
All conditions are respected, it returns true.
1445.76 -> Now right up to a fruit one with lots of to
fruit two, they have the same root value.
1450.72 -> Their children on both know both calls
return true. All conditions are respected.
1455.52 -> The caller turns true. Back to here, all
conditions are respected. The Colita is true.
1462.32 -> Now REITs offshore foot one with Latakia for
two, they don't have the same root value,
1467.28 -> the call returns false. Not all conditions
are respected. The call returns false.
1473.92 -> Who don't ever need to check the second call
because we already have a non respected condition.
1478.16 -> The call returns false. The initial call
return false so our tree is not symmetric.
1485.52 -> In code, let's first make our our symmetric
function. It takes two trees as parameter
1490.64 -> route one and route two. First case
both don't exist without Q two and true.
1496.32 -> Second case, one of them exists but the other
one doesn't. Third case, they have a different
1501.68 -> root value. In both of these cases, the
trees are not symmetric, so return false
1507.84 -> will write, if root one is non is not equal
to root two is non, or root one dot val is
1509.68 -> not equal to root two, that wall will
return false. And now in the last case,
1517.6 -> both trees exist and have the same root
value, we still need to Chuck's up trees,
1522.56 -> which are that would wander I'd love to see much
to root to the droid. And that root wonder tried
1523.92 -> is symmetric to root to the left, we do so by
recursively, calling the function twice. Once with
1532.56 -> root one dot left and root two dot right and once
with root one or twice and route two dot left,
1538.56 -> both of them need to return true. So we turn
the results combined with the Add operator.
1544.96 -> And now when our main solution function is
symmetric, we first check that the input
1549.2 -> tree exists because if it doesn't, we cannot
return true, an empty tree is symmetric. Else,
1555.52 -> whichever is sub trees are symmetric to
each other with the function who made Now
1559.6 -> we return our symmetric Rudolph left and rudos.
Right. That's it. For the time complexity,
1566.16 -> we're just performing a depth first
search traversal of the input tree
1569.52 -> and the first cause of anti m
where n is the number of nodes.
1574.72 -> And for the space complexity, a symmetric tree
has to be balanced. And the call stack size
1579.44 -> needed by a recursive function that traverses a
balanced binary tree is an awful look at who got
1584.64 -> an off walk and space complexity. That's it for
this lecture, we've seen an interesting problem
1590.32 -> on trees. I hope that you have been able to
understand the solution seen with the next lecture
1602.56 -> Welcome back to the course in this lecture, we
will solve the Generate parenthesis problem,
1607.44 -> a problem that we will solve using backtracking.
By the way, in this course, I try to include
1613.36 -> problems on different pattern spire research
hash table backtracking, depth, first search,
1619.44 -> and so on. But one problem and each is not enough.
This is why you need to study more problems.
1625.44 -> For that, I suggest you to have a look at my
50 popular coding interview problems course.
1630.88 -> It contains 50 Problems different from the ones
in this course, about many data structures and
1635.76 -> algorithmic techniques, you can have a look at
the curriculum and reviews on the main page.
1642.24 -> Anyway, let's go back to our problem. We
are given an integer n and we're asked
1646.64 -> to generate all valid combinations of N pairs of
parenthesis. For example, with an equal to three,
1652.8 -> here are all the valid combinations. First of
all, what does a vowel combination mean and how
1658.56 -> to check if a combination is valid. A combination
that contains one type of paranthesis is valid.
1664.4 -> If every opening parenthesis has its closing
parenthesis, and it doesn't have a closing
1669.28 -> parenthesis without having an end used opening
parenthesis before it. Let's see some examples.
1677.2 -> This combination is invalid because these opening
parenthesis don't have closing parenthesis
1682 -> the syntax is invalid. Second example
this combination is invalid because we
1687.2 -> have a closing parenthesis without an
end use opening parenthesis before it.
1692.08 -> Last example, this one is valid because each
opening parenthesis has its closing one,
1696.88 -> and there is no closing parenthesis
without an unused opening one before it.
1702.48 -> Now how to check if a combination is valid. To do
so we can maintain a stack where we push one we
1708 -> find an opening parenthesis and we pop one and
find the closing one. The condition is that we
1713.6 -> don't try to pop from the stack when it's empty,
or the stack has to be empty after we finish
1718 -> traversing the combination. Trying to perform
the stock one is empty means that we have a
1723.28 -> closing parenthesis without an available opening
one before it all ones what we found before have
1728.56 -> been popped by all the closing parentheses. And
the second condition is that the stock must be
1733.92 -> empty at the end. Because still having elements
in the stack after traversing means that we have
1738.88 -> opening parenthesis that didn't gather closing one
yet. In both cases, the combination is not valid.
1747.52 -> And because we have only one
type of parenthesis around once,
1751.12 -> we don't ever need the stock, who can do is use
a variable div that represents the difference
1755.68 -> between the number of opening parenthesis and the
number of closing once. It has to be zero the end
1762.32 -> and if it becomes negative during
the process, then it's not valid.
1767.12 -> Okay, but what's the relation with backtracking?
In this problem, we're not asked to check if a
1771.92 -> combination is valid. We're asked to generate
all valid combinations of N pairs. And we use
1778.16 -> backtracking because at each step of building the
combination, we have two possibilities, adding an
1783.92 -> opening parenthesis and adding a closing one. And
because we want all combinations, we try them both
1790.64 -> because we get new ones want to add an opening
one, and all the ones want to add a closing one.
1796.16 -> Also in backtracking. We can have a
condition where we backtrack With continuing,
1801.28 -> it's one we know that the actual branch won't
lead us to a valid solution. In our case,
1806.8 -> it's one death becomes negative, it means that
the combination we built until now isn't valid,
1812 -> it's useless to continue building it, we know
that it won't give us a valid combination. Anyway.
1818.32 -> Let's see an example. With n equal to
three, we'll get this recursion tree.
1824.56 -> If you're wondering why our sauce at six is
because the n given us input represents the
1829.2 -> number of pairs. And in n pairs, we have two
advantages. So we multiply by two because we're
1834.88 -> adding one parenthesis by level one, we go to
the left to add an opening one and increment div.
1840.16 -> And when we go to the right to add
a closing one, and decrement, if
1846 -> we can notice that we have branches
that have been stopped earlier,
1849.36 -> those are branches were diff becomes
negative. As soon as they've becomes negative,
1853.52 -> we stop also Evon with branches that created the
combination of N pairs, we don't take all of them,
1860.16 -> we take only those who are divisible to zero,
remember the validity condition. So at the end,
1866.48 -> here are the combinations that get
added to our combinations array.
1873.44 -> Encode who stopped by creating a recursive
function rack that will fill the combinations
1877.68 -> array, it takes us parameters and the
number of remaining parentheses to add,
1882.56 -> def the difference between opening and closing
brackets COMM The actual combination that we're
1887.92 -> building, and comps theory of combinations, the
one that we're searching for in our problem.
1896.56 -> The first base case is one difficult negative, hit
it with Dr. Batra Kodaka return to go back to the
1902.4 -> previous call. The second base case is one we've
been able to add all the parenthesis we've been
1908.08 -> able to build a combination. But in this case,
we don't automatically add it to our comps array,
1914.24 -> we first check if d f is equal to zero. Remember
the condition. So if f is zero, we draw the
1920.48 -> parenthesis of our combination to form a string
and we add it to our valid combinations array.
1926.72 -> Else, we have the recursive case, we
said that we have two possibilities,
1931.6 -> adding an opening parenthesis and adding a closing
one. Therefore, who will have two recursive calls.
1937.92 -> For the first one, we add an opening parenthesis
to our combination, then when we call and becomes
1943.68 -> n minus one because we have one less parent is
to add and this becomes the plus one because
1949.44 -> remember that to add one one will add an opening
parenthesis. After the call, we move the opening
1956 -> one we added to put a closing one. After doing
so we call the function again. But this time
1962.16 -> diff becomes diff minus one. Remember that we
subtract one when we add a closing parenthesis.
1969.12 -> And after the call, we remove the parenthesis
we added to backtrack to the previous call.
1974.16 -> And we made our function. By the way, we can ever
optimize a little bit by dotclear. Returning if
1981.52 -> div is greater than add because if is the case,
it means that we don't have enough remaining
1986.16 -> parenthesis to close all our opening parenthesis.
div is greater than n. So we just return.
1993.84 -> If you're confused about what's happening
here, have a look again other recursion tree.
1998.08 -> And most importantly, you should learn
more about recursion and backtracking.
2002.16 -> The method we're using here to
generate all possible combinations,
2005.44 -> it's common to a lot of problems, you
should really be comfortable with it.
2011.2 -> Now in the main solution function, we'll first
create an array comps where we will put our valid
2015.6 -> combinations and we call our QC function to fill
it. But the tricky part here is that we pass two
2022.08 -> times n as an argument not add, because the n
given as input represents the number of pairs,
2027.92 -> nor the number of parenthesis and a pair is
made of two parentheses. So we pass two times,
2034.48 -> our combinations will be of length two
times. After filling comps, we just return it
2041.44 -> for the time complexity. In the worst case, what
add a zero, we have a cost of an to join the
2046.64 -> parenthesis we write T of zero is equal to add
n in the recursive case, and again moving from
2053.28 -> the combination cost of one, but we have two
calls where the input size gets reduced by one
2058.48 -> and is becoming added minus one in total t of n
is equal to two times u of r minus one plus one.
2067.92 -> Now we keep replacing t of n is equal to two times
t of n minus one plus one. So T of N minus one is
2070.545 -> equal to two times t of n minus two plus one we
replace or simplify and we got four times two
2080.64 -> and minus two plus three. We replace again, t of
n minus two is two times two of Armani three plus
2086.88 -> one will replace will simplify and we get
eight times T of animal is three plus seven.
2094 -> This recurrence relation is a common one we can
already notice the drill form. It's t of n is it
2100 -> To power k times t of n minus k plus two power k
minus one, we have the value of T of zero, so we
2107.12 -> need to find the value of K to go to T of zero
and minus k equal to zero. So k is equal to n,
2114 -> where plus k by n, who got t of n is equal to two
power n times T of zero plus to power n minus one.
2121.28 -> We know that T of zero is earned workplace,
we got t of n is equal to two power n times
2127.04 -> n plus two power n minus one would give us
an O of n times two power n time complexity.
2133.92 -> Know that here and represents the length
of the combination, which is two times
2137.44 -> the number of pairs given us input. Also,
n times two power n is not the exact power,
2143.2 -> the number of operations will be less than that
do two branches where we backtrack earlier,
2148.08 -> O of n times two power n would be the exact
bound if we had no condition on our combinations.
2155.2 -> If you don't know the technique are used to find
the time complexity over this recursive function.
2159.52 -> It's called the substitution method, it's an
important technique to know you should learn
2163.92 -> about it. And for the space complexity, we have
n plus one for the cost like size, the length
2170.4 -> of the longest branch in the recursion tree. But
we also need to count the required space to store
2175.2 -> the combinations. The length of a combination
is at and it has two possible characters. So
2182 -> we have two power and possible combinations.
And the length of each one of them is n. So we
2187.84 -> need n times two power n space to solve them. We
got off on times to power and space complexity.
2194.88 -> But not all of them are valid, we will
need way less than n times to power n,
2199.6 -> n times two power n is just an upper bound. And
we all t the required space for combinations
2205.12 -> is n times the number of elements after
filling the comms array. We reached the end
2211.52 -> of this lecture, I hope that you understood this
backtracking solution and see you in the next one.
2224.4 -> Welcome back to the course in this lecture
rules or the gas station problem are given
2229.44 -> a circle a list of gas stations where we can
go from Station II to the station i plus one,
2234.8 -> and the last one goes back to the first one.
2238.16 -> And we are asked to find the index of the station
from where we start to be able to traverse all the
2242.72 -> stations and go back to the initial one without
running out of gas. Know that we can only move
2250 -> forward the gas tanks thought empty gas of i
represents the amount of gas at the station i
2255.68 -> cost of iron presents the cost to go from the
station out to the next one, the answer is
2260.08 -> guaranteed to be unique. And if the station we're
searching for doesn't exist return minus one we
2264.24 -> did use or there will be at most one station
for more we can traverse and be able to go back.
2272.08 -> For example, if we have these 10 stations, the
output is eight because when we start from station
2277.04 -> eight, we can go back to it without running out
of gas. We start with no gas, as mentioned the
2282.56 -> the problem, we add for gas of station eight and
we pay one to move to the next station, who add
2284.075 -> five gas of station line and we pay two to move
to the next station, we add one and pay five who
2291.92 -> add five and pay two, or three and pay two, or
three and pay eight, we add five and pay two,
2295.28 -> or three and pay for we add one and pay two, or
three and pay five, and we've been able to go back
2306 -> to station eight, you could see that the amount of
gas never became negative. Which is not the case
2312.56 -> for Office stations, for example with station one
who add five and pay two, or three and pay two,
2317.36 -> or three. And if you pay the eight to move to the
next station, the amount of gas becomes negative,
2324.64 -> which means that we can't continue, the
station we started from is not the right one.
2331.52 -> Let's solve this problem. A brute force solution
of a darkly comes in our mind is to simply
2336.72 -> simulate what happens with every station and we
find one that respects the condition who turn its
2342.4 -> index. Let's try it with our example. With the
first one, we add one with a five and the cost
2349.52 -> becomes negative Dr eliminated. Next one, we add
five and P two we have three and PE two, we have
2354.8 -> three and PE eight air the amount of remaining
gas became negative not this one. Next one, three
2362.8 -> and PE two we have three NP eight and remaining
became negative. Next one, we have three NP eight
2369.36 -> remaining became negative. Next one, we add five
and pay two or three and pay for we add one and
2375.84 -> pay two or three and pay five and remaining became
negative not this one. Next one, we add three and
2382.24 -> pay for negative. Next one, we add one and pay
to negative next one who has three and pay five
2388.96 -> negative next one who have plus four minus one
plus five minus two plus one minus five plus five
2396.48 -> minus two plus three minus two plus three minus
eight. was four minus two, plus three minus four,
2402.88 -> plus one minus two plus three minus five.
And we've been able to go back to the station
2407.36 -> from where we started without the alternate
because we know that the answer is unique.
2414.8 -> In code, we create a function that takes
us parameters, the array gas theory, cost,
2419.76 -> and index of the station form where we thought
the goal of this function is to tell us if we can
2425.44 -> finish the cycle by starting from the stations
thought, we first need a variable remaining
2431.12 -> that source the remaining amount of gas, we also
need the variable to store our actual position.
2436.64 -> Our initial position is the station FOMO,
we thought so initialize our add start.
2443.2 -> And we also need a boolean variable started
to know who started walking yet or not,
2448.08 -> we need this variable in our loop condition. We
need to keep looping until we go back to start,
2454.4 -> we write why isn't equal to start or not
started. Here not started is important,
2460.4 -> because if we don't use it, we won't even enter
the loop. Remember that I initialize a stock.
2466 -> So I installed our equal, we want them to
be equal. But after traversing the cycle,
2471.68 -> not now this is why we need the variable started
to know if we're in the case, where is equal to
2476.64 -> start, because we didn't start yet. Or because
we traverse a cycle, and I went back to start.
2483.36 -> Let's continue inside the loop we saw started to
true because we started then we update remaining.
2489.92 -> We saw that on the station i We are the amount of
gas in it, and we pay the cost to move to the next
2495.04 -> one. So we add gas of our answer drug costs of I.
After doing that, if we mainly becomes negative,
2502.64 -> it means that we couldn't go back to salt, we
don't have enough gas, we return false, else,
2508.48 -> we move to the next station. The next station is
usually i plus one. But we need to add modules
2514.72 -> the number of stations to handle the case were of
the last station i becomes i plus one add modules
2520.64 -> and it goes back to zero, we right i becomes
a plus one modules the number of stations.
2528 -> And if we finish the loop, it means that we've
been able to go back to start return true.
2534.8 -> Now in our main solution function, we just
tried the function we made on each station
2539.04 -> as we did in the example. And we turn the actual
station as soon as the function returns true.
2545.04 -> And if the function fails with all stations
will to n minus one for the time complexity,
2551.6 -> because the answer is unique. A
possible worst case is this one,
2555.84 -> because by starting from station zero, which
covers n stations before getting a negative amount
2560.96 -> from station one, which covers n minus one from
station two, which covers n minus two, and so on,
2567.36 -> we get the sum n plus n minus one plus n minus
two and so on until one. After simplifying,
2574.08 -> we found that it's an O of n squared, we'll
get an O of n squared time complexity,
2579.6 -> or the constant space complexity because
we're not using input size related variables
2586 -> of n squared is low. For this problem, we're
getting off n squared because for each station,
2590.8 -> we're traversing again, almost all the stations,
let's see how to optimize it. The main thing that
2596.88 -> you need to understand for the second solution
is that if we start from the station or the index
2601.52 -> thought and reach a negative amount of the station
I then all stations between Staten Island clauses
2607.68 -> are not valid. We don't need to try them with
Dr. John to Iboss one. Let me tell you why. We
2614.72 -> have the case where gas of thought is smaller
than cost of start. He the loop stops darkly
2619.44 -> because remaining becomes negative, it stops us
thought so there are no other stations involved.
2624.64 -> Nothing to talk about here. But when gusoff Start
is greater than or equal to cost of salt, the loop
2631.28 -> moves to all the stations, it can traverse a bunch
of stations before remaining becomes negative.
2637.04 -> For example, here when we start at station four,
remaining becomes negative at index seven. It
2642.16 -> means the stations 456 and seven are all invalid.
We'll start again from purpose one, eight. But why
2649.84 -> girls will start is greater than or equal to cost
of start. In our case, the difference is three.
2655.28 -> Every three is considered as an advantage over
starting from next stations is like a bonus by
2661.28 -> starting from station four. We have three more gas
and installing from the next one, station five.
2667.04 -> And even with that bonus, we didn't have enough
gas to do a full cycle. We stopped at station
2673.28 -> seven avenue that bonus we couldn't go over
station seven. So how do you want to go over
2678 -> it without a bonus? It's like you have $500 and
they aren't enough to buy a particle PC. Then you
2685.12 -> say this $500 weren't enough. I'll try with four
hundreds maybe they will be enough. It's a logical
2695.04 -> and this is why when remaining becomes negative.
We don't try again from the station that comes
2699.68 -> after The one where we started, who skipped all
the ones who traversed we started going from IPOs.
2705.52 -> With our example, who start from the beginning, we
may name becomes negative who's taught from IPOs
2710.72 -> one at five and we pay two remaining becomes three
who are three, we pay two remaining becomes four,
2717.68 -> who else we pay eight remaining becomes minus
one negative. What we're going to do now is to
2723.52 -> dock the start again from here, without
trying again, from stations to three,
2728.16 -> who add follow up to remaining becomes three, who
else we will pay for remaining become Sue, who
2733.92 -> add one who pay two remaining becomes one who
has free will by five remaining becomes minus one
2739.6 -> negative. Once again, without his thought, again,
from i plus one, we add four we pay one remaining
2746.64 -> becomes three who add four who pay two remaining
becomes six, and we finish traversing the array,
2752.88 -> the candidate is station eight. But it doesn't
mean that it can do a full cycle, it just means
2758.48 -> that we can reach the end of the array without
reaching a negative amount of gas. We didn't check
2763.2 -> the pot before it. This is why I said candidate.
It's a potential valve station. We don't know yet.
2770.56 -> To say that a candidate station
is valid by starting from their
2774.16 -> remaining Miss never become negative one
traversing the path from candidate to the end.
2778.8 -> But also when traversing the path from the
beginning of the array to candidate X clause.
2784.88 -> The first part of the cycle is what we verified
during the traversal. And for the second part of
2789.6 -> the cycle, we just calculated the sum of gas from
zero to candidate minus the sum of costs from zero
2795.04 -> to candidate, the result represents the remaining
gas after traversing that pot. And if by adding
2802.64 -> it to remaining the result stays positive, then
candidate is a valid station. Else we have no
2807.92 -> valid station. For example, here the sum of
gas from zero to candidate exclusive is 24.
2814.16 -> The sum of course from zero to candidate exclusive
is 30. The difference is minus six. What remained
2821.28 -> from candidate to the end is six what remains in
the pot before candidate is minus six by adding
2826.16 -> them together we get zero which is positive. So
candidate is the valid station. Question. What if
2832.64 -> we got a negative result here, then with Akutan
minus one because there is no valid stations.
2839.76 -> But what if the valid station comes after
the candidate, it's impossible. Once again,
2844.96 -> the station forma we started candidate
has an advantage over next stations,
2849.92 -> we know that gas of candidate is greater than
or equal to cost of candidate. So even if with
2854.8 -> that bonus, we get a negative remaining amount of
gas, it will also be the case for next stations.
2862.32 -> Let's jump to the code on the side all of
this remaining starts at zero. And same
2866.64 -> thing for candidate because at the beginning, we
assume that the first station is the candidate,
2871.36 -> the potential valley station. Now we start
traversing stations. For each station, we update
2878.16 -> remaining gas, we are guests of our answer truck
cost of AI. And if remaining becomes negative,
2884.72 -> we sell that to start again from APL small, it
becomes a new candidate, and remaining becomes
2889.92 -> zero because we will start again. After the loop,
we calculate the remaining gas of the pot before
2895.92 -> candidate the sum of gas minus the sum of costs
of the pot from zero to candidate X clause IV.
2902.88 -> Now we have three possible cases we have
the case or candidate is equal to and
2907.36 -> it means that we reached the end of the
array without finding a potential station,
2911.52 -> no station made it to the end
of the array, we return minus
2915.76 -> one. So in case we have a candidate, but when
adding prev remaining, we found the negative
2920.32 -> result, it means that it stopped somewhere
in the pot from zero to candidate return
2924.96 -> minus one. And third case, we have a candidate and
remaining plus prayer remaining is not negative.
2931.76 -> So candidate is the false station for more we
start to be able to do a full cycle, we return it.
2939.12 -> In code. If candidates is equal to
length of gas, the number of stations,
2943.6 -> or remaining prosperity remaining is smaller than
zero, we return minus one else we return candidate
2951.36 -> who can even keep track of remaining in the first
loop to avoid traversing again to calculate the
2956.16 -> sums. For the time complexity, we just have a loop
that does n iterations all other operations are in
2963.36 -> over one, we get an off and on complexity, where n
is the number of stations much better than O of n
2969.12 -> squared. Add a constant space complexity because
we're not using inputs all related variables.
2977.28 -> Basically, in the solution, we search
for the candidate the station that made
2980.88 -> it to the end of the array without
reaching a negative amount of gas.
2984.72 -> Then we check if it still boosts up
when traversing remaining stations,
2988.88 -> those in the pot from the beginning of the
array until going back to it who's the end
2994.08 -> of this video. I hope that you understood the
optimal solution and see what the next one
3006.24 -> Welcome back to the course in this lecture
rules of the course scheduled problem,
3010.88 -> we have n courses labeled from zero to n
minus one in clauses that we need to take.
3016.16 -> But some of them are prerequisites the other, we
cannot take a course before taking the other one.
3022.56 -> And we have to determine if it's possible to
finish all the courses. So we are given an integer
3028.4 -> as representing the number of courses and an
array prerequisites who are prerequisites of r is
3033.28 -> equal to a b means that you first need to take the
course P before taking the course a. For example,
3039.44 -> if we have this input, the output should be false.
Because to take course three, we must have taken
3043.354 -> course zero, and to take her zero must have taken
course one. And to take course one, we must have
3044 -> taken course three, it is impossible, it's
like we have a dependency cycle. This is
3055.44 -> why we cannot finish all the courses return
false. But with this input, the output is true,
3062.16 -> we can take or zero then course three, then
course one, then course five, then of course two,
3065.12 -> that course for each course will have its
prerequisite satisfied when taking it.
3072.72 -> How to solve this problem. Here we have
elements, courses and relationships between them,
3078 -> of course being approved was that of a novel
course. And every time we faced this situation,
3083.76 -> having elements and relationships between
them, you should think of using a graph.
3088.96 -> I'm not saying that it will always give the best
solution. But even if it doesn't, it will at least
3094.08 -> give you a way to visualize the problem with
vertices and links between them. In our case,
3100 -> the vertices represent courses and edges
represent dependencies and add from u to
3105.04 -> v means that we first need to take the course
you before being able to take the course fee.
3112.08 -> Okay, now we build our graph, we got a
directed graph what we will do with it.
3117.04 -> Our goal now is to search for dependency cycle.
If we find one, it means that it's impossible
3122.32 -> to finish all the courses return false. Else if we
don't find one at all, it means that it's possible
3128.56 -> to finish the mole return true. Basically, we're
searching for a cycle in a directed graph. In a
3136.8 -> linked list, a classic way to check if there is a
cycle is to traverse a linked lists while saving
3141.76 -> visited nodes. And if we step on a node that we
visited before, it means that there is a cycle.
3148.48 -> We can think of applying the same logic for graph
which covers with depth first search, for example,
3153.52 -> while using a set of visited notes. And if we step
on a node that has already been visited, it means
3158.88 -> that we have a cycle return false. By the way, if
you don't know about the full search, it's a way
3165.68 -> of traversing trees and graphs by diving deep into
a direction until we can move forward anymore.
3171.12 -> Go back, try and all the way and so on. I made a
huge video about the subject that you can watch.
3177.6 -> You need to know about DFS before continuing
this video is a prerequisite intended pun.
3184.88 -> But this strategy doesn't always
work. Here is a counter example.
3189.2 -> We have this graph let's start traversing. We
start for example from two we put it in visit it
3194.88 -> we move to zero we pull it in visit it will
move to three we pretend visit. This one has
3200.72 -> no outgoing others, we backtrack, this one has
no remaining neighbors to traverse we backtrack.
3207.04 -> From here we move to the second neighbor one we
put it in visit it will move to four we put it
3211.92 -> in visited. Then from here we move to three
and it's already in visited so our strategy
3216.96 -> would return false. Which is wrong because
we can totally finish all the courses here.
3222.24 -> We can start with two then zero then three than
one than four, all prerequisites are respected.
3230.112 -> Was the solution done? Well, let me
first talk about topological sort.
3235.04 -> We have a directed graph where vertices represent
tasks and an ad from u to v means that u is
3240.16 -> a prerequisite of V. topological sort is the
process of finding a linear ordering of vertices
3246.16 -> such that each vertex comes after is perquisites.
And all the words, for each edge from u to v,
3252.32 -> V must come after you in the ordering. For
example, for this graph here is a valid ordering.
3260.08 -> Know that a valid ordering is not always unique,
it's possible to find all the orderings that
3264.8 -> satisfy the condition. But the logical sort is
not always possible. It's not possible when the
3271.92 -> graph contains a cycle like this one. B is a
prerequisite of C, C is a prerequisite of D, D
3278.56 -> is a prerequisite of E and E is a prerequisite of
B, so we have no way to order them. Fortunately,
3285.2 -> while performing the topological sort, we can
detect the existence of a cycle. If it happens,
3290.24 -> we just stop and say that we cannot have a valid
ordering of vertices. And it's exactly what we
3296.56 -> want to do in our problem who have courses.
Some of them are prerequisites the other ones,
3301.52 -> and we want to know if we can take all the courses
and all the terms if a valid ordering exists.
3307.76 -> Therefore, who will use the biological source
and if during the process, we found the cycle
3312.32 -> return false. Otherwise return true because it
means that we've been able to make an ordering.
3319.12 -> Okay, but how does topological salt work? A
possible way to implement a biological source
3324.4 -> is depth first search. Let's, for example, soft
foam this vertex, we put it on the past stack,
3330.88 -> the pastor contains the vertices of the actual
path. B is its neighbor, we go to it, and we
3337.2 -> put it on the past stack is his neighbor, we go
to it, and we put it on the path stack. He is
3343.52 -> his neighbor, we go to it, and
we put it on the path stack.
3347.36 -> Now it has no neighbors, it means that
we can safely put it in our ordering,
3351.76 -> because there are no vertices that have to come
after it is perquisite of no one, we put it in the
3357.36 -> order stack and we backtrack to the previous node.
Next neighbor is I will go to it and we put it on
3364.8 -> the path stack. Same again, it has no neighbors,
who remove it from path stack and pull it
3370.32 -> on all the stack. And we backtrack. Next neighbor
is if we go to it, J is a neighbor, we go to it,
3378 -> it has no neighbors, we move it from path stack,
we put it on all their stack, and we backtrack.
3384.72 -> Next neighbor is all but it's already visited. It
has no more unvisited neighbors, so all vertices
3390.96 -> have to come after it in the ordering our visit,
we can safely put this one and we backtrack.
3397.44 -> This one also has no more unvisited neighbors,
we can put it in order staff, and we backtrack.
3404.08 -> And the process continues like that. Until
we traverse the whole graph of the end,
3409.36 -> we get this ordering, we reverse it together from
the beginning to the end. Now the opposite. encode
3417.12 -> our DFS function takes us parameters to graph
the vertex promo we start the path stock,
3422.88 -> the oldest stack and the set of visited notes. We
start by adding our vertex to the past, just when
3430.4 -> we start traversing it, then for each unvisited
neighbor, who first added to visit it, and we call
3436.32 -> DFS with it as an argument to traverse it. After
doing so for all the neighbors who can safely put
3442.48 -> the actual vertex on the oldest stack will pop
up from past stack and give it to all the stack.
3449.36 -> And in our main top sort function, it takes us
an argument the adjacency list of our graph,
3454.88 -> it squares the visit itself,
the Bastok, the older stack,
3458.72 -> then it visits each unvisited vertex
with DFS. After doing so, the other
3463.84 -> stack is filled with turns reverse together the
ordering from the first vertex to the last one.
3468.64 -> Now the opposite. And starting from this
algorithm, we can add instructions to detect cycle
3476.8 -> we find the cycle when you find a back arch,
and as that goes from vertex u to a vertex V,
3482.16 -> where V is already in the path stack. Like here
we have a B, C, D, E, then he has B as a neighbor,
3489.44 -> which is already on the path stack. This graph
contains a cycle B and E depends on each other.
3497.2 -> In Code, our DFS function will not be a Boolean
function. It returns if yes or no we've been
3502.56 -> able to make an ordering. What we add is that
before moving to a neighbor, we check if it's not
3508.24 -> already in the stack. If it's the case, without
a return false, we can't make a valid ordering.
3514.56 -> Also, if traversing enable returns false, it means
that we found a cycle one going from there. This
3520.64 -> is why if the recursive code turns false, will
also return false here. And if we've been able
3526.64 -> to traverse neighbors without getting a negative
result return true, we could make an ordering.
3533.2 -> Know that to optimize checking for the
existence of the actual vertex in the stack,
3537.2 -> it's better to use a set instead of a list
searching and assert is an old one time an average
3542.56 -> pass doc now is of type set at the beginning we
add our vertex to it after the Lupu move it out to
3548.88 -> add it to all the stack. And in our main solution
function, we first need to build the graph
3556.32 -> because input will have the number of courses and
the list of prerequisites. Now the adjacency list
3563.84 -> to build the adjacency list, we know
the number of vertices so we create
3567.44 -> an array of N empty arrays. Each one
will contain neighbors of the course I
3573.04 -> know which are first prerequisites of the problem
says that prerequisite of one has to come before
3578.56 -> prerequisite of zero. So create an ad from
prerequisite of one two prerequisite of zero.
3584 -> In other words, who add prerequisite of
02 neighbors have prerequisite of one.
3590.4 -> Now that we've built the adjacency list, we can
start traversing but before we create the visit
3595.76 -> itself, the past arc which is also a set on
the order stack, For each and visited course,
3602.08 -> who added to visit, and if calling DFS with it
returns false, it means that we couldn't build
3607.28 -> the old, we found the cycle. So return
false, who can't study all the courses
3613.6 -> out of the loop doesn't return false, it means
that we could find an ordering because all
3618.24 -> the order courses return true. And we've been able
to solve our problem. For the time complexity,
3626.48 -> we're just performing dub for search on a graph.
And the time complexity of DFS is often for v plus
3632 -> length of E, will length of v is the number of
vertices and length of E, the number of edges,
3638.64 -> ie the number of vertices is the number
of courses and, and the number of edges
3642.64 -> is M for length of prerequisites list, who
got an off campus and found complexity.
3649.12 -> And for the space complexity, we have the space
for the adjacency list length of v plus length
3653.84 -> of E, the space for the visited set length of v,
the space for the path stack, and the older stack
3659.6 -> length of v and the space for the call stack
length of v in total, who got full times length
3665.44 -> of v plus length of v, which is an awful length
of v plus length of v, who does have apples.
3672.8 -> Know that you may find a similar solution. But
with collars each vertex can have the color white,
3677.68 -> gray or black. Compared to our solution,
what means and visited. Green means visited
3683.76 -> and in the past dark, and black means
visited add not in the past anymore,
3688.32 -> we've put it in the order. And if
we found the neighbor that is great,
3692.96 -> it means that it's in the actual path, and we
found it again. So we have a cycle return false.
3702.08 -> No implemented topological sort with depth
first search. But we can also implement it
3706.4 -> with breadth first search. Let me show you how.
If you don't know about breadth first search,
3711.6 -> I made a YouTube video about it, I recommend
you to watch it before continuing this one.
3718.16 -> The general idea of our second solution
is to split our directed graph into levels
3723.04 -> and the first level, we have vertices that have no
prerequisites, we'll conduct this thought studying
3727.92 -> them. Once we finish, we can remove them from
the graph by removing them from the graph. So
3734.56 -> vertices won't have prerequisites anymore, because
their prerequisites were courses we just started
3739.6 -> and they're satisfied. And these vertices
represent the second level, we'll put them in
3745.2 -> our ordering and remove them after removing them,
so vertices won't have prerequisites anymore,
3752.72 -> they represent the third level, we put
them in our ordering and remove them.
3757.92 -> And the process continues like that
until we have no more vertices.
3764.08 -> Both removing vertices from graph costs on time,
because we need to update the adjacency list.
3769.6 -> Instead, we'll keep track of
the integral of each vertex
3773.04 -> and when we traverse a vertex, we decrement
the integral of all these neighbors,
3778 -> the in degree of a vertex is the number of edges
that are entering it. For example, the integral
3783.2 -> of this vertex is to so when we remove a vertex
that is going to it would decremented in degree
3790.88 -> and if the integral of a vertex becomes zero,
it feels at all its perquisites are satisfied,
3795.84 -> who can traverse it, we put it in the queue. With
his graph, we call the integral of each vertex,
3802.48 -> we search for vertices with an integral
of zero, we put them in the queue and you
3806.64 -> apply classic BFS traversal. The difference
is that when we pop a node from the queue,
3812 -> who start by putting it in the ordering array
because it has no remaining prerequisites,
3816.72 -> also want to traverse a neighbor with
decremented in degree, and if it becomes zero,
3821.76 -> we put it in the queue. Last thing would
not need a visited set, because we're not
3827.2 -> putting a vertex in the queue until we finish
traversing all vertices that are going to it.
3832.24 -> So we don't have the risk of traversing a vertex
again. With this graph, this is what happens.
3859.84 -> Encode will create an array of an
empty arrays as we did with DFS,
3864.16 -> but we'll also add an array of N zeroes
to solve the integral of each vertex.
3869.28 -> Now when filling the adjacency list will also
increment in degree of prerequisite of zero
3874.24 -> because prerequisite of zero is the vertex
where the edge enters, so incremented in degree
3880.88 -> now we'll create an array where
we'll put our ordering and a queue.
3884.56 -> The queue initially contains
vertices hoes in degrees zero,
3888.64 -> after it will start traversing while the cool
still contains elements will pop a vertex we
3894.16 -> add it to ordering because it has no unsatisfied
prerequisites, and we traverse its neighbors for
3899.6 -> each With different methods in degree, and if it
becomes zero, we incue it as explained earlier.
3906.48 -> After the loop, the other array is not filled, we
return it. This time, we don't need to reverse it
3911.6 -> because we started by putting vertices of level
zero than one and so on. With this topological
3918.24 -> sort algorithm, we can also detect cycles.
When the graph contains cycles. What happens
3923.44 -> is that at some point of the process, we will
have no more vertices with an index of zero,
3928.56 -> which means that nothing will be inserted in the
queue, the process stops earlier, the array will
3933.04 -> contain less than the total number of vertices,
who will deduce that the process couldn't continue
3938 -> because there is a cycle. And in our problem, who
just want to know if we can make an ordering or
3943.92 -> not, this is why after the loop, who will just
compare the length of the order array with add
3948.88 -> the number of courses either equal, the
algorithm returns true, else it returns false.
3956.16 -> So we keep the same code who just changed
the return statement with a length of order
3960.96 -> equal to add. For the time complexity,
we're just applying breadth first search,
3967.04 -> which has an off length of the plus length of
a time complexity O of n plus m in our problem.
3973.36 -> And for the space complexity, we have length
of v plus length of a for the adjacency list,
3978.24 -> length of v for the in degree array, and
length of v for the queue of the ordered array,
3983.2 -> three times length of v plus length of
V gives offline for v plus length of E,
3987.2 -> which is of m plus m and our problem,
we get an off campus M space complexity.
3994.48 -> We reached the end of this video in this
one, we solve the CO schedule problem
3998.08 -> with two different ways. I hope that you
understood them both and seen the next one.
4009.44 -> Welcome back to the course in this lecture, we
will solve the Kieth permutation problem with
4014.8 -> a range of numbers from one to n in clause
IV, who can make n factorial permutations.
4020.24 -> by labeling them in order starting from one,
you're asked to return the Keith permutation.
4026.08 -> For example, with n equal to three and
k equal to three, here are the three
4029.92 -> factorial permutations of 123 labeled animateur K
is three, so we turn the third one to one, three.
4038.88 -> The first solution that may come in your
mind is obviously the brute force solution,
4042.88 -> generating all the permutations doesn't return
the case one, we give to the function the range
4048.72 -> from one to n in closet, then we return the one at
index k minus one. Remember that an array is zero
4054.8 -> indexed, so the Keith one is at index k minus one.
But the problem with this solution is that with
4061.44 -> n elements, we can make n factorial permutations
of lung add that it costs n times n factorial to
4067.2 -> generate them, we get n or m times n factorial
time complexity Soulshine, which is extremely
4072.72 -> slow. We did use the tree to solve this problem
is to be able to find the permutation without
4079.36 -> generating the permutations. And this is what
we'll see now. Let's take n equal to four. Now
4086.72 -> take a paper or notepad software, list all the
permutations of 1234 in order and tell me what
4092.96 -> do we notice, I want you to take some minutes
to analyze the structure of those permutations.
4100.08 -> Here are all the permutations we have a
total of four factorial permutations 24.
4107.12 -> Who can notice that these 24 permutations can be
divided into four parts. The first one contains
4112.64 -> permutations that start with one, the second
one contains permutations that starts with two,
4117.52 -> and so on. Let's suppose that we want to
find a 16th permutation k equal to 16.
4125.44 -> If we label them starting from zero, it means
that we want to find the permutation 15.
4130.24 -> Because 16 is one we label them starting from
one. The question now is, can we find what part
4136.48 -> among these four parts of permutation 15 will
be? The answer is yes, by using math. We know
4143.6 -> that the first part contains permutations from
zero to five in clause, the second one from six
4148.4 -> to 11 in closet, the third one from 12 to 17, in
closet, and the last one from 18 to 23. In Closet.
4156.96 -> To find which one we'll just divide K by the
length of a part, which is six in our case.
4162.96 -> 15 divided by six gives two so the permutation 15
won't be in the pot zero won't be in the pot one,
4169.6 -> but it will be in the part two. Now that
we're working with zero index labeling,
4176.88 -> we know that the part two contains permutations
that starts with three. So we already know that
4181.28 -> our Keith permutation will start with a three
and it will be one of these permutations.
4187.68 -> Let's continue. What's gonna happen now
is that the search space will be reduced.
4192 -> Now we know that our case permutation
will be in these six permutations.
4196.16 -> We need to update variables. K needs to be more
Initially we were searching for permutation
4202.64 -> 15 Among all the permutations, but in this
group of six permutations, we're searching
4207.76 -> for permutation three, why three, because 15
and model six, the length of the pod is three.
4215.28 -> Case 15, when we start from permutation zero,
but in this particular part it will label
4220 -> permutation starting from zero, whose solution for
permutation three, and also represents the number
4227.04 -> of remaining elements of the permutation to find
and found one, so we decrement and becomes three
4234.64 -> and factorial also changes it becomes three
factorial six. Same thing for pot length,
4240.96 -> it also gets updated six divided by three is two.
We also remove three from a news elements because
4247.84 -> we already found its place in the permutation,
and our permutation doesn't contain duplicates.
4255.12 -> Who can divide these six permutations into
three parts of two elements, the first part
4259.68 -> contains permutations holds the second element
is one, the second part contains permutations
4264.64 -> hosts the second element is to other third parts
contains permutations who is the third element is
4271.2 -> 41214. All the elements or two we
didn't use it in our permutation.
4276.64 -> Case three now can we find the pot case
permutation belongs to yes once again we divide
4282.48 -> by the pot length, three divided by two gives
one so the case permutation will be in part one.
4289.12 -> And we know that permutations of
Part One have to our second element,
4293.2 -> so the second element of our permutation will
be to, again the search space will be reduced we
4299.76 -> know that our kid permutation will be in these
two permutations. This is why we update the
4304.88 -> variables k gets modified as we did previously,
it becomes k models the length of part three
4310.88 -> models two gives one it means that among these two
permutations, we're switching for permutation one,
4318.08 -> and gets decremented it becomes two which changes
the value of n factorial two factorial is two.
4323.92 -> Which also changes the value part length,
and factorial divided by n is two divided
4328.56 -> by two which is one. We can divide these two
permutations into two parts of one permutation,
4335.52 -> the first part contains permutations
holds the third element is one,
4339.04 -> and the second part contains permutations
holds the third element is for,
4344.08 -> to know the path or case permutation belongs to,
who divided by the port land, the path length here
4349.36 -> is one and one divided by one gives one, our
permutation will be in part one. permutations
4356.8 -> of part one how for as a third element, so the
third element of our permutation will be four.
4363.44 -> We do use the search space we know that
our permutation will be in this part of
4367.28 -> one permutation, we update variables, k becomes k
models the port length, one model is one zero, and
4374.32 -> becomes one and factorial becomes one pot length
becomes one and we move for from a news elements.
4381.68 -> We divide this one permutation
into one part of one permutation.
4385.36 -> The first part will contain permutations host,
one is the fourth element, the length of a part
4390.16 -> is one, and k divided by one gives zero. So our K
permutation will be in pod zero. And permutations
4398 -> of pod zero have one is fourth element. So the
fourth element of our permutation will be one
4406 -> now and becomes zero and it gets
decremented. So we'll finish the process.
4409.92 -> The case permutation is 3241, who found it
without generating all the permutations.
4419.04 -> If you didn't understand what we've been doing
here, let me show it to you in another way.
4424.48 -> You first need to understand
how our permutations generated
4427.84 -> are the beginning who have no
choices forming this example.
4431.44 -> And because we want all the permutations, we try
all possibilities. When we add one when we add
4436.8 -> 212 and three, add one, add four, this is why
you're seeing four branches are the beginning.
4443.76 -> And we're talking about permutations without
repetitions. So when we take an element,
4448.16 -> we remove it from possible choices. This is why
when we take two other beginning for example,
4453.52 -> lat branch creates three branches only not
411111, a three and one one, we add four,
4460.48 -> you can see that we didn't add two
again. And when we take four for example,
4465.28 -> we have two choices left one or
three, we get two new branches only.
4469.84 -> Then when we take one of them, we have
one choice left, the one we didn't take
4475.76 -> part in our problem we want to find the case one.
The way we solve this problem is by using math
4480.96 -> to calculate what branch we should go from
to reach the combination we searching for.
4488.8 -> Imagine that you have 1000 books labeled from
zero to 999. Let's say that you're searching
4494.48 -> for the book 436 You remember that these books
are organized into Turn boxes have 100 elements.
4502.16 -> The first one contains books from zero
to 99, the second one from 100, to 199,
4507.76 -> and so on how to know which book to search, and
you simply divide the number of the book you're
4513.12 -> searching for by the size of a box, because here
they have the same size. 436 divided by 100 is
4520.64 -> four, we searched in box four, we still didn't
find the book, but we reduce the search space.
4528.08 -> Now, we will search for book 36 In this box of
100. Why, because 436 modules 100, the pot length
4536.4 -> gives the receipts. Once again, the 100 books
of the books four are organized in 10 boxes of
4542.96 -> 10 books each, the first one contains books from
zero to nine after for hundreds, the second one
4548.16 -> from 10 to 19, after for hundreds, and so on.
The book size is 1036 divided by 10 gives three,
4555.84 -> we search in box three. In that box three, we
search for book six because 36 modulus 10 is six,
4564.16 -> so on and so on until we found the
book without traversing all the books.
4569.2 -> And basically this is what
we did to solve this problem.
4573.68 -> Let's try to generalize or the beginning we'll
have n k add in us that contains elements in the
4579.12 -> range from one to adding clause if now while n is
greater than zero, who stopped by calculating the
4584.8 -> port length, it's an factorial divided by n.
After it, we calculate the index of the next
4591.2 -> element to put in the permutation. Remember that
we calculated by dividing cable the port length,
4597.12 -> after we calculated we add an use of either
the permutation either deleted from unused
4603.52 -> who add an use of oil because it represents the
common element in the if part. Then we decrement
4610.32 -> n and k becomes K model a spot length to get
its new position in the path that we choose.
4618.16 -> who keep repeating that and when a becomes
zero, we have our case permutation and we
4623.04 -> solve the problem. In code, we have a function
that takes as parameters and key the given input
4630.4 -> or create an empty array for the permutation
we're searching for. Then you create and used,
4634.96 -> it contains elements of the run want to
add in clause if afterward to avoid all is
4640.72 -> recalculating our factorial recruiting the very
fact we're factoring it represents a factorial,
4646.24 -> it contains n plus one elements to have the
values from zero factorial to n factorial.
4652 -> To fill it, we know that zero factorial is one.
And for remaining elements, we just take the value
4657.52 -> of the previous cell and we multiply it by I, for
example, with five who got 1126 24 120. Now we
4667.68 -> decrement k, because k represents the position
of the permutation with one index labeling,
4673.04 -> but we're working with zero index labeling, okay
becomes k minus one to fit. We're working with
4679.68 -> zero index labeling. To simplify the math, we get
the right result when we divide and use modules.
4688.48 -> Who can start the loop now, while n is greater
than zero, Portland is an factorial factor of
4694.24 -> n divided by an avant the index of the
element to take is k divided by potluck.
4700.96 -> After getting the index of
the next element, which is I
4704.16 -> who are unused of our to our permutation, and
we delete the element at index i from N used
4710.56 -> before moving to the next permutation with
decrement N, and k becomes k models pot length.
4717.84 -> After the loop, our permutation is built, we just
join its elements into a string and return it
4723.44 -> will return the case permutation
as required by the problem.
4728.72 -> Basically, the solution will may now use this
math to keep calculating the part of the case
4732.88 -> permutation belongs to, because by knowing it, we
know what is the next element to take from unused
4738.56 -> and use, which is the array of elements we
didn't use yet. For the time complexity,
4744.32 -> creating a news cost and creating factory
costs are plus one and film get costs. And
4750.72 -> then the while loop gets repeated while and
is greater than zero. And Angus recommended
4754.96 -> that each iteration, so the number of iterations
is equal to the initial value of an inside it,
4761.44 -> all operations are in all one, except popping the
element I found and used can have the case where
4766.88 -> we need to shift all the elements to the left
and entered all the elements of the permutation
4773.36 -> were four n plus n squared plus one who got an O
of n squared time complexity, way better than O of
4779.12 -> n times n factorial of the first solution. And
for the space complexity, we have n for a news
4785.2 -> and permutation and n plus one for fact array, two
m plus one gives us an O of n space complexity.
4793.6 -> Where is the end of this video? I hope that
you at least understood the general idea of
4797.36 -> the second solution, which is dividing the
perimeter Asians into pause with a common
4801.52 -> element and calculating which part of the key
permutation belongs to see you the next lecture
4813.68 -> Welcome back to the course in this lecture, we
will solve the minimum window substring problem,
4819.04 -> we have two strings SN T, and we're asked to
find the shortest substring of us that contains
4824.48 -> all characters of T. If such a substring
doesn't exist, return the empty string.
4830.48 -> For example, if we have this input as output, we
get C E A, B, E, ba, is the shortest substring
4837.28 -> of our salt contains all characters of t, in all
the words, two A's, one B and one C. First of all,
4845.28 -> how can we know that a string contains all
characters of another one who can't know it by
4850.08 -> having the counter frequencies of each string,
the one we use in the valid anagram problem,
4856 -> if record is the counter of as one and for to the
counter of as two, for every distinct character of
4861.6 -> as two for one of CH must be greater than or equal
to for two of CH in other words, as one must have
4868.32 -> at least the number of occurrences of CH and
s to encode will write for each CH in for two.
4875.04 -> If rec one of CH is smaller than freq, two of
CH will return false after the loop return true.
4882.8 -> Knowing this, we cannot really think of a
solution, the brute force solution, which covers
4887.6 -> all possible substrings of us while keeping track
of the shortest wall that has all characters of t.
4893.44 -> In Code, who can add an early exit condition. If
t is longer than us, we can't find the substring
4899.04 -> that satisfies the condition, there isn't enough
characters without glue join the empty string.
4904.96 -> Same result when t is empty,
who can return the empty string,
4908.4 -> because technically, the empty string contains
all the characters of the empty string.
4914.32 -> Else who first create a counter for T to
know the frequency of each character in it,
4919.28 -> let's name it for t. We also need the string
shortest that represents the shortest valid
4924.88 -> substring that we found until now, it initially
has amples one characters to be sure that it gets
4930.08 -> updated as soon as we found the valid sub train.
Now we start traversing substrings of us to do so
4937.68 -> we start by traversing the length, because we will
start by traversing substrings of one character
4942.88 -> than substrings of two characters and so on.
This is why learn goes from one to adding clauses
4950.48 -> inside it, whichever starting positions, because
with the same length, we have the substring the
4955.673 -> source at index zero, the water source at index
one, and so on. But it doesn't go until the end
4963.04 -> to avoid going out of bounds when adding the
length I goes from zero to n minus length in
4969.96 -> clause if we have the starting position, which is
I will have the length of the actual substring.
4976.48 -> So we can extract the actual substring is the one
between I in clause 11 i plus length fix clauses.
4984.48 -> Now that we got the substring, we'll create a
counter for it. Let's name it for us, after it
4990.88 -> will give the actual substring replaces
the shortest one who found until now,
4995.2 -> for that two conditions, the actual
substring must contain all characters of t,
5000.56 -> we can't know it when the function we made and
it must be shorter than the actual shortest one.
5006.48 -> The length of the actual substring is length.
So if length is smaller than length of shortest
5011.04 -> will replace shortest becomes the substring.
After the loop who don't don't quote on shortest,
5018.32 -> because it's possible that we didn't find any
substring that contains all characters of T
5022.96 -> to know that we took the length of
shortest. If it's still n plus one,
5027.2 -> it means that it doesn't get updated, we return
the empty string as required by the problem,
5032.48 -> else return shortest. Basically, we return
shortest one is length is at most n.
5039.44 -> For the time complexity, let's say that
n and m all lengths of Sn T respectively,
5044.56 -> will have N for building for T and n plus one for
creating shortest. Then we have n iterations for
5050.8 -> the outer loop animal's life plus one iterations
for the inner loop. And inside it the cost of
5056.24 -> extracting the substring. And generating the
counter depends on length. We also have the
5061.68 -> cost of checking if it contains all characters,
which is because in the function which covers
5066.4 -> characters of reg T, their number is at most m we
get this sum which is equal to one divided by six
5073.6 -> times n times m plus one times three M plus two
and plus four was simplify. And in the worst case,
5080.08 -> T is shorter than s. So M is smaller than
n. We're going off and coupon complexity.
5086.32 -> And for the space complexity, we have amphiphilic
T amples, one for shortest and for sub n n for
5092.72 -> fracas we'll get three and Paul samples one,
which gives them off samples and space complexity.
5100.24 -> This solution is obviously very slow is
not the best one. This is why we will move
5104.48 -> to the second solution. Inside the inner
loop, we have a cost of length to extract
5110.4 -> a cost of length who generate the counter and cost
of arm to check if it contains all characters?
5116 -> What if we try to reduce this cost to over one?
To get a constant cost per in a loop iteration?
5122.4 -> Follow me well, how can we extract
the substring and old one? In reality,
5127.44 -> we will need to extract the substring and our
second solution, who will just work with the start
5132 -> and end indexes of the substring? Which are i and
i plus length, we already know them. Second thing,
5138.72 -> how can we generate the counter of the actual
substring and oh one, the trick is to not always
5144.16 -> build the counter again, who will use the one
of the previous substring. Let me show you how.
5151.36 -> Emergent that we have this
string, US ABC a D CB AC.
5158.24 -> And the first sell triangle five characters has
this counter two A's, one B, one C and one T.
5165.76 -> Starting from this counter, how can we get the
counter of the next substring of five characters,
5170.4 -> which is this one? To do. So we'll just increment
the number of occurrences of the character that
5175.52 -> went out of the window and increment the number
of occurrences of the character that went in
5180.08 -> the window. The one that went out is a it was
in the first substring, but not in the second
5186.32 -> with decrement fracks of A, it becomes more
than the one that went in is he it's in the
5193.28 -> second substring, but not in the first one
who increment for x of e, it becomes one.
5199.84 -> And we got the new countering old one because
no matter the length of the substring,
5204.32 -> we need to work on two characters, only the
one that went out and the one that went in,
5209.68 -> there will always be only two because
we're moving by one position at a time.
5215.52 -> Then for the next substring. Same logic, the one
that went out is B so with the command for X of B
5221.36 -> and the one that went NTC. So increment for x
of C. And the process continues like that for
5227.92 -> all sub strings of length five. The technique
we're using here is called sliding window.
5234.48 -> In our example, we were using a window of length
five. Then to move to the next substring we don't
5240.64 -> start again from the beginning, we just move the
window by performing the necessary changes. And we
5246.48 -> just focus on what went out of the window and what
entered not what's inside because it won't change.
5254.08 -> Last thing to optimize the cost of
checking if it contains all characters,
5258.64 -> the cost is actually M because we need to
traverse characters of rec T Mobile let me
5263.36 -> show you how to optimize it. In our example, T is
ABC a it has three distinct characters A, B and
5270.96 -> C. So we have three conditions to respect to say
that our substring contains all characters of t,
5277.12 -> the conditions are at least two occurrences
of a at least one occurrence of b, at least
5282.64 -> one occurrence of C. The idea to avoid always
traversing all the characters, which can cause
5288.56 -> um, is to keep track of how many conditions
are satisfied. And for the actual substring,
5294.64 -> the number of satisfied conditions is equal to the
length of reg T, which has a number of distinct
5299.36 -> characters of t, then the actual substring
contains all characters of T It's a valid one.
5306.4 -> Okay, but some characters are going out of
the window and some of them are accurate,
5310.56 -> which means that when moving the window, some
satisfied conditions can become unsatisfied,
5315.6 -> and vice versa. Some unsatisfied conditions
can become satisfied how to handle that.
5322.8 -> To do so, after incrementing the number of
occurrences of the counter that went in,
5327.6 -> if it's in fact T and frakkers of want
n becomes equal to FRAC to point n,
5332.16 -> it means that we didn't have enough occurrences
of the characters that went in. And now we do
5336.96 -> so a new condition is satisfied,
who increments satisfied.
5343.04 -> And before decrementing, the number of
occurrences of the character that went out
5347.2 -> if it's in fact T and for us of one out is equal
to for active one out, it means that we had enough
5352.72 -> occurrences of the characters that went out, but
after decrementing, we won't because for us of one
5358.4 -> out will be smaller than what's required. This
is why we Diekmann satisfied, a condition that
5363.68 -> was satisfied before is not satisfied anymore.
And we decrement for x of one out obviously.
5371.12 -> For example here we have these strings SN t
and we have this window of seven characters,
5376.4 -> the conditions are at least two occurrences
of a at least one occurrence of p
5381.36 -> at least one occurrence of C. Here we have one
occurrence of a two occurrences of B and one
5387.76 -> occurrence of C. Only two conditions are satisfied
not enough. Let's move the window. Now he wants
5394.96 -> out but we don't care because it's not a character
of t with the command for X but nothing happens.
5401.6 -> And a went in who increment for X of A
and it becomes equal to fracture of A,
5406.48 -> we satisfied a new condition, the number
of conditions is equal to length of rec t.
5411.76 -> So the substring is valid, it contains all
characters of reg T, all conditions are satisfied.
5419.6 -> We move the window, now see what's out of the
window, and is number of occurrences in the
5424.56 -> substring is equal to the number of occurrences
in t. So, when we decrement because it's one out,
5430.32 -> the condition becomes unsatisfied.
We don't have enough C's anymore.
5435.44 -> And the answer is the window. But we
don't care. It's not a character of t.
5440.64 -> Now the number of satisfied conditions is smaller
than what's required. This substring doesn't
5445.36 -> contain all characters of t. And the process
continues like that. Once again, no matter
5452.56 -> the length of the window, we're dealing with two
characters, only the one that went in and the one
5457.84 -> that went out. So checking if the actual substring
is valid because of one we've been able to
5464.88 -> optimize extracting the substring, generating the
counter and checking if the substring is valid.
5470.56 -> We've seen how to do all these in old one. But he
didn't see the code yet. Let's move to the code.
5478.16 -> We keep the same old exit condition, we
generate the counter of t. But now we don't
5482.96 -> save the shortest string itself. We just save it
started and position. Initially shortest sources
5489.76 -> a string of samples one characters, so initialize
thought and and zero and amples one respectively.
5496.88 -> Now we start traversing lengths. What we were
doing in the example is to update frogs according
5502.88 -> to characters that went in and out compared to
the previous position of the window. But to do so,
5508.56 -> we first did an initial window that's
also zero and hose the length is length
5513.04 -> arrow, traverse it normally to fill frogs
and count the number of satisfied conditions.
5519.28 -> We create fracks and recreate satisfied a
variable to keep track of the number of satisfied
5524.16 -> conditions. It initially starts at zero because
we didn't start traversing characters of as yet,
5530 -> we didn't satisfy any condition. Then we
traverse characters of the first window,
5536.88 -> for each character ch will increment flux of
ch. And if CH is a character of T and flux
5542.96 -> of CH becomes equal to react, you have ch now
the number of occurrences of CH became enough,
5548.48 -> a new condition is satisfied, who increments
satisfied. After processing the first window,
5554.88 -> we check if it's a valid substring before moving
to all the ones and if it should replace the
5559.52 -> actual shortest one. If satisfied is equal to
length of reg t, the number of conditions and
5565.76 -> length is smaller than the length of the actual
shortest substring, which is add minus thought
5570.96 -> replace, start an N becomes zero and length
respectively, the boundaries of the first window.
5578.48 -> Now we can start traversing all the
sub strings are the same length,
5582.48 -> I start at one and zero
because the first sub string,
5585.6 -> which is the first window is already
processed, we start from the second one.
5592.08 -> We want to work with the character that went out
of the window and the one that went in bought,
5596.96 -> where are them. The actual substring is between
indexes I in clauses when I post length exclusive,
5604.32 -> the character that went out comes just before
the window. So its index is i minus one.
5609.84 -> And the one that went in is the last character of
the window, its index is i plus length minus one,
5616.48 -> we can start working, the character that entered
the window is opposition ai plus length minus one.
5622.24 -> So increment for x of s of Ai plus length minus
one. Now if it's a character of T and its number
5628.48 -> of occurrences and the substring became equal to
the number in t, it means that the new condition
5633.2 -> is satisfied, we write if as of apples length
minus one in fact T and flux of hours of eyeballs
5639.44 -> length minus one is equal to freq T of s of i
plus length minus one, we increment satisfied.
5647.52 -> After it, we work with a character that went out
the one at index i minus one if it's a cocktail of
5654.08 -> T and S number of occurrences and the substring
is equal to its number of occurrences in t
5658.56 -> it means that a condition was satisfied but not
anymore, because we will decrement for x of s
5663.12 -> of n minus one. So it will become smaller than
freq T of s of i minus one not enough occurrences
5668.88 -> of x of i minus one anymore. We write if as of i
minus one in fact T and for x of s of i minus one
5676.08 -> equal to T of s of i minus one with the
cumin satisfied will also satisfied condition
5683.76 -> after it with the CaroMont
for x of s of i minus one.
5689.92 -> Now that we know the number of satisfied
conditions by the actual substring the one between
5694.56 -> i and i plus Lang who can check if it replaces the
actual shortest one, we write If satisfied equal
5700.8 -> to the length of reg T and length is smaller
than n minus thought, we replace thought and
5705.6 -> by i and i plus length respectively, we found a
shorter string that contains all characters of T.
5712.16 -> After the loop, soften and represent the
boundaries of the shortest substring,
5716.4 -> but only found one. If underline his thought
is greater than n, then we didn't update
5721.68 -> start and end, we turned the empty string
because we didn't find any valid substring.
5726.8 -> Else, we told the part of us between starting
clause event and X closet. What about the time
5735.2 -> complexity, we'll have time for building for T
n iterations for the outer loop. Then inside it,
5741.2 -> we have linked iterations for the first loop,
and one is length iterations for the second bond,
5746.16 -> and all operations inside the mind of
one plus n for returning the part of us
5752.56 -> length plus and minus length gives n and we
can ignore M, we've got an O of n squared
5757.2 -> time complexity, the space complexity doesn't
change. You can see that now we've been able to
5764.64 -> reduce the time complexity from F and Q to off
n squared isn't enough. No, we can do better.
5772.32 -> When using sliding window, we have the case where
the window size is fixed. For example, if we have
5777.28 -> an array of integers and are asked to find the
key consecutive elements with the greatest sum,
5782.08 -> we use a window of size k, we will move
it but we won't change its size because
5787.44 -> we know that the array we're searching for
is of length k. However, in our problem,
5793.44 -> we don't know the size of the shortest
substring that contains all characters.
5797.36 -> This is why we tried all possible lengths,
which are sub strings of length one
5801.76 -> than those of them do, and so on, which
resulted in an O of n squared time complexity.
5809.36 -> But we have another way of manipulating the
window, instead of sliding the window by
5813.52 -> one position. In other words extended from the
right by one and reduce it from the left by one,
5819.12 -> what we can do is to extend it from the right by
one, but reduce it from the left by a number of
5824.16 -> times that is between zero and the length of
the window. Depending on what we want to do.
5830.4 -> Let's apply to our problem.
5834.16 -> Theory of the strategy that we're going to use
is to find the shortest substring that answer the
5838.88 -> actual index of write that respects the condition,
which is containing all characters of tea.
5845.52 -> Obviously keeping track of shortest one among
the more the one that will be returned our via
5852.24 -> let's start for the first index, we find an A
but no condition is satisfied, we need to as
5858.96 -> next index, we find d, we don't care about it.
Next index we find c a condition is satisfied.
5866.48 -> Next index if we don't care. Next
index e we don't care. Next index B,
5873.36 -> the second condition is satisfied, but not all
of them. Next index e we don't care. Next index
5880.4 -> see we have two Cs now, better condition is still
missing. Next index, he won't care. Next index
5888.16 -> a, we now have two A's, which is
enough to satisfy the last condition.
5894 -> All conditions are satisfied now, it means that
the substring from left to right in clause is
5898.72 -> valid. But is it the shortest one that answer
the index, right? Not true. Because we may
5904.56 -> have characters that we don't care about by
characters that we don't care about, I mean
5909.92 -> characters that we can remove without breaking
the validity of the actual string. And these
5915.12 -> characters are characters that are not in TRL and
calculus of t that are in excess in our substring.
5922.08 -> For example, if we need to see is only and we have
five C's in our substring who can remove the first
5927.52 -> three wants to make the substring shorter while
satisfying the condition. Remember that our goal
5933.52 -> is to get the shortest valid substring. Who cannot
remove from the metal because characters of a
5940.48 -> substring must be ages. And we cannot remove from
the right because we're searching for the shortest
5945.6 -> valid substring that answer the actual position
of right. So we can only remove from the left
5951.68 -> will keep removing while the characters position
left is a character we don't care about.
5958.88 -> Here the character off left is a
it's in T and it's not an excess.
5963.04 -> We need two A's and we have exactly two. So
this one is important. We cannot remove it.
5969.04 -> We finish removing from the left. So the
shortest substring that answer right is this one
5974.24 -> is length is smaller than the actual shortest
one, we update. We move to the next position of
5980.08 -> Freud who find the be the one at left is still
important. So the shortest one is this one.
5985.76 -> But we don't update because it's not the global
shortest one. Next position, we find an E the
5992.16 -> counter left is still important. Next position we
find to be the character left is still important.
5999.6 -> Next addition, we found in a and here is where
things change, because the character at left
6005.28 -> is not important anymore. The reason behind it
is that we found another A, which means that
6010.24 -> the character is now in excess, we can remove
the first one without breaking the Validate
6016.96 -> he will move a and with the
command is number of occurrences.
6020.96 -> Let's see if we can continue removing D is not
an important character is not in T, we move it
6027.92 -> C is in T but we have more than enough
we have two C's while we need only one
6032.8 -> we move and decrement is number of occurrences
of is not empty. Remove is not empty, remove it.
6040.72 -> These in excess we remove it is not empty, we
remove it. C is in T and it's not in excess.
6048.24 -> So we stopped removing. We found out that the
shortest valid substring that antithyroid is c
6053.92 -> e a b e BA and it's like the seven smaller than
the length of the actual shortest one replace.
6061.84 -> Let's continue. We'll move to the next position of
right. By the way, if you're wondering why aren't
6068.08 -> we going back to the beginning going to increment
right is because we know that characters before
6072.8 -> the actual position of love aren't important.
We don't need the for the actual substring.
6080.4 -> Who found the D the cow top left is
still important and we don't update
6084.48 -> because the length is not smaller than seven.
6088.4 -> Next position who find half the characters
left is still important and we don't update.
6094.24 -> Next position we find the seat which means that
the character at left is not important anymore.
6099.52 -> The character sees in excess we can remove it is
not in T we also remove it is important because
6107.44 -> it's in T and it's not in excess we stopped
removing we get a B e b a DFC, which is not
6114.16 -> shorter than the actual shortest. We don't update
we continue with on D and A still important.
6121.44 -> Next position we find f is still important.
Next position, we find c an ACL important.
6128.96 -> Basically it will continue like this for the next
five positions. And there is no update because the
6133.92 -> substring is getting longer. We're not being able
to remove from the left and in the next position
6140.88 -> we find an eight so the one on the left is not
important anymore, we remove it. These in excess
6146.88 -> we remove it is not empty, we move it be still in
Excel. So remove it is not in Excel. So we stop
6156 -> the substring is a DFC, DFC BFC BA, but not short
enough to update. last index, we find d and a
6166.32 -> still important, we don't update and we finish
traversing as the global shortest substring that
6172.64 -> contains all characters of t is the one between
indexes seven and 13 and clauses, it c a b e BA,
6181.2 -> take some seconds to process what you've
seen until now before continuing the video.
6189.6 -> Let's move to the code. We keep the same URL
the exit condition we build for it as we did
6194.56 -> previously, we initialize thought and add on
zero and respectively. We initialize satisfied
6199.92 -> zero regret for x which is empty at the beginning.
And we add a variable left that represent the left
6205.52 -> boundary of the window here and started on and not
n plus one because now Andrew presents the last
6212 -> nfo the window is in closet. Also, you can see
that we created frogs and satisfied outside the
6218.08 -> loop because we won't traverse lengths as we did
in the previous solution. We don't need to do that
6223.68 -> will darkly traverse positions of right the right
boundary of the window for each position right
6230.32 -> we increment the number of occurrences of the
character obviously, an objective is satisfied a
6234.96 -> condition as we did in the previous solution if
as of right is in fact T and flux of arthritis
6240.56 -> equal to for active as a Freud who satisfy the
condition so increments satisfied. Basically the
6247.2 -> path before satisfying all the conditions. This
is what we need to do only incrementing the number
6252.48 -> of occurrences and checking if we satisfied a
condition remember that we're expanding from
6257.28 -> the right only less remains zero. But once we
satisfy all the conditions which we can verify
6265.28 -> with this condition, satisfied equal to length
of FRAC t we can start removing from the left.
6271.76 -> We don't know how many characters are we going
to remove from the window. So we use a while
6276.16 -> loop. We saw that we keep removing characters
while the character has left is an important
6282.32 -> and a character that is not important. means
that either is not a character of the origin
6287.12 -> access means we have more than what's required. We
write while as of last noted for akti or flux of
6293.76 -> us have left greater than for active as of left.
Inside the loop, we move the car out which means
6300.88 -> that we decrement its number of occurrences
in frogs and move left to the next position.
6306.48 -> Now that we removed all unnecessary characters
who go the shortest valid substring? That answer
6311.52 -> the actual position of right is the
one between left and rotting clause if
6317.04 -> what do we do now, we check if it
replaces the actual global shortest one.
6322.56 -> The length of the substring, we found now is
right minus left plus one plus one because
6327.44 -> this time the right boundary, which is right
is included in the window. And the length
6333.44 -> of the actual global shortest one is and minus
thought plus one plus one for the same reason.
6339.92 -> So if right minus left plus one is smaller
than and minus that plus one, we update,
6344.64 -> start and end become left and respectively.
After the loop, almost same return statement,
6351.04 -> we just add plus one because and is included in
the window. Now, if we could update at least once
6357.36 -> we turned the part of ASP video installed and
adding clause if else return the empty string.
6364.8 -> By the way, you can see all of this time, we
weren't checking if we lost a satisfied condition.
6369.52 -> Because we stopped removing as soon as we reached
the minimum required occurrences will never
6374 -> go below it. This is why when the counter had
left was important. We weren't removing at all,
6380.56 -> not like in the previous solution when we were
removing no matter what. For the time complexity,
6387.28 -> we have an for building for T and
the outer loop does n iterations
6392.16 -> is true that we have an inner while loop where
the tricky part is that its number of iterations
6396.96 -> doesn't exceed n in total. I repeat in total.
Why? Simply because at each iteration of the
6402.88 -> while loop, we're removing a character and we
have n characters. remaining operations are
6408.88 -> owned over one. So the cost of the outer loop
isn't over. And yes, oh if and only not more,
6415.76 -> who else pause and for extracting the output.
And in total, we have ample of ampersand,
6420.72 -> we'll give some of ample time
complexity better than O of n squared.
6426.64 -> And for the space complexity, same as the previous
solution of m plus m. And we've been able to solve
6433.6 -> the problem in off campus m time only by
using a bunch of interesting optimizations.
6439.84 -> One was a tough problem, I hope that you
understood how we built our optimal solution
6444.72 -> that you will be able to use it to solve all the
problems. We've reached the end of this video,
6450.16 -> it was a long one. But I tried to detail
each operation to let you understand what is
6454.8 -> each line of code doing and how the algorithm
works in general. See you in the next video.
6466.48 -> Welcome back to the course in this lecture, we
will solve the largest rectangle in histogram
6470.88 -> problem. We are given an array heights that
contains the height of each bar in the histogram.
6476 -> And we're asked to return the area of
the largest rectangle in the histogram.
6480.72 -> Know that each bar has a width of one. For
example, if we have this input here is the
6487.2 -> largest rectangle. It's area seven times five,
which gives 35. How can we solve this problem?
6494.56 -> Let's for example, take this bar, it has a
height of two. Now, can you tell me what's
6499.68 -> the largest rectangle of high two that passes
from this bar? Is this one. But how did we know?
6506.48 -> Let's start from that bar and keep expanding? Can
we expand to the left? Yes, three is greater? Can
6512.32 -> we expand? No, because we have no more bar from
the left? From the right now can we expand? Yes,
6518.56 -> four is greater? Can we expand? Yes, five is
greater than two. In other words, this bar is
6524 -> higher so our rectangle can pass from it. Can we
expand? Yes, seven is greed. Can we expand? Yes,
6530.4 -> six is greed. Can we expand? No, we can't expand
from here because one is smaller than two. So if
6536.64 -> we expand, the rectangle won't be fully included
in the histogram is not a valid rectangle.
6543.52 -> We expanded as much as possible from both sides,
we go the largest rectangle that passes the
6548.16 -> initial bar is this one, it has an area of 12 Two
times six, the height multiplied by the width.
6556.72 -> And we can apply this strategy on each bar
while keeping track of the greatest area
6561.12 -> of the end who will have found the greatest
area, because the largest rectangle necessarily
6566 -> passes from a bar that has the same height as
itself. The reason behind it is that if the
6571.52 -> rectangle is shorter than all the bars it passes
from, it means that it's not the largest one,
6576.24 -> you can still make it bigger by increasing its
height. And if it's taller than at least one bar
6581.92 -> learn is not fully included in the histogram.
It's not a valid rectangle. With this example,
6589.28 -> with the first bar we get this rectangle with
the second bar we get this rectangle and so on.
6599.44 -> The end The greatest area that we
find is 35. The area of this rectangle
6607.52 -> encode we initialize Max area to zero because
we didn't traverse any rectangle. Therefore,
6612.8 -> each bar I expand from both sides from each side,
we keep expanding while the bar is still higher or
6619.6 -> as high as our bar. So we stopped when we found
the shorter one, or when we have no more bars.
6627.44 -> Left thoughts are the actual position, then
while the previous bar exists and is at least
6631.68 -> as high as the actual bar, we expand, we
move left by one position to the left.
6637.6 -> Same logic with the right side but
by incrementing, right starts on I,
6641.92 -> then while the next part exists, and is at
least as high as the actual bar, we expand,
6646.56 -> we move right by one position to the right. While
right plus one smaller than N and height of right
6652.16 -> plus one greater than or equal to height of
AI, we increment y to take it to the right.
6658.88 -> Now that we know boundaries of our
rectangle, we need to calculate its area.
6663.52 -> The area of a rectangle is the height multiplied
by the width, the height here is the height of
6668.16 -> the initial bar height of i, and for the width is
the difference between left and right plus one,
6674.48 -> right minus left plus one plus one because both
left and right are included in the rectangle.
6681.84 -> Now we check if replace Max area becomes
the most between its actual value and the
6686.32 -> area of the actual rectangle heights of our
multiplied by right minus left postponed.
6692.32 -> After the loop, we just return max area.
6696.4 -> For the time complexity, we're traversing the N
bars, and for each bar when searching for left
6701.92 -> and right boundaries, who may traverse the whole
histogram. This is why we get an O of n squared
6706.96 -> time complexity. In the worst case, the worst
case happens when all bars have the same height.
6713.12 -> And for the space complexity, it's
constant, we're just using eight variables.
6719.52 -> This solution is slow. Let's move to the next one.
There are interesting properties to know about the
6725.84 -> shortest bar. The first one is that by starting
to make a rectangle from it, we can expand it
6731.12 -> until the boundaries of the histogram because
there are no shorter ball to stop the expansion.
6737.12 -> Second one, the rectangle starting from
any other ball can't pass through it,
6741.52 -> except if it has the same height, but
we get the same rectangle in that case.
6746 -> So the shortest part is like a wall, any other
rectangle will either be on his left side or on
6751.44 -> his right side. From these two information, we
can make a solution based on divide and conquer.
6758.24 -> We search for the shortest bar, and we have a
rectangle that traverses the whole histogram
6763.36 -> will also recursively search for the
largest rectangle in the left side,
6766.96 -> the largest rectangle in the right side, and
we take the maximum among these rectangles,
6772.08 -> it represents the largest rectangle in the
whole histogram. The reason behind it is
6777.76 -> that because our bar is the shortest one, we
can't have a taller rectangle that joins the
6782.56 -> left on the right side, a taller rectangle will
either be in the left part or in the right part.
6788.16 -> So we don't worry about missing the larger
rectangle, we can search in each side separately.
6794.48 -> We also consider the rectangle host the height
is the height of the shortest bar, the one
6798.72 -> that traverses the whole histogram, because
it's possible for it to be the largest one,
6803.84 -> even if it has the smallest height, but its
width can make it the most interesting rectangle.
6811.12 -> Encode recruiter recursive function that
takes us parameters the array of heights,
6815.44 -> but also low and high the boundaries of the part
of the histogram we're searching in right now. Or
6822.24 -> the beginning we search the whole histogram. So
low and high will be initialized to the index of
6827.12 -> the first and the last bar. The first base case is
where the actual part is empty. While low exceeds
6833.92 -> right, we just return zero, we have no rectangle.
Second base case one, we have only one bar,
6840.88 -> one low is equal to high. In those case,
we just returned the height of that bar.
6846.16 -> Else, we have the recursive case, we'll start by
calculating the position of the shortest bar in
6850.96 -> the actual part. For that, we calculated
the smallest height in the actual part
6856.16 -> between low and high in clause IV, then we
search for its index in the outro part again.
6862.88 -> Now that we have it, we recursively search for
the greatest area and the left and right sides.
6868.72 -> For the left part, we put postman minus one
as the right boundary. And for the right part,
6873.76 -> we put postman postman as the left boundary.
URL for the short put wide rectangle, its height
6879.76 -> is the minimum height we calculated before and
its width is the whole path we're searching in
6885.2 -> high minus low plus one. So we turn the maximum
between from left form right and mean eight times
6892.24 -> high minus low plus one. This one represents
the area of the short but while rectangle
6898.72 -> we also need a long question function to call
this one, the initial values of low and high
6903.2 -> are zero and n minus one, the index of
the first and last bar respectively.
6908.8 -> For the time complexity, in the best case,
the shortest bar is always in the middle.
6913.68 -> So in recursive cases, we divide the input size by
two, we have infosearch. And for the men, and two
6919.92 -> times t of n by two for cursive calls, we get t of
n is equal to two times t of n divided by two plus
6925.76 -> n. By using the master method, this recurrence
relation gives off n log n time complexity.
6933.36 -> But in the worst case, the shortest bar is always
the first or the last bar. For example, when
6938.64 -> it's the first one, from the love, the input size
becomes zero, we get to zero. But from the right,
6944.32 -> the input size becomes n minus one, we get to
have animals one plus n to search for the minimum,
6950.72 -> we got t of n is equal to t of n minus one plus n.
And if we keep replacing, this recurrence relation
6957.12 -> gives off n squared time complexity, same as
the previous solution is like we didn't often
6964 -> what's slowing down the process is finding the
minimum its causes and to search for the minimum
6969.04 -> in the run who are searching for. But we can
optimize this by using a segment tree for example,
6974.88 -> you can reduce the cost of searching for the
minimum and arrange to look at the recurrence
6979.92 -> relation becomes t of n is equal to t of n
minus one plus log n, we get off n log n plus
6986.56 -> n to build the segment tree, we've got an O of n
log n time complexity, but if n over n squared.
6994.96 -> But we still have two solutions to discover.
6999.2 -> The strategy we use in our first solution is dot
for each bar, we search for the next shorter bar
7004.64 -> from the left the next shorter bar from the right,
and we calculate the area of the rectangle bar
7010.56 -> always traversing bars again to search resulted
in an O of n squared time complexity. What if we
7016.48 -> can traverse the histogram only wants to find the
next shorter bar of each bar? Yes, it's possible
7022.24 -> by using a stack, we can maintain a stack and
in increasing order. For example, we have 2478,
7030.56 -> then we want to insert five, but it wouldn't
be increasing anymore. So we'll keep popping
7035.76 -> until we find a smaller element. And we push
it remains increasing. Then again, we'll have
7042.48 -> six 910 12/3. To insert eight will pop 12 will
pop done, we put mine and insert, and so on.
7056.72 -> And we can use this principle for our problem
will work with heights will maintain a stack and
7063.12 -> to follow the next shorter bar from the left of
the actual bar who keep popping into the top of
7067.68 -> the stack becomes shorter after it will also
push the actual bar for the next iterations.
7074.56 -> Let me show you how it works with our example.
We can add two bars of height minus one at
7080.24 -> extremities to avoid having an empty stack minus
one to be sure that they will be shorted at any
7085.6 -> bar or the beginning, the stock contains
the bar zero, and will start from bar one.
7092.4 -> The top of the stack is shorter, who already
found the next shorter is at index zero. And
7098.96 -> we'll push the actual bar. Next index, the top
of the stack is higher Ooh pop. Now it's smaller,
7105.84 -> the index of the next shorter is zero, and we'll
push the actual bar. Next bar top of the stack
7112 -> is shorter, we already found next shorter is
the bar index two and we push the actual one.
7119.04 -> Next bar, same thing, top of the stack
is shorter. Next bar same thing. Next
7124.96 -> bar who performs and we find it next bar, we
have to pop all of them except the last one
7131.76 -> because they are all higher. Next bar, the top
of the stack is all really short, its index is
7137.2 -> seven, next bar are really shorter. And it
continues like this for the whole histogram.
7159.2 -> Now we got the index of nav shorter
bar from the left of each bar.
7162.56 -> But we still need the one from the right. So we
can apply the same process but from the right.
7169.04 -> The Start Now initially contains the rightmost
bar, the one we added from there, who is the
7173.76 -> height is minus one. First of all from the right,
the top is already shorter, and we push. Next bar
7181.04 -> we put once and we find the index we also push
next bar already shorter. We write and we push.
7188.88 -> Next bar, same thing we write and we push.
Next bar same thing. Next bar, same thing.
7196.24 -> Next bar we pop twice, we write and we push
next bar, we put once we write, and we push,
7203.68 -> next bar, we pump thrice, we write, and we
push, and the process continues like that
7225.2 -> now for each bar, we have the index of the next
shorter bar from the left the index of the next
7230 -> shorter bar from the right, we can calculate the
largest rectangle that passes from the actual bar.
7235.84 -> For example, for this one, next
shorter bar from the left is zero.
7239.68 -> From the right, it's seven.
So we get this rectangle
7244.4 -> to get the largest one in the whole histogram,
whichever is largest rectangle of each bar,
7248.96 -> while keeping track of the global largest
one, as we did in the first solution,
7253.36 -> but we don't need to search again here. And we've
been able to solve the problem by traversing the
7259.36 -> histogram only thrice instead of n times, which
will reduce our time complexity to O of n.
7267.28 -> Encode who first are those two bars of height
minus one not extremities, then we create an array
7273.2 -> from left or from left of it represents the index
of the next shorter bar from the left of the bar,
7278.16 -> I will also create a stack that initially
contains the first bar, the one we added.
7285.6 -> Now for each bar, except once on extremities, who
keep popping, while the bar on top of the stack
7290.48 -> is higher, or as high as the actual bar, we write
wild heights of stock of minus one greater than or
7296.8 -> equal to heights of oil, we pump stock of minus
one represents the top element. After the loop,
7304.48 -> the next shorter bar from the left is on top of
the stack, we assign it to from left, I will also
7310.56 -> push the index of the actual bar i. We finished
filling from left, who will do the same thing from
7317.2 -> the right, acquired from right, we initialize the
stock by putting the last bar only and we start,
7324.48 -> now we traversed bars, but in the reverse order.
Remember that we start from the right and we move.
7331.92 -> Same logic for what's inside the loop.
But we assigned to from right of it now.
7337.04 -> After having both arrays filled, we can start
working to initialize Max area to zero, then for
7343.12 -> each bar, who took it who replaced Max area, Max
area becomes the maximum between its actual value
7349.44 -> and the area of the rectangle of the actual
bar. The height of the actual bar is height
7354.32 -> of AI. And the width is fomite of i minus from
left of i minus one minus one. Because the next
7360.72 -> shorter bars are not included in the rectangle,
we don't count their width. So the area is height
7367.28 -> of I times from right of i minus from left of
i minus one after the loop return max area.
7376.32 -> For the time complexity, we have n to add those
bars and integrate from left. And now for the loop
7383.146 -> is true that we have a nested while loop where
the total number of iterations won't exceed
7387.68 -> and because each bar is pushed and
popped only once. And we have an bars
7392.16 -> so the cost is N. Same logic for the process
from right, and we add on to search for Max area.
7399.84 -> We're getting off on time complexity. And for
the space complexity we have and for heights
7406.56 -> and for the stack, and for from left. And and for
form, right we're going off and space complexity.
7415.52 -> In this ocean, whichever is the histogram
thrice, but we have a solution where we
7419.76 -> traverse it only wants let's talk about it. Okay,
imagine that we have this bar as a first bar,
7426.4 -> we can still expand it from the right because
we didn't find the shorter one. The next bar
7431.2 -> is this one, you can still expand them both,
we found nothing that stops them. Third bar,
7437.12 -> same thing. Fourth bar, same thing, you can still
expand them all because the hearts are increasing.
7444.72 -> And we're putting them in a stock by the way.
Both for example, the next bar is this one,
7450.48 -> this bar will love the previous three bars
stop expanding because it's shorter than them.
7455.68 -> In other words, all bars that
are taller than it in the stack.
7461.04 -> And because now we know where these bars stop,
we can't calculate the largest rectangle, the
7466.32 -> actual bars at index four, so they stop at index
three. For this bar, here's the rectangle for the
7473.6 -> next one of the stack. Two is the rectangle.
And for the next one, here is the rectangle.
7479.92 -> We stopped poppin because the top of the
stack is shorter, so it can still expand.
7485.84 -> You can see that what we're doing is that we're
pushing our bars into a stack. Then when we found
7490.56 -> the next shorter bar from the right, we know
where they stop, we calculate their rectangle.
7497.12 -> Now we push this point we continue this bar
Higher, we have nothing to pop without clear
7502.4 -> push it. But for this one is shorter. So we pop
this one, and we calculate the rectangle. The
7509.76 -> top is still taller, we pop on we calculate the
rectangle, put weight with this bar, the largest
7516.32 -> rectangle that we can make is not this one is this
one. The reason behind this one result is that
7523.6 -> the index of a bar is not necessarily for Maurice
largest rectangles thoughts, in reality is largest
7526.64 -> rectangle thought form where the last
popped bar thought iterations thoughts.
7534.16 -> For example, for this one, remember that we have
three bars, this one was the last one, it means
7540 -> that the largest rectangle starts from here. This
is why when we push bar, we keep the same height,
7545.68 -> both as index we put the index where the
rectangle of the last popped bar starts.
7551.92 -> Let's work with a greater example, the
one we've been working with in this video,
7557.36 -> we can add those two bars of height minus
one extremities to avoid having an empty
7561.52 -> stack. First bar, the bar on top is shorter,
we totally put it on the stack. Next bar,
7568.96 -> the bar on top of the stack is taller. So we
found the end of its rectangle, its Imani spawn,
7574.72 -> the top of the stack is not small anymore, who
stopped poppin. But now when we push this bar,
7581.12 -> as high throughput to both as index, we put the
index of the last pop bar, which is one let's
7588.24 -> continue for is taller, we just push with
this index because we didn't pop anything.
7593.76 -> Next bar, same thing. Next bar, same thing.
Next bar we need to pop. And we found the end
7600.32 -> of this bar here is it's rectangle. And
when we put six as index, we put five.
7606.48 -> Next bar, we need to pop here is the largest
rectangle of the pop the bar is beginning is
7612.48 -> the index we pushed and is and is i minus one, we
can calculate the rectangle and see if it replaces
7618.8 -> top is still higher, we pop top is still
higher, we pop top is still higher, we pop,
7625.2 -> you can see that for each prompt bar, you
can easily find his largest rectangle,
7630.56 -> top is not higher anymore, we stop. And the
starting index of the last pop node is one.
7636.32 -> So we put an index of one when pushing the actual
bar. The reason behind it is that from the left,
7642 -> our bar can extend until the index one from
the road we don't know yet. Next bar higher
7649.84 -> without the push, next bar, same thing. And
the process continues like this until the end.
7665.68 -> After finishing, we found the largest
rectangle is this one, we turn this area.
7672.24 -> And we've been able to solve the problem
by traversing the histogram only once.
7678.48 -> In code, we start by adding those bars
of extremities will create Max area,
7683.12 -> we'll create a stack and we'll start traversing
bars. The stack initially contains the first bar,
7690.16 -> the one that we added. And you can see that now
we're storing both the index and the height.
7696.08 -> In the previous solution, we stole the
index only because we could calculate
7699.52 -> the heart from the hearts array. But
this time, the pushed index is not
7703.76 -> necessarily the one where the bar is in the
histogram. So we store both. And this time,
7709.92 -> we need to traverse the bar of the right
extremity to empty the stack to calculate
7714.24 -> rectangles of bars that are still in the stack.
So I start from one and goes until the last bar
7720.96 -> at each bar, I will need a variable start that
indicates for more the largest rectangle of
7725.68 -> the bar our thoughts. Remember, that is the
starting index of the last bar we popped.
7733.52 -> But we didn't start popping yet. So we set it
to I now we start poppin while the bar the top
7740.88 -> of the stack is higher or as high as the bar our
we bought, we put the index and the height into
7746.24 -> variables. Remember that we need to calculate
the largest rectangle of the pumped bar.
7751.6 -> It's hard is the pushed height. It's taught
index is the pushed index. And it's an index is
7757.04 -> our minus one. So its width is i minus one minus
top index plus one, which is I'm on a stop index.
7765.2 -> We check if it replaces Max area Max
area becomes max between its actual value
7770.08 -> and top height times I'm on his top index.
7774.32 -> And we're searching for the start index of
the last pop bar. So start receive stop index.
7780.88 -> After the loop stock moves the start index of
the last popped bar and the height of the actual
7785.84 -> bar is height of I we push start heights of AI and
after the outer loop, we're done Max area thoughts
7796.32 -> for the time complexity, once again the inner
while loop will do a Most alliterations in total,
7801.84 -> because each bar is pushed and popped only
once we've got an O of n time complexity,
7806.48 -> and for the space complexity off, and
because of the stack and height array.
7812 -> We've reached the end of this video. In this one,
we've seen different solutions to this problem.
7816.4 -> I hope that you understood them all. And
that was the last problem of this course.
7824.4 -> Congratulations for finishing this course. I hope
that you learned a lot of new things from it,
7829.04 -> and we'll be able to apply them in all
the situations. To learn even more. You
7833.68 -> can check the other courses I made, like the
50 popular coding interview problems course,
7838.64 -> check the links below. Also, please tell
me what you thought about this course.
7843.44 -> If it's good, if it's bad, what should be
improved, etc. See you in another occasion.
Source: https://www.youtube.com/watch?v=Peq4GCPNC5c