









Study with the several resources on Docsity
Earn points by helping other students or get them with a premium plan
Prepare for your exams
Study with the several resources on Docsity
Earn points to download
Earn points by helping other students or get them with a premium plan
Introduction to Computer Science. Lectures of Programming Abstractions on Sorting. Prof. Zelenski - Stanford University
Typology: Study notes
1 / 16
This page cannot be seen from the preview
Don't miss anything!










ProgrammingAbstractions-Lecture
Instructor (Julie Zelenski) :Hey. Welcome to Wednesday. Things that have happened and we’re talking about sorting. We’ve got lots of sorting to talk about today. Some more sorting we’ll talk about on Friday. That will be covered in Chapter 7 there. And then things that are happening. Boggle is coming in this Friday, so hopefully you’re making good progress on Boggle. How many people just totally done with Boggle? All done, all behind you? Oh, that’s a very healthy number. Like to see that. I did put a note here about Boggle Wednesday. Boggle is due Friday and given our late day policy we actually count days class meets and since Monday’s a holiday that technically means if you were to handle Boggle late it is due on Wednesday.
Here’s something I also want to say while I say that. Don’t do it. It’s a bad idea. You need to prep for the mid-term. I’m not gonna hand out assignments on Friday. I’m gonna just try to clear your plate for getting your head together for the mid-term. I think if you were finishing a late Boggle in that period you’re really short changing yourself. The mid-term counts a lot more, kind of covers more ground, is more important in the big scheme of things than working on a late assignment. So do make your plans to get it done on Friday so that you can actually not have it hanging over your head when you’re trying to get ready for that mid-term next Tuesday evening. Terman Auditorium, which is a little hike away from here over there in the Terman Building, 7:00 to 9:00 p.m. will be the big party zone.
Today I put up the solution to the problems I gave you on Monday, so you have both of those to work on. I should encourage you not to look at them kind of in parallel. I think that’s one of the bad ideas to say. Look at the problem, look at the solution, and say yeah, that looks right. They are right, actually. I’ll tell you that. You don’t need to do that. What you need to do is say good, I have generated that and the best way to check whether you’ve generated it is not to say yeah, that looks like what I would have done is to do it, right? Do it on the paper without the solution around and then look at your answer relative to the solution and see if there’s something you can learn about how our solution, your solution aren’t exactly the same. There’s a lot of partial credits. It’s not like you need to be able to reproduce and it’s not like there’s only one correct answer, but, sort of, that practice is the best practice for the exam.
Getting ready to think about how to do it on paper. The other handout also very worthwhile reading. It’s kind of long, but it has a lot of information that the section leaders and students over time have kind of gathered their wisdom and tried to write down just about how to make sure you do well on the exam. I think it’s one of the more challenging aspects is to try to come up with the fair way to give an exam in a class that is largely experience and skill based like [inaudible] is that we can capture an assessment of how you’re doing, right? But in this funky format of being on paper and stuff. So definitely do read that handout for, sort of, our advice and thoughts on how to make sure that you do succeed in that endeavor. Anything administratively you want to ask or talk about? Question?
Student: So if you have a conflict with the mid-term and have all ready filled [inaudible] when will we hear back on that?
Instructor (Julie Zelenski) :So we’re gathering them all by Thursday and then we’re gonna try to do this big intersection. So either late Thursday or, sort of, Friday is when we’re kind of sort out all the possibilities and try to come up with a schedule that gets everybody taken care of. So you should hear by the end of this week you will know what’s going on. All right.
So I’m going to pick up on where I left off on Monday. And this was the code for the selection sort algorithm. And, in fact, I’m actually not gonna – I’m gonna be showing the code for each of the algorithms that I’m using here, but I’m gonna encourage you to not actually get really focused on the details of the code. This is actually one of those places where the code is an embodiment of the idea and the idea is the one that actually we’re interested in studying, which is the approach it takes to sorting, how it decides to get that data in sorted order, and then how that algorithm performs and the kind of details of where it’s a plus one and minus one less than – it’s actually a little bit less important for this particular topic.
So what I’m gonna show you actually is a little demo of this code in action that’s done using our graphics library just to show you what’s going on. And so this is the selection sort code that the outer loop of which selects the smallest of what remains from the position I to the end of the array and then swaps it into position after it’s done that finding. So this inner loop is basically just a min finder finding the minimum index element from I to the end. So I’m gonna watch this code step through. So you can see as it moves J down, which goes bopping by in green it’s actually updating this kind of min, the blue pointer there, that says, well, what’s the min I’m seeing so far?
Early on, it was 92, then it bumped down to 45, then it bumped down to 41, then 28, then 8 and finally got to the end and found nothing smaller so it says, okay, that must be where the min is. Let me move that guy to the very front. So exchanges those two guys, flopping another one back in there. Now, we know we have the very smallest one out of the way. It’s in the right spot. We have nothing more to do with that. There’s no changes that’ll need to be made to anything to the left of I and we do the same operation again. Select from the remaining n minus one elements, the minimum element. So watch the green, sort of, finger move down while the blue finger kind of stays behind on the smallest it’s seen so far. So it’s okay, so, well, then 15 is it. Swap that guy up into the second slot.
Do it again. Keep doing this, right? Working it’s way down and then, as you see, the array’s kind of going from the left to the right, the smallest most elements, right? Being picked off and selected and moved out and then leaving the larger ones kind of over here in a little bit of a jumbled mess to get sorted out in the later iterations of those loops. So there is select and sort in action, right? Doing its thing. Okay. Let me do another little demo with you because I have – there’s lots of ways to look at these things. I’m gonna do one that actually can do slightly bigger ones and so I’m gonna set this guy up and it’s
Finishing it up. So giving you a kind of a little memory for, sort of, what’s the action selection sort. The small amount of tones and the kind of sparse distance meeting shows that it’s doing a lot of work in between each of those moves, right? And then the fact that the tones kind of advance from low to high tells you that it’s working on the smaller values up to the larger values. Kind of working it’s way to the end and that speeding up it closing in on the remaining ones as it gets to the end.
Left that around because even if you are can see it you can also hear it and that may help something. What we’re gonna look at is how much work does it do? So if I go back and look at the code just to refresh what’s going on, right? This inner loop is doing the comparisons all the way to find the min elements and so this is gonna iterate n minus one times on the first iteration, then n minus two, then n minus three, and all the way down those final iterations, three to look at, two to look at, one to look at and then one swap outside that. So what I actually have here is the sum of the values n minus one compares, plus one swap, but as two comparisons one swap, so one just swaps but actually the terms I’m looking at here are the number of comparisons the sum of the numbers one to n minus one is what we’re trying to compute here.
Then if you’ve seen this some are the [inaudible], some are the Gaussian, and some you may all ready know how to solve it, but I just kind of showed you how to do the math to work it out, which is the term you’re looking for is this sum. If you add it to itself, but rearrange the sequence of the terms so that they cancel each other out you’ll see that the n minus one plus one gives you an n and that what you end up with is n minus one n’s being added together is what the sum of this sequence against itself is and so we can divide by two to get the answer we’re looking for, so we have a one-half n squared minus n term. Which in the big O world, right? Just comes down to n squared.
So tell this – it’s a quadratic sort, right? That we would expect that if it took a certain amount of time to do a hundred, three seconds, then if we double that input we expect it to take four times as long. So if it was three seconds before it’s 12 seconds now. So growing as a parabola is a fairly sharp steep curve to get through things. All right. So let me give you an alternative algorithm to this. Just to kind of think, that’s gonna be the theme is, well, that’s one way to do it and that has certain properties. Let’s look at another way and then maybe we’ll have some opportunity to compare and contrast these two different approaches to see what kind of tradeoffs they make in terms of algorithm choices.
So the way you might handle a deck of a cards – sorting a deck of cards. So if I’m handing out cards to people you’re getting each card in turn that one way people do that is they pick up the first card and they kind of – you assume it’s trivially sorted, right? It’s in the right place. You pick up the next card and you decide, well, where does it go relative to the one you just had. Maybe you’re just sorting by number order and you say, well, okay, it’s greater than it and goes on this side. You pick up your next one and it’s like it goes in between them. And so you’re inserting each new element into the position of the ones that are all ready sorted.
So if you imagine applying that same thing in terms of computer talk in a vector sense is you could imagine kind of taking the vector, assuming that the first element is sorted, then looking at the second one and deciding, well, where does it go relative to the ones all ready sorted? The ones to my left and kind of moving it into position, shuffling over to open up that space that’s gonna go into and then just extending that as you further and further down the vector. Taking each subsequent element and inserting it into the ones to its left to make a sorted array kind of grow from the left side. So it grows from the left somewhat similar to selection sort, but actually it will look a little bit different based on how it’s doing its strategy here.
So let me give you the insertion sort code. So my outer loop is looking at the element at index one to the element of the final index of the raise side minus one and it copies that into this variable current to work with and then this inner loop is doing a down to loop, so it’s actually backing up from the position J over starting from where you are to say, well, where does this – it keeps sliding this one down until it’s fallen into the right place. So we’ll move over the 92, in this case, to make space for the 45 and then that’s kind of the whole iteration on that first time. The next time we have to look at 67. Well, 67’s definitely gonna go past 92, but not past 45. 41 is gonna need to go three full iterations to slide all the way down to the front. 74 is just gonna go over one number. Just needs to slide past one.
So on different iterations, right? A little different amount of work is being done, right? This loop terminates when the number has been slotted into position. We won’t know in advance how far it needs to go, but we go all the way to the end if necessary, but then kind of sliding each one up by one as we go down. So that one moved all the way to the front, 87 just has one to go, eights got a long way to go. All the way down to the very front there. Sixty-seven goes and stops right there, 15 almost to the bottom, and then 59 moving down this way. So kind of immediately you get the sense it’s actually doing something different than selection sort, just visually, right? You’re seeing a lot more movement for a start that elements are getting kind of shuffled over and making that space so it’s definitely making a different algorithmic choice in terms of comparison versus moving than selection sort did, which is kind of a lot of looking and then a little bit of moving.
It’s doing kind of the moving and the looking in tandem here. If I want to hear how it sounds, because nothing’s complete without knowing how it sounds, I can go back over here and let me turn it down a little bit. So you hear a lot more work in terms of move, right? Because of the signal I’ll speed it up just a little bit. So, as you can see, a kind of big [inaudible] a lot of work [inaudible] as it slides that thing down into position and then finding it’s home. Some will have come a long distance to travel like that last one. Other ones are very quick where they don’t have so far to go in turn. I think that’s it. I’ll crank it up to like a, sort of, a bigger number, let’s say, and turn off the sound and just let it go and you can kind of get a sense of what it looks like.
It seems to move very, very quickly at the beginning and then kind of starts to slow down towards the end. If I compare that to my friend the selection sort, but it looks like
case, each element is swapped so there’s actually two moves. One in, one out. So it does basically a number of moves that’s linear relative to the number of elements.
The insertion sort though is making it, sort of, a different tradeoff. It’s doing a move and a compare for most elements, right? In tandem and then that last compare doesn’t do a move with it. So there should be roughly tracking in the same thing. But they look closer to n squared over four. Showing that kind of one-half expected being thrown in there. But in the total, the idea is that it does about 100,000 compares a move. This one does about 100,000 compares that when you look at them in real time they tend to be very, very close neck in neck on most things. So if I do this again giving it another big chunk and just let it go, again, it looks like insertion sorts off to that early lead, but if you were a betting person you might be putting your money on insertion sort. But selection sort is the ultimate comeback kid and toward the end it just really gets a second wind.
In this case, beat it by a little bit bigger fraction this time. If I put the data into partially sorted order. So now, in this case, I’ve got about half of the data all ready where it’s supposed to be and another half that’s been randomly permuted and now let it go. Insertion sort takes an even faster looking early lead and selective sort, in this case, never notices that the data was actually in sorted order. But time actually is a little bit artificial here and, in fact, the number of comparisons is maybe a better number to use here, but it really did a lot more work relative to what insertion sort did because insertion sort was more able to recognize the sortedness of the data and take advantage of it in a way that selection sort totally just continued doing the same amount of work always.
That’s kind of interesting to note, right? Is that when we’re talking about all these different sorting algorithms, right? That we have multiple evidence, because actually they really do make different tradeoffs in terms of where the work is done and what operation it prefers and what inputs it actually performs well or poorly on. That selection sort is good for it does exactly the same amount of work no matter what. If it’s in sorted order, or reversal order, or in random order, right? It always is guaranteed to do the kind of same amount of work and you don’t have any unpredictability in it. That’s both an advantage and a disadvantage, right? On the one hand it says, well, if you knew that you needed this sort to take exactly this time and no better, no worse would be fine then actually having that reliable performer may be useful to know.
On the other hand, it’s interesting to know that insertion sort if you gave it to data that was almost sorted then it would do less work, right? Is an appealing characteristic of that. And so having there be some opportunity for it to perform more efficiently is nice. The other thing, also, is about this mix of operations. Whether it considers comparisons or moves are more expensive operation. For certain types of data those really aren’t one-to- one. That a comparison may be a very cheap operation and a move may be expensive or vice versa depending on kind of the data that’s being looked at.
For example, comparing strings is often a bit more expensive than comparing numbers because comparing strings has to look at letters to determine when they distinguish. If you had a lot of letters in the front that were overlapping it takes, sort of, more work to
distinguish at what point they divide and which one goes forward. On the other hand, moving a large data structure if it were a big structure of student information, right? Takes more time than moving an integer around. So depending on what the data that’s being looked at, there may actually be a real reason to prefer using more comparisons versus moves. And so examples I often give for this are like if you think about in the real world, if you were in charge of sorting something very heavy and awkward like refrigerators you kind of line up the refrigerators by price in the warehouse or something.
You would probably want to do something that did fewer moves, right? Moves are expensive. I’m picking up this refrigerator and I’m moving, I don’t want to move it more than once, right? And so you might want to go with the selection sort where you go and you figure out who’s the cheapest fridge, let me pull that one and get it over here, and now I’m not gonna touch it again rather than kind of sitting there with your insertion sort and moving the fridges one by one until you got it to the right spot. But if I were in charge of finding out who was, let’s say, the fastest runner in the mile in this class you probably would not enjoy it if my strategy were be to take two of you in a dead heat and say run a mile and see who won. And now whoever won, okay, well, you get to run against the next guy. And if you win again you get to run against the next guy.
Like you might just say hey, how about you let me get ahead of that person if I beat them once. I don’t want to have to go through this again. Like fewer comparisons would certainly be preferred. So both of these, I would say, are pretty easy algorithms to write. That is certainly the strength of selection and insertion sort. They are quadratic algorithms which we’re gonna see is not very tractable for large inputs, but the fact that you can write the code in eight lines and debug it quickly and get it working is actually a real advantage to the – so if you were in a situation where you just needed a quick and dirty sort these are probably the ones you’re gonna turn to.
So just some numbers on them, right? Is 10,000 elements on my machine kind of an unoptimized contact was taking three seconds. You go up by a factor of two. We expected to go up by about a corresponding factor of four and the time it roughly did. Going up by another factor of two and a half again going up. By the time you get to 100,000 though, a selection sort is slow enough to really be noticeable. It’s taking several minutes to do that kind of data and that means if you’re really trying to sort something of sufficiently large magnitude a quadratic sort, like insertion sort or selection sort, probably won’t cut it.
So here’s an insight we’re gonna kind of turn on its head. If you double the size of the input it takes four times as long. Okay. I can buy that, right? I went from ten to 20 and went up by a factor of four. So going in that direction it feels like this growth is really working against you, right? It is very quickly taking more and more time. Let’s try to take this idea though and kind of turn it around and see if we can actually capitalize on the fact that if I have the size of the input it should take one quarter the amount of time. So if I had a data set of 100,000 elements and it was gonna take me five minutes if I try to sort it in one batch. If I divided it into two-50,000 element batches it will take just a little over a minute to do each of them.
Student: When you merge the halves are you taking the higher one in the alphabet or the lower one?
Instructor (Julie Zelenski) :Well, it typically I’m taking it in the order I’m trying to sort them into, right? Which is increasing. So I’ll typically start at A’s and work my way to Z’s, right? So it turns out it’s completely –
Student: It doesn’t really matter.
Instructor (Julie Zelenski) :It doesn’t really matter is the truth, but the – whatever order they’re sorted in is the order I’m trying to output them in. So, in fact, these are the – if they’re first on the sorted piles then they’ll be first on the alphabet pile. So whatever that first is. So I’m gonna actually go – look at the code for a second before we come back and do the diagramming. We’ll go over here and look at the stepping part of it. So this is the code that’s doing merge sort and so it has a very recursive flavor to it. It does a little calculation here at the beginning to decide how many elements are going to the left and going to the right.
As I said, it does no smart division here. So this is called an easy split hard join. So the split process is very dumb. It says figure out how many elements there are, divide that in half, take the one from zero to the n over two and put them in one separate subvector and take the ones from n over two to the n and put them in a second subvector and then recursively sort those. So let’s watch what happens here. Computes that and says, okay, copy a subvector of the first, in this case four elements, copy a subvector that has the second four elements, the second half. So I’ve got my left and my right half and then it goes ahead and makes a call under merge sort, which says merge that left half and merge that right half and then we’ll see the merge together.
So I’m gonna watch it go down because the thing that’s gonna happen is when I do a merge of this half it’s kind of like it postponed these other calls and it comes back in and it makes another call, which does another division into two halves. So taking the four that are on the left and dividing them into two and two and then it says, okay, now I merge the left half of that, which gets us down to this case where it divides them into one on the left and one on the right. And then these are the ones that hit my base case. That an array of size one is trivially sorted. So, in fact, the merge sort never even goes into doing any work unless there’s at least two elements to look at.
So when it makes this call to the merge the 45 it will say, okay, there’s nothing to do. Then it’ll merge the 92 and works for the 92, which also does nothing. And now the code up here’s gonna flip. So be warned about what’s gonna happen here is I’m gonna show you what the process of the merge looks like, which is gonna do the left-right copy to the output. So this code looks a little bit dense and, again, this is not the time to get really worried about what the details of the intricacies of the code are. I think you really want to kind of step back and say conceptually the process I described of taking them off the two piles and then merging them is very much the take home point for this. And so what this
upper loop is doing is it’s basically saying well, while the two stacks, the two piles, the two halves, whatever, each have something left in them.
Then compare them and take the smaller one off the top. So it’s keeping track of all these indices, right? The indices of the left subarray, the indices of the right subarray and the indices of the right output array and it’s actually kind of – and each step is putting a new one, copying one from one of the left or the right onto the output. And so that upper loop there said is 45 less than 92? It is, right? So it copies 45 and then at this point, right? There’s nothing left on the left, so it actually drops down to those last two pieces of code, which, actually, do to copy the remainder from if there’s only one stack left then just dump them all on the end. So we’ll do the dump of the end of the 92. And so I’ve reassembled it. And so when I get back to here then I have merge sorted the left side and then I go through the processes of merge sorting the right side.
Which copies the 62 and the 41 down to the things and then does a merge back. Let me get to the stage where I’m starting to do a little bit bigger merges. So here I am with indices on my left and my right side and then my output index and it’s like, okay, is 45 less than 41? If so then take 41 here and kind of advance P2 over and still see the 41 go across and it moved both the P and the P2 up an index to say, okay, now we’re ready to pick the next one. So it looks at P1 versus P2’s, decides it’s pulling from the left side this time, and then now it still has the last most members of the two piles to look at, takes from the right side, and then we’ll just dump the end of the other side.
So then in going through this process to do it all again, kind of break it all the way down. So it’s just using recursion at every stage. So at the small stages it’s a little bit hard to figure out what’s going on, but once it gets back to here, right? It’s just doing the big merge, let it go a little too fast there, to build it back up. So there’s one thing about merge sort I should mention, which is merge sort is using extra storage. And I’m gonna get to a stage where I can explain why that’s necessary. But selection sort and insertion sort both work with what’s called in place and that means that they are using the one copy of the vector and just rearranging things within it so it doesn’t require any auxiliary storage to copy things over and back and whatnot. That it can do all the operations on one vector with a little bit of a few extra variables around.
That merge sort is really making copies. That it divides it into these two subarrays that are distinct from the original array and copies them back. So it copies the data away and then it copies it back in. And the reason for that actually is totally related to what’s happening in this merge step. That if I had – well, actually this is not the step that’s gonna show it. I’m gonna show it on this side. That if it were trying to write over the same array
Is that as it was doing the copy, right? If it’s – if this really were sitting up here and this really were sitting up her and if we were pulling, for example, from the left side we
it’s sorting is these little one element arrays, which are being merged into a two element array. Like all the work is being done in merging really. That sorting is kind of funny. When does it actually sort anything? Like never actually. It only merges things and by dividing them all the way down into all these one element arrays the merging of them it says, well, here’s these trivially sorted things. Make a two element sorted thing out of them and that you have these two element things, merge them into one-four element thing, which you merge into an eight element thing and all the way back. So all of the work is really done in the merge step.
Kind of on the sorting angle it actually is deferring it down to the very bottom. So that’s a kind of odd thing to do, right? It really just divides it all up into one – a hundred piles, each of size one and then kind of joins those into one pile, these into one pile, these into one pile, and so now I have 50 piles, right? That are each a size two. Now I join them into 25 piles of size four and then 12 piles of size eight and so on. So this is the code for the outer point of the merge algorithm. I’m actually not gonna look at the merge algorithm very in depth. I’m really more interested in the kind of outer point of this algorithm and so the steps that are going on here is being a little bit of constant work. Let me actually – we do a copy operation that copies out the left half and the right half.
Both of those operations together require linear time. So I’ve looked at every element and I’ve copied the first half onto something, the second half onto something. So that took – I looked at every element in that process. I make two calls on the left and the right side and then I do a merge on the end. We talked earlier about how that was linear and the number of elements because as I build the two piles into one sorted pile every element is touched in that process as it gets chosen to be pulled out. So linear divide and a linear join and then there’s this cost in the middle that I’d have to kind of sort out what’s happening, which is there’s sort of n work being done at this level, but then I make two recursive calls that should take time n over two.
So the input they’re working on is half, again, as big as the one I have. How much time do they take? Well, there’s my recurrence relation that allows me to express it. T of n is n, so at this level this kind of represents the copy step and the merge step at this level plus the work it took to get the two halves in sorted order. So I’m gonna show you a slightly different way of looking at recursive analysis just to kind of give you different tools for thinking about how to do this. I showed you last time how to do the repeated substitution and generalization. The pattern – I’m gonna show you this way to do it with a little bit of tree that kind of draws out the recursive calls and what’s happening in them. That the merge sort of an input of size n, does n work at that level?
The copy in the merge step, right? For there plus it does two calls to merge sort of n over two. Well, if I look at each of those calls I can say, well, they contribute an n squared and an n squared on each side. So this one has n squared copy and merge. This one has n squared copy and merge and so, in effect, I have another n squared plus itself there and then this level, right? Looks at the n over four, which has four n over four components. So that actually at each level in the tree that every element, right? Is being processed in
one of those subcalls across it. So every element is up here and they’re in two subgroups and then four subgroups and then eight subgroups.
And that each element is copied and merged in its own subgroup at each level of the recursion. So that kind of gives me this intuition that there’s n work being done on every level, every element copied and merged as part of that there. And so then what we need to complete this analysis is just to know how deep this tree grows. How far we get down in the recursion before we hit the base case. So we’re dividing by two each time. N over the two, over two to the second, to the third, to the fourth, and so on. So at any given level K down, right? We have n divided by two to the K. What we want to solve for is where n over two to the K equals one. So we’ve gotten to this smallest case where we have those trivially sorted one element inputs to look at and so we just do a little math here, rearrange that, right? Divide by two to the K or multiply by two to the K both sides when n equals two to the K. Take the log base two of both sides and it will tell me that K is log base two of n.
That I can divide n by K by two K times, where K is the log base two of n, before it bottoms out with those one element vectors. So log n levels, n for level tells me the whole thing is n log n. So n log n is a function you may not have a lot of intuition with. You may not have seen it enough to kind of know what it’s curve looks like, but if you remember how the logarithmic curve looks, right? Which is a very slow growing almost flat line and then n being linear it – the kind of combination of the two it’s called the linear rhythmic term here is just a little bit more than linear. Not a lot more, but it grows a little bit more steeply than the state of linear was, but not nearly, right? As sharply as something that’s quadratic.
So if we look at some times and let selection sort compared to merge sort, right? On an input of 10,000, right? Took a fraction, right? To do a merge sort than it did to do a selection sort and as it grows, right? So if we go from 20,000 to 50,000 we’ve a little bit more than doubled it that the merge sort times, right? Went up a little bit more than a factor of two in growing in these things. Not quite doubling. A little bit more than doubling, right? Because of the logarithmic term that’s being added there, but growing slowly enough that you can start imaging using a sort like merge sort on an input of a million elements in a way that you cannot on selection sort, right?
Selection sort – my estimate I did not run it to find this out, right? Based on the early times I can predict it’ll be about eight hours for it to sort a million elements, taking just a few seconds, right? For merge sort to do that same input. So a very big difference, right? From the n square to n log n that makes it so that if you have a sufficiently large data set, right? You’re gonna have to look to an n log n sort where an n squared sort would just not work out for you. I’ll mention here that actually n log n is the theoretical boundary for what a general purpose sort algorithm can do. So that our search from here will have to be a quest for perhaps an n log n that competes with merge sort and maybe exceeds it in some ways by having lower constant factors to it, but we won’t get a better big O for a general purpose sorting algorithm.
using sort of no smart information whatsoever and then all of the work was done in that join. I’ve got these two sorted piles. I’ve gotta get them back into order. How do I work it out? Well, the quick sort algorithm is also recursive, also kind of a split join strategy, but it does more work up front that’s not even in the split phase.
If I kind of did a more intelligent division into two halves then I could make it easier on me in the join phase. And it’s strategy for the split is to decide what’s the lower half and what’s the upper half. So if I were looking at a set of test papers I might put the names A through M over here. So go through the entire pile and do a quick assessment of are you in the upper half or the lower half? Oh, you’re in the lower, you’re in the upper, these two go in the lower, these two go in the upper. I examine all of them and I get all of the A through M’s over here and all of the N through Z’s over there and then I recursively sort those.
So I get the A to M’s all worked out and I get the N through the Z’s all worked out. Then the task of joining them is totally trivial, right? I’ve got A through M. I’ve got N through Z. Well, you just push them together and there’s actually no comparing and looking and merging and whatnot that’s needed. That join step is where we get the benefit of all of the work we did in the front end. That split step though is a little hard. So we’ll come back in on Friday and we’ll talk about how to do that split step and then what is some of the consequences of our strategy for that split step and how they come back to get at us, but that will be Friday. There will be music. There will be dancing girls.
[End of Audio]
Duration: 50 minutes