 Hello and welcome back. In this lecture, we are going to look at a C++ implementation of merge sort and we are also going to analyze its performance. Here is a quick recap of some of the relevant topics we have already studied. We have looked at the sorting problem in general. Specifically, we have looked at two sorting techniques selection sort and merge sort and for merge sort in the last lecture, we looked at the intuition behind merge sort. We saw that it was basically a divide and conquer strategy and the key step in merge sort is that of merging two sorted subarrays into one larger sorted array. In this lecture, we are going to look at a C++ implementation of merge sort and we are also going to analyze its performance. Specifically, we will count the number of basic steps that are needed to sort an array of size n by merge sort. So, let us quickly recall the basic idea behind merge sort. We are given an array which is unsorted and contains a certain number of integers. We first divide this into two subproblems of almost the same size. So, the original size was n, each of these two sizes is roughly n by 2. Then we solve each of these two subproblems. Basically, we sort the first half of the array and the second half of the array separately and after I have sorted them separately, I take these two sorted subarrays and I merge them together into one larger sorted array that contains all the elements of the original array. So, what were the key steps involved in merge sort? We basically divided an array of size n into two subarrays of roughly n by 2 size and of course, if n is odd then these two subarrays will not be exactly the same size but their sizes may differ by 1 and this was an easy step. Then we recursively sorted each subarray of size n by 2 using the same technique as the one that we used for sorting the original array and the terminal case of this recursion was arrays of size 1. In these cases, the arrays are already sorted. So, we do not need to recurse anymore and we also saw how to merge sorted subarrays each of size n by 2 and if you recall from the last lecture, we saw that this merging of sorted subarrays can actually be done quite efficiently by making a single pass over each sorted subarray. So, how might a C++ implementation of merge sort look like? Here is what a program might look like. So, we have the main function in which I have used an integer array A of size 100. So, I am going to be able to read at most 100 integers. Then I ask the user to give the count of integers that she wants to sort. I read in that number and then I do some input validation. Basically, I check whether the number of integers that the user has given us to sort is less than or equal to 0 or greater than 100. If so, we print an appropriate error message and return minus 1. Note that if the main function had executed as expected, I would have returned 0. Here, there was an unexpected execution because of invalid input. So, I return minus 1. So, the caller of the main function, which is the operating system in this case, can figure out from this return value whether something unexpected happened or not inside the function main. Now, after I have read and validated the inputs, I am basically going to ask the user to give all the integers to sort and I am going to read them in a simple for loop and I am going to read them in the array A. So, note that the elements are now going to be stored in A0 through An minus 1. After I have read the elements, I am going to call merge sort, pass the array A to it and say that the starting index for the elements to be sorted is 0 and there are a total of n elements. So, basically, I want to sort the elements A0 through An minus 1. And after that, we could have some other code which does something as yet unspecified with this sorted array and finally, we return 0. Now, what does the function merge sort look like? Here is how it might look like. So, let us first look at its precondition and post condition. As a precondition, I require that the starting index in the array should be less than the ending index and both must be within the array bounds of A. As a post condition, we are going to guarantee that the part of A between the indices start and n minus 1 will be sorted and in this particular case, since we are interested in sorting in decreasing order, so this will be sorted in decreasing order. Note that I have passed an integer array as a parameter here and if you recall from our lecture on selection sort, when I pass an integer array, basically inside this function, whenever I refer to the array A, I am really referring to the same array that the main function used when it called merge sort. This is not copy by value. This is not call by value anymore. So, what do I do inside the function merge sort? The first thing I do is to check for the termination case of the recursion. If the sub array of interest has exactly one element, then n would be equal to start plus 1. There is nothing else to do because an array of one element is already sorted, so I simply return. Note also that merge sort does not return any value, so its return type is void. Otherwise, if the sub array of interest has more than one elements, I first find out the mid index in the sub array of interest. So, this is simply start plus n over 2 and then I am going to recursively call merge sort twice once for the sub array of A starting at start ending at mid minus 1 and then for the sub array of A starting from mid and ending at n minus 1. Once these two calls return, I know that these two sub arrays, the part of A between start and mid minus 1 and the part of A between mid and n minus 1 are already sorted. After that, I simply need to merge these two sorted sub arrays to get the entire sorted array A. So, that is accomplished by calling this function merge sorted sub arrays, which takes the array A and the two sub arrays in there, the two sorted sub arrays in there are from A start to a mid minus 1 and a mid through a minus 1. And finally, since merge sort does not return any value, I simply return from here. So, this is the key step in merge sort. How do we merge two sorted sub arrays? Let us see how this function might look like. So, to merge two sorted sub arrays, our precondition is that the two sub arrays A start through a mid minus 1 and a mid through a n minus 1 are sorted in decreasing order. And the post condition that we want to guarantee is that the entire sub array from A start through a n minus 1 would be sorted in decreasing order. Now, inside merge sorted sub arrays, what do we do? We actually have two counters or running indices, one for the first sub array and the other for the second sub array. So, I will be the running index for the first sub array A start through a mid minus 1 and J will be the running index for the second sub array a mid through a n minus 1. I am also going to use an integer array called temp A of the same size as the array A, which is passed as a parameter to this function. In our case, the size was 100. And I am also going to use an integer variable called index, which is initialized to start. This temp A array is basically where the merged and sorted sub array is going to be temporarily stored. And later on, we will copy the merged and sorted sub array from this temp A back to A. This integer variable index basically tells us where the next element of the merged and sorted sub array should be stored in the array temp A. So, let us see how we might use them in doing this operation of merging sorted sub arrays. So, here is our central and important merging loop. In this loop, I am going to initialize the running index for the first sub array to start the running index for the second sub array to mid. And I am going to iterate in this loop as long as there are elements to examine in either of these two sub arrays. So, as long as either i is less than mid or J is less than n, I am going to iterate in this loop. Note, I have not incremented the values of i and j over here in the for loop. These will be incremented appropriately within the body of the for loop. Now, what do I do within the body of the for loop? I have to determine whether a i coming from the first sub array a start through mid minus 1 or a j coming from the second sub array a mid through a minus 1. I have to determine which of these two should appear next in the sorted order. And I have to update temp A index accordingly. And if I have basically copied a i to temp A index, I have to increment i. If I have copied a j to temp A index, I have to increment j. And of course, then I must increment index. So, the next time around, the next element to appear in the sorted order would be stored in the next position and temp A. Now, if I take a closer look at the merging loop, then there are two cases to consider in the merging loop. First is when none of the two sorted sub arrays have been fully seen yet. Basically, both i is less than mid as well as j is less than n. And the other cases when one of the two sub arrays is already fully seen. Now, this is the easier case when one of the two sub arrays is already fully seen. What do we do then? We simply copy elements from the other sub array to temp A. How do we implement this in C plus plus? Here is a simple piece of code to do that. Basically, we check if i is less than mid, then certainly the sub array from a start through a mid minus 1 has not been fully seen yet. But since I am in this else part and therefore, it cannot be the case that both the sub arrays have not been fully seen. Therefore, it must be the case that one of the sub arrays has been fully seen. But since i is less than mid, it must be the case that the other sub array, the second sub array, the sub array from a mid through a mid minus 1 is fully seen. So, in this case, I will simply copy a i to temp A index and increment i. The other case is when I have fully seen the first sub array a start through a mid minus 1 and therefore, I am simply going to copy elements from the second sub array to temp A. So, a j will be copied to temp A index and j will be incremented. What do we do? When none of the two sub arrays have been fully seen yet, we have to find out which of a i coming from the sub array a start through a mid minus 1 or a j coming from the sub array a mid through a and minus 1. Which of these two a i or a j should be the next sorted element and I must copy that element to temp A index and I must increment i or j appropriately. So, this is how we do it. We basically check if a j is greater than a i. If so, then I am going to copy a j into temp A index. Recall that we are trying to sort the array in decreasing order. So, whichever one is greater, I will copy that into temp A at the current position index. And because I have copied a j to temp A index, I must increment j. Similarly, if a j is not greater than a i, then I will copy a i to temp A index and I will increment i. So, overall, this is the whole story of the merging loop. And if you think back about it, it is actually not a very complicated piece of code, but it is doing something remarkable. It is taking two sorted sub arrays each of size roughly n by 2 and it is generating one merged and sorted array of size n by passing over each of these sorted sub arrays exactly once. As you would notice that i is only incremented, j is only incremented and which means that I am basically going over each of the sorted sub arrays only once from its starting index to its last index. After we have merged the two sorted sub arrays, we then need to copy the values from temp A to A at all the indices from start through n minus 1. So, this is done by a simple for loop over here, where I have reused the running index for the first sub array here to denote the index which I am copying from temp A to A, the value at which position I am copying from temp A to A. So, if you now think back about the basic steps that we required in merged sorted sub arrays, this is the key function. We were basically required to read two array elements, compare them, write one of these elements in temp A and also increment the indices. So, I can think of this as one basic step and I also required to copy an element from temp A to A when I was copying the entire array from temp A back to A. So, let us count how many basic steps would merge sorted sub arrays required if I started off with two sorted sub arrays each of size n by 2. Well, since I am iterating over each of these sorted sub arrays in a single pass, so at most n by 2 basic steps would be required to iterate over each one of them. This basically means that at most n by 2 plus n by 2 or n basic steps are required to get the merged sorted sub array in temp A and finally, of course, I have to copy temp A back to A, so n basic steps are required over there. So, overall at most two n basic steps are required if I feed two sorted sub arrays each of size n by 2 to merge sorted sub arrays. Now, how many basic steps would the whole of merge sort require? Let Tn denote the maximum count of basic steps to sort an array of size n by merge sort. If you look at how merge sort operates, it basically recursively calls itself twice with half the array sizes and then it merges the two sorted sub arrays. So, Tn should be equal to Tn over 2 to account for the first recursive call of merge sort plus another Tn over 2 to account for the second recursive call of merge sort and then plus 2n which is to account for the call to merge sorted sub arrays and of course, the termination case here is T1 whenever array of size 1, I need exactly one basic step and if you go ahead and solve this recurrence relation using standard techniques, you will find that Tn is roughly the ceiling of 2n log n of log n base 2 plus n. Now, compare the number of basic steps required in merge sort with that required in selection sort. So, I have sort of plotted it here for different values of array sizes from 10 through 100. The blue bars show how the number of basic steps increase in selection sort. The orange bars show how they increase for merge sort. Clearly, merge sort is a winner here compared to selection sort. So, merge sort is expected to be much faster than selection sort for large n. So, in summary, we looked at a C plus plus implementation of merge sort using recursion. We analyzed its performance by counting the number of basic steps required to sort an array of size n and clearly merge sort wins over selection sort for large n. Thank you.