Interview Algorithm - Quick Sort

1. Concept

Quick sort, if you listen to the name, you can think that it has a faster sorting speed. It is a divide and conquer idea. Many sorting libraries that come with various languages ​​now use quick sort.

space complexity

Quick sort is an in-situ sorting that only requires a small stack as an auxiliary space, and the space complexity is O(log 2 n), so it is suitable for use when the data set is relatively large.

time complexity

The time complexity is more complicated, the best case is O(n), and the worst case is O(n 2 ), so O(nlogn) is usually referred to as the average time complexity.

2. Basic idea

Randomly find a number, you can take it randomly, or you can take a fixed position. Generally, the first or last one is called the benchmark, and then the one smaller than the benchmark is placed on the left, and the one larger than the benchmark is placed on the right. How to do it, It is to exchange with the benchmark, so that after the exchange, the left side is smaller than the benchmark, and the right side is larger than the benchmark, so that an array is divided into two sub-arrays, and then the sub-array is divided into smaller ones according to the same method. subarrays until they cannot be decomposed.

3. Examples [1]

The following paragraph is excerpted from the Internet, and there are many examples of various blog articles in the sorting process.
 
  Suppose we now sort the 10 numbers "6 1 2 7 9 3 4 5 10 8". First, find a random number in this sequence as the reference number (don't be frightened by this term, it is a number used for reference, and you will know what it is used for later). For convenience, let the first number 6 be the base number. Next, you need to put all the numbers larger than the reference number in the sequence to the right of 6, and the numbers smaller than the reference number to the left of 6, similar to the following arrangement.
      3  1  2  5  4  6  9  7  10  8
 
In the initial state, the number 6 is in the first position of the sequence. Our goal is to move 6 to a position in the middle of the sequence, let's say that position is k. Now we need to find this k, and take the kth bit as the dividing point, the numbers on the left are all less than or equal to 6, and the numbers on the right are all greater than or equal to 6. Come to think of it, is there a way you can do this?

The method is actually very simple,

1. Start "probing" from both ends of the initial sequence "6 1 2 7 9 3 4 5 10 8" respectively. First find a number less than 6 from right to left, then find a number greater than 6 from left to right, and then swap them. Two variables i and j can be used here, pointing to the leftmost and rightmost of the sequence, respectively. We give these two variables nice names "sentry i" and "sentry j". At the beginning, let the sentinel i point to the leftmost of the sequence (ie i=1), pointing to the number 6. Let sentinel j point to the far right of the sequence (i.e. j=10), pointing to the number 8.

094811yilrz1tkzkvlrriz.png

 First sentinel j began to dispatch. Because the base number set here is the leftmost number, it is very important that sentinel j is dispatched first (please think about why). Sentinel j moves to the left step by step (i.e. j--) until it finds a number less than 6 and stops.

 

2. Next, the sentinel i moves to the right step by step (i.e. i++) until it finds a number greater than 6 and stops. Finally, sentinel j stopped in front of the number 5, and sentinel i stopped in front of the number 7.

 

095430axy0qkhxxkktkktk.png
095437kdandfxhbtokk2qh.png


Now swap the values ​​of the elements pointed to by sentinel i and sentinel j. The sequence after the swap is as follows.

6  1  2  5  9  3  4  7  10  8
 
 
 
095448k1kevwlz41373e7k.png
095458ejza15wscjv7iw5c.png

 

3. The first exchange ends. Next, sentinel j continues to move to the left (a friendly reminder that sentinel j must start first each time). He found 4 (smaller than the benchmark number 6, which satisfies the requirement) and stopped. Sentinel i also continued to move to the right. He found 9 (larger than the reference number 6, which meets the requirements) and stopped. At this time, the exchange is performed again, and the sequence after the exchange is as follows.
6  1  2  5  4  3  9  7 10  8
 
The second exchange ends, and the "probing" continues. Sentinel j continued to move to the left, he found 3 (smaller than the reference number 6, which satisfies the requirement) and then stopped. Sentinel i continues to move to the right, whoops! At this point, sentinel i and sentinel j meet, and both sentinel i and sentinel j come to 3. Indicates that "detection" ends at this point. We swap the base numbers 6 and 3. The sequence after the swap is as follows.
3  1  2  5  4  6  9  7  10  8
 
095506uz7e1uuukcblhkxv.png
095514cag5fumuqqg5jnsw.png
095530e0jf6p0y6aaaw2ir.png
At this point, the first round of "probing" really ends. At this time, the reference number 6 is used as the dividing point. The numbers to the left of 6 are all less than or equal to 6, and the numbers to the right of 6 are all greater than or equal to 6. Looking back at the process just now, in fact, the mission of sentinel j is to find numbers smaller than the base number, and the mission of sentinel i is to find numbers greater than the base number until i and j meet.
The remaining steps are to repeat the above process.
OK, the explanation is over. Now that the base number 6 is in place, it happens to be in the 6th position in the sequence . At this point, we have split the original sequence into two sequences with 6 as the dividing point, the sequence on the left is "3 1 2 5 4", and the sequence on the right is "9 7 10 8". Next, the two sequences need to be processed separately. Because the sequence to the left and right of 6 is still very confusing. But it doesn't matter, we have mastered the method. Next, we only need to simulate the method just now to process the sequences on the left and right of 6 respectively. Now let's deal with the sequence to the left of 6 first.
The sequence on the left is "3 1 2 5 4". Please adjust this sequence based on 3, so that the numbers on the left of 3 are all less than or equal to 3, and the numbers on the right of 3 are all greater than or equal to 3. Okay, let's start writing.
If your simulation is correct, the order of the sequence after adjustment should be.
 
2  1  3  5  4
 
OK, now 3 is in place. Next, the sequence "2 1" to the left of 3 and the sequence "5 4" to the right need to be processed. Adjust the sequence "2 1" with 2 as the reference number, and the sequence after processing is "1 2", and 2 has returned to its place. The sequence "1" has only one number and does not require any processing. So far, we have finished processing the sequence "2 1", and the obtained sequence is "1 2". The processing of the sequence "5 4" is also modeled in this way, and the resulting sequence is as follows.
 
1  2  3  4  5  6  9  7  10  8
 
       The process just now is also simulated for the sequence "9 7 10 8" until a new subsequence cannot be split. You will end up with a sequence like this, as follows.
 
1  2  3 4  5  6  7  8  9  10
 
1  #Quick sort incoming list, start position and end position 
2  def quick_sort( li , start , end ):
 3      #If start and end meet, it means that there is only one number left in the sub-sequence I want to sort, so I don’t need it Sorted 
4      if  not start < end :
 5          return 
6  
7      mid = li[start] #Take out the first number as the reference number mid 
8      low = start    # low to mark the left side from the reference number to find the one larger than mid Number position 
9      high = end   # high to mark the right end to the left to find the position of the number smaller than mid 
10  
11      #We want to loop, as long as low and high do not meet, and when low and high are equal, it means they meet 
12      while low < high :
 13          #Starting from high to the left, find the first number that is smaller than or equal to mid, mark the position, (if the number of high is larger than mid, we move high to the left) 
14          #And we have to determine before finding, if low and high 
15          while low < high and li[high] > mid :
 16              high -= 1
 17          #After jumping out of while , the subscript of high is the found number on the right side that is smaller than mid 
18          #Find it Put the number in the left space low to mark this space 
19          li[low] = li[high]
 20          #Start from low to the right, find the first number larger than mid, mark the position, (if the number of low is less than Equal to mid, we move low to the right) 
21          #And we have to make sure that before we find it, if low and high meet, we will not find it. 
22          while low < high and li[low] <= mid :
 23              low += 1
24          #After jumping out of the while loop, the subscript of low is the position of the number on the left that is larger than mid. 
25          #We put the found number on the vacancy on the right, and high marks the vacancy 
26          li[high] = li[low]
 27          #Above we have finished finding a decimal from the right and moving to the left, finding a large number from the left and moving to the right 
28      #When this while jumps out, it is equivalent to low and high meeting, we put the mid position Put it in this space 
29      li[low] = mid
 30      #At this time, the numbers on the left side of mid are smaller than mid, and the numbers on the right side of mid are 
31  
32      #Then we sort all the numbers on the left side of mid as above 
33      quick_sort( li , start, low-1 )
 34      #We sort all numbers on the right side of mid as above 
35      quick_sort( li , low +1 , end )
 36   
37  
38 # ok let's practice 
39  if  __name__ == ' __main__ ' :
 40      li = [5,4,3,2,1 ]
 41      quick_sort(li , 0 , len(li) -1 )
 42      print (li)

4. Algorithm Analysis

Advantages: fast speed, space left, disadvantages: very fragile, must pay attention to a few small details when implementing.

When is it best to:

The sequence to be sorted is sorted in ascending order O(n), that is, 1 2 3 4 5 6 7. In this case, the benchmark selects the first number and the number of adjustments is the least. Note that only the number of debugging is reduced, and the number of comparisons is not less.

So in theory, the comparison just needs to put the data into the register and then compare.

mov ax,
mov cx,
cmp ax,cx

But in reality, because of the existence of L1 and L2, and then your program has various complex factors such as process switching, on-site recovery, etc., the actual speed is easy to say.

 

What is the worst case:

The descending order of the sequence to be sorted is O(n 2 ), that is, 7 6 5 4 3 2 1, which degenerates into bubble sort.

5. Blind

Quick sort, after the first pass, the base number you choose is the real position of the number in the sorting sequence.

So you can think about it, if the benchmark is chosen accurately, can the median of a series be quickly found? ? ?

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324687025&siteId=291194637