In the last article, we learned about the two sorts related to the insertion class. However, compared with the sorting of the exchange class, they are really just younger brothers. It can even be said that among all the sorting algorithms, the two most famous sorts are in the exchange sorting to be introduced today. Whether it is bubbling or fast sorting, it is a common sorting algorithm in interviews. How common is it? Anyone who learns data structures and algorithms, even if you haven't learned them at all, will have heard of these two sorting algorithms. And some large and medium-sized companies directly indicate in the interview questions that these two algorithms should not be used to achieve some sorting questions. Why? Of course, it is also because these two algorithms are so famous that many people can write them by hand.
Of course, no matter what the requirements of the company you are interviewing for, as long as it is a student who is interested in developing in the programming development industry, bubbling and fast queues will definitely be a hurdle that cannot be avoided in the interview. Let’s learn about these two sorting algorithms today. But first we must figure out what this "exchange" means.
The insertion sort in the previous article refers to directly inserting data into a specified position. The meaning of exchange is to allow the data of the two locations to be directly exchanged after comparison. For example, we have an array like [3, 1, 2], which needs to be arranged in the form of [1, 2, 3]. Then we can compare 3 and 1 first and find that 1 is smaller, so we swap the positions of 3 and 1, and the result is [1, 3, 2]. Then compare 3 and 2 and find that 2 is smaller, and then exchange their positions, so the result is an array of [1, 2, 3].
Of course, this example simply illustrates the principle of exchange sorting. But it is always the same. Whether it is bubbling or fast rowing, their basic principles and core ideas are the same. After comparing the two data, they exchange positions according to the rules. In fact, from the code, we can quickly tell from one place whether a piece of sorting code is exchange sorting, that is, they will have a process of data exchange for two elements, and often use an intermediate variable under normal circumstances. . We can see this after looking at the code.
Bubble Sort
Bubble sort, first understand it from the name, it actually means to make the data float up one by one like bubbles in soda.
Let's take a look at the code directly, the code is actually very simple.
function BubbleSort($numbers)
{
$n = count($numbers);
for ($i = 0; $i < $n - 1; $i++) { // 外层循环 n - 1
for ($j = 0; $j < $n - $i - 1; $j++) { // 内层循环 n - 1 - i
if ($numbers[$j] > $numbers[$j + 1]) { // 两两相比来交换
$temp = $numbers[$j + 1];
$numbers[$j + 1] = $numbers[$j];
$numbers[$j] = $temp;
}
}
}
print_r($numbers);
}
BubbleSort($numbers);
// Array
// (
// [0] => 13
// [1] => 27
// [2] => 38
// [3] => 49
// [4] => 49
// [5] => 65
// [6] => 76
// [7] => 97
// )
It’s not easy to understand just looking at the code, so let’s take a look at the ultimate killer, that is, the graphical steps!
As you can see in the code, we have two loops. So in this picture we also show the two-layer loop of i and j. Of course, due to space limitations, we only show the j loop inside the i loop for the first time, that is, when i = 0, the j loop inside is executed.
- i = 0 is, the internal j <n-1-i, that is, the internal j is looped seven times. We directly look at the steps of the j loop on the right.
- Bubble sort is actually the use of j and j + 1 to compare two adjacent elements. From the figure, we can see that every time j++ is comparing the current j and the next j + 1 element. If the current j is greater than j + 1, then swap them.
- When j = 0, 49 at the 0th position is greater than 38 at the 1st position, so 49 and 38 exchange positions.
- When j = 1, the 49 in position 1 is compared with 65 in position 2, and the condition is not met, so it will not change. In the same way, 65 and 97 are also contrasted when j = 2, and there is no exchange.
- When j = 3, 97 is greater than 76, so a swap occurs, and 97 is swapped to j + 1, which is the position of subscript 4. At the same time, 97 is also the largest number in the entire sequence, so it will be swapped until the end of the j loop.
- The final result is that the largest number 97 is moved to the last digit of the data. In other words, the largest number has been placed in the correct position in the entire sequence.
- Then the inner loop ends, i++, and the second inner j loop with i = 1 starts. What needs to be noted here is, why do we use j <n-1-i? Because we have already sorted the largest number, we put the largest number 97 in the last position. So in the second loop of i++, we have to put the second largest number in the penultimate position. At this time, j does not need to be cycled to the last digit, just cycle to the penultimate digit.
From the step-by-step explanation above, we can see that each cycle of i in the outer layer is actually through the j cycle in the inner layer to place the largest number in the next position in order. Just like soda bubbling upwards continuously, it is the origin of the legendary bubble sorting algorithm concept.
In fact, there is another verbal decision about the bubble sorting algorithm that many students know, and it can also help us remember.
- Outer loop N minus one
- Inner loop N minus one minus I
- Pairwise comparison is small and forward (positive order)
Why is the small one in front of the positive order? In the code, our if condition is judged to be j> j+1. If it is true, exchange them, that is, put the big data in the back and the small data in the front, so that after a round, the largest data is placed The last digit is to complete the determination of the position of the largest data. If we reverse the condition, that is, if j <j+1, the largest data will be put to the front, which means the reverse order will be realized. Isn’t it amazing? Friends can try it, just change the greater than sign of the if condition.
The time complexity of bubbling can actually be seen clearly, O(N 2 ). It is an algorithm with average efficiency but very easy to understand, and it is a stable sorting algorithm.
Quick sort
How does the bubbling feel? But there is a problem with bubbling, that is, it can only compare two adjacent data, so 2 ) basically does not include the best and worst cases, no matter what It has to reach this O(N 2 ) level.
So is there any other way to optimize bubbling? A big guy invented a sorting algorithm that optimizes bubbling. That is the quick sort algorithm. Remember the binary search we learned when we were learning to search? Compared with linear search, is the efficiency of binary search improved a lot? However, the efficiency of quick sort cannot be improved so much, after all, sorting is still more complicated than searching. Moreover, it is an improvement based on bubbling, and it also uses the idea of dichotomy, which is a concept of divide and conquer. Let each comparison no longer just compare two adjacent elements one by one. So its average time complexity can be raised to O(NlogN) level. Compared with O(N 2 ), this time complexity has actually taken a big leap. Especially the larger the amount of data, the more obvious.
Similarly, let's take a look at the code first, and then look at the graph to analyze the algorithm.
function QSort(&$arr, $start, $end)
{
if ($start > $end) {
return;
}
$key = $arr[$start];
$left = $start;
$right = $end;
while ($left < $right) {
// 右边下标确定
while ($left < $right && $arr[$right] >= $key) {
$right--;
}
// 左边下标确定
while ($left < $right && $arr[$left] <= $key) {
$left++;
}
if ($left < $right) { // 交换步骤
$tmp = $arr[$left];
$arr[$left] = $arr[$right];
$arr[$right] = $tmp;
}
}
$arr[$start] = $arr[$left];
$arr[$left] = $key;
// 递归左右两边继续
QSort($arr, $start, $right - 1);
QSort($arr, $right + 1, $end);
}
function QuickSort($numbers)
{
QSort($numbers, 0, count($numbers) - 1);
print_r($numbers);
}
QuickSort($numbers);
// Array
// (
// [0] => 13
// [1] => 27
// [2] => 38
// [3] => 49
// [4] => 49
// [5] => 65
// [6] => 76
// [7] => 97
// )
Have you found a familiar figure? Yes, quicksort uses recursion. This recursion actually also contains the idea of divide and rule, just like Qin unified the six countries, divide and rule. We put a certain data in the designated position and then continue the sorting of other data according to the left and right divide and conquer, instead of letting other data to make a complete judgment on the entire sequence, thereby improving the efficiency of sorting. Therefore, the time complexity of fast sorting is much better than that of bubbling.
Similarly, on the surface, it recurs continuously, but in fact, recursion is also a kind of cycle. We can see that, like bubbling, it actually has the concept of two layers of cycles. Here we also take the first outer loop as an example to analyze what its inner loop has done.
- First, we have determined a keyword key, here we directly specify the first data 49. Then specify the left and right pointers, the left pointer left starts from 0, and the right pointer right starts from the rightmost subscript.
- Enter the inner loop, the condition is left <right, that is, the left and right pointers cannot meet!
- To start the pointer movement, start from the right first. If the data pointed to by right is greater than or equal to key, right will perform a subtraction operation, otherwise, the pointer will stop. As you can see, our pointer stops at the position of the data 27, which is the second-to-last data. The first data 49 is the same as our key value 49, so right moves to the second-to-last data. Now, 27 is less than the key value.
- Then move the left pointer to the position that meets the conditions, which is the subscript with a value of 65, and then swap the values of left and right.
- Continue the subsequent operations until left and right meet, then exit the loop, and exchange the values of the key and left positions again outside the loop. At this time, the value of 49 in the first subscript has been placed in the position determined by it. In other words, this value completes the sorting.
- Then, centering on this sorted value, split the left and right sequences, and continue to enter the recursive sorting process until all the data is sorted.
See the difference between quick sort and bubble sort, right? Each sort of fast sorting will determine the specific position of a keyword. The comparison is based on the idea of divide and conquer to reduce the size of n, except that each number is compared with the key in pairs for the first time. The sort of range. And each time the loop will arrange the data left and right according to the size of the key value, which is also the core idea of the binary search tree. This content is not explained in our series of articles, so you can refer to the relevant materials and learn by yourself.
Little easter egg: swap the values of two variables
There is a core code in the content of today's study, which is the code that exchanges the values of two variables that we said at the beginning.
// 冒泡中
$temp = $numbers[$j + 1];
$numbers[$j + 1] = $numbers[$j];
$numbers[$j] = $temp;
// 快排中
$tmp = $arr[$left];
$arr[$left] = $arr[$right];
$arr[$right] = $tmp;
We all use a temporary variable to exchange. However, many interview questions often see a question that does not use the third variable, that is, this temporary variable to exchange the values of the two variables. Have you ever been there? In fact, there are several options, let's briefly talk about two.
$a = 1;
$b = 2;
$a += $b; // a = 3
$b = $a - $b; // b = 3 - 2 = 1
$a = $a - $b; // a = 3 - 1 = 2
echo $a, PHP_EOL; // 2
echo $b, PHP_EOL; // 1
$a = "a";
$b = "b";
$a .= $b; // a = "ab"
$b = str_replace($b, "", $a); // b = str_replace("b", "", "ab") = a
$a = str_replace($b, "", $a);// a = str_replace("a", "", "ab") = b
echo $a, PHP_EOL; // b
echo $b, PHP_EOL; // a
For numbers, the exchange of two variables can be completed by directly using the addition and subtraction operations in the first paragraph. For strings, you can use str_replace() to achieve. In fact, their ideas are the same, first merge into a variable, and then use subtraction or substitution to make one variable the value of another variable first. Then use the same method to convert the value of another variable successfully. Of course, this is just the simplest and most basic algorithm. With some functions and features of PHP, we can also implement this function more conveniently.
$a = 1;
$b = 2;
list($a, $b) = [$b, $a];
echo $a, PHP_EOL; // 2
echo $b, PHP_EOL; // 1
The list() function stores the data in an array into the specified variables, and in PHP we can also directly define the array like this [x, x]. So instead of using the third temporary variable to exchange two variables, we can do it with just one line of code. list($a, $b) = [$b, $a]. I'm really sorry for this question if I don't like it here! !
Summarize
These two algorithms of exchange sorting are equivalent to the facade of the course of data structure and algorithm. Whenever you want to talk about sorting in algorithms, there will inevitably be two of them. After all, it is too classic, but we first learned the sorting of the two insertion classes and warmed up to learn these two classic algorithms. I believe that after comparing, we can have a deeper understanding of the magic and difference of these algorithms.
Test code:
Reference documents:
The example in this article is from the second edition of "Data Structure", Yan Weimin
"Data Structure" Second Edition, Chen Yue
Searchable on their respective media platforms [Hardcore Project Manager]
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。