Bubble sorting and optimization

Posted by Sir Jos on Thu, 01 Aug 2019 10:19:12 +0200

Algorithmic Thought

Bubble sort is a typical exchange sort.

Exchange sort, as its name implies, is to judge whether it meets the requirements by comparing two elements, and if it fails to meet the requirements, to exchange positions to achieve the purpose of sorting. Bubble sorting names originate from the fact that small (large) elements, like water bubbles, float slowly from the bottom to the top of the water through constant exchange.

The idea of bubble sorting is to use comparative exchange to place the smaller or larger elements of I by using a loop. The sorting operation uses the comparison of two adjacent elements i n n. If the order is correct, it will not exchange, and if the order is wrong, it will exchange positions. The entire sorting is completed by repeatedly iterating through the array until there are no elements that can be exchanged.

Example

Let's take an example to understand the basic bubble sorting, assuming that we have an array a with internal elements of 3, 4, 1, 5, 2, that is, the initial state, as shown in the figure below. Our goal is to achieve bottom-up order from big to small through n comparisons.

First Sort

Let's start with the first sorting. As shown in the figure below, red represents the elements currently compared, and green represents the elements that have been sorted.

(1) Compare the first and second elements, 4 > 3, exchange.

(2) Compare the second and third elements, 1 < 3, no exchange.

(3) Compare the third and fourth elements, 5 > 1, exchange.

(4) Compare the fourth and fifth elements, 2 > 1, exchange.

Finally, we can see that 1 is already at the top. Four comparisons are needed in the first time to compare the five numbers.

Second Sort

The initial state of the second ranking is the final state of the first ranking, i.e. 4, 3, 5, 2, 1.

(1) Compare the first and second elements, 3 < 4, no exchange.

(2) Compare the second and third elements, 5 > 3, exchange.

(3) Compare the third and fourth elements, 2 < 3, no exchange.

The second sorting will put two in place, and this time only needs three comparisons.

Third Sort

The initial state of the third ranking is the final state of the second ranking, i.e. 4, 5, 3, 2, 1.

(1) Compare the first and second elements, 5 > 4, exchange.

(2) Compare the second and third elements, 3 < 4, no exchange.

The third sorting will place three, and this time only needs two comparisons.

However, we can see that this time all five numbers have been sorted, but when we use the ordinary bubble sort, the algorithm will continue to go down.

Fourth cycle

The initial state of the fourth ranking is the final state of the third ranking, that is, 5, 4, 3, 2, 1.

At this point, you can see that the sorting has actually been completed in the third pass, but the algorithm will continue to go down, the next code implementation to see what the situation is.

Bubble sorting performance

algorithm Best time Worst time average time Extra space stability
Bubbling O(n) O(n2) O(n2) 1 Stable

On stability: Because in the process of comparison, when two elements of the same size are adjacent, only larger or smaller, they will not exchange positions when they are equal. When two equal elements are far away, they are only exchanged to adjacent positions. Their position will not change in any way, so the algorithm is stable.

Why is the optimal time complexity O(n), of course, after the optimization of the algorithm! Just keep looking down and you'll see! ____________.

Bubble Sorting Regular Version - Code Implementation

Following is a detailed analysis of the conventional version of bubble sorting, the whole algorithm process is actually the process of analysis of the above example. It can be seen that we have to do a small loop to traverse the adjacent elements and exchange each large cycle. So there are two layers of loops in our code first.

Outer loop: The main loop, we need to help us find the current smallest element in order to put it in place. So we'll go through n-2 times to make sure that the first n-1 element is in the right place, and the last one is in the right place.

Inner circulation: that is, sub-circulation, which needs to assist us in comparing and transposing adjacent elements to float large or small on the surface of the water. So we will go through n-1-i times all the time so as to ensure that there is no homing as far as possible, and the homing need not be compared.

The above problems also arise from these two brain-less cycles. It is precisely because the cycle is carried out downward desperately that it will lead to redundancy in some special cases. For example, in the case of 5, 4, 3, 1, 2, the regular version will have four loops, but in fact the sorting has been completed for the first time.

/**
 * @author jyroy
 * Bubble Sorting Routine Edition
 */
public class BubbleSortNormal {
    public static void main(String[] args) {
        int[] list = {3,4,1,5,2};
        int temp = 0; // Create a temporary space to store the intermediate value of the exchange
        // Number of times to traverse
        for (int i = 0; i < list.length-1; i++) {
            System.out.format("The first %d All over:\n", i+1);
            //Compare the sizes of two adjacent numbers in turn. After traversing one time, place the smallest number in the array at the first position.
            for (int j = 0; j < list.length-1-i; j++) {
                // Compare adjacent elements, if the number in front is less than the number in back, exchange
                if (list[j] < list[j+1]) {
                    temp = list[j+1];
                    list[j+1] = list[j];
                    list[j] = temp;
                }
                System.out.format("The first %d The first of all%d Subexchange:", i+1,j+1);
                for(int count:list) {
                    System.out.print(count);
                }
                System.out.println("");
            }
            System.out.format("The first %d The final results are as follows:", i+1);
            for(int count:list) {
                System.out.print(count);
            }
            System.out.println("\n#########################");
        }
    }
}

Running results

First optimization of the algorithm

After the above discussion and coding, the conventional bubble sorting has been implemented. So what we're going to discuss next is the questions we just raised during the analysis.

First of all, for the first question, when we finish the third time, the whole sorting has actually been completed, but the regular version will continue sorting.

In the example above, the effect may not be obvious, but it is obvious when the array is 5, 4, 3, 1, 2. In fact, the whole array has been sorted by the first loop, but the conventional version of the algorithm will continue the following process, which is redundant.

In order to solve this problem, we can set a flag bit to indicate whether there is a switch in the current line i, if there is an i+1 trip, and if not, the current array has been sorted. The implementation code is as follows:

/**
 * @author jyroy
 * Bubble Sorting Optimized First Edition
 */
public class BubbleSoerOpt1 {
    public static void main(String[] args) {
        int[] list = {5,4,3,1,2};
        int temp = 0; // Create a temporary space to store the intermediate value of the exchange
        // Number of times to traverse
        for (int i = 0; i < list.length-1; i++) {
            int flag = 1; //Set a flag bit
            //Compare the sizes of two adjacent numbers in turn. After traversing one time, place the smallest number in the array at the first position.
            for (int j = 0; j < list.length-1-i; j++) {
                // Compare adjacent elements, if the number in front is less than the number in back, exchange
                if (list[j] < list[j+1]) {
                    temp = list[j+1];
                    list[j+1] = list[j];
                    list[j] = temp;
                    flag = 0;  //Exchange occurs, mark position 0
                }
            }
            System.out.format("The first %d The final results are as follows:", i+1);
            for(int count:list) {
                System.out.print(count);
            }
            System.out.println("");     
            if (flag == 1) {//If no elements have been exchanged, the order is already in place
                return;
            }
                   
        }
    }
}

Running results: It can be seen that the optimization effect is very obvious, two cycles less than normal.

Now let's talk about a little place left above! That's right. The optimal time complexity is O(n). After this optimization, we can do it.

When you give us a sequence of numbers, 5, 4, 3, 2, 1, let's sort from big to small. That's right. It's already arranged. That is to say, because of the existence of the flag bit, the circle above will only go through once. If flag does not become 1, the whole algorithm will end. That's the origin of O(n)!

Second optimization of the algorithm

In addition to the above problem, there is another problem in bubble sorting, that is, the small or large elements of the first row have already been in the first place, and even possibly the i-1 position has been in the second place, so when the inner cycle occurs, this situation will lead to redundant comparisons. For example: 6, 4, 7, 5, 1, 3, 2, when we do the first sorting, the result is 6, 7, 5, 4, 3, 2, 1. In fact, many subsequent exchange comparisons are redundant, because there is no exchange operation.

Let's run this array with the algorithm we've just optimized.

/**
 * @author jyroy
 * Bubble Sorting Optimized First Edition
 */
public class BubbleSoerOpt1 {
    public static void main(String[] args) {
        int[] list = {6,4,7,5,1,3,2};
        int len = list.length-1;
        int temp = 0; // Create a temporary space to store the intermediate value of the exchange
        // Number of times to traverse
        for (int i = 0; i < list.length-1; i++) {
            int flag = 1; //Set a flag bit
            //Compare the sizes of two adjacent numbers in turn. After traversing one time, place the smallest number in the array at the first position.
            for (int j = 0; j < len-i; j++) {
                // Compare adjacent elements, if the number in front is less than the number in back, exchange
                if (list[j] < list[j+1]) {
                    temp = list[j+1];
                    list[j+1] = list[j];
                    list[j] = temp;
                    flag = 0;  //Exchange occurs, mark position 0

                }
                System.out.format("The first %d All over%d The results of the trip are as follows:", i+1, j+1);
                for(int count:list) {
                    System.out.print(count);
                }
                System.out.println("");     
            }

            System.out.format("The first %d The final results are as follows:", i+1);
            for(int count:list) {
                System.out.print(count);
            }
            System.out.println("");     
            if (flag == 1) {//If no elements have been exchanged, the order is already in place
                return;
            }
                   
        }
    }
}

Running results: It can be seen that the multiple comparisons of the third trip can not be made, because the middle positions have not been exchanged in the second trip.

In view of the above problems, we can think of using a marker to record the subscript of the last position exchanged by the current train i. In the course of the train i+1, we only need to circle to the position of the subscript, because the elements in the latter position did not change positions in the previous trip. It's impossible to change positions again. For this reason, we can further optimize our code.

/**
 * @author jyroy
 * Bubble Sorting Optimized Second Edition
 */
public class BubbleSoerOpt2 {
    public static void main(String[] args) {
        int[] list = {6,4,7,5,1,3,2};
        int len = list.length-1;
        int temp = 0; // Create a temporary space to store the intermediate value of the exchange
        int tempPostion = 0;  // Record the location of the last exchange
        // Number of times to traverse
        for (int i = 0; i < list.length-1; i++) {
            int flag = 1; //Set a flag bit
            //Compare the sizes of two adjacent numbers in turn. After traversing one time, place the smallest number in the array at the first position.
            for (int j = 0; j < len; j++) {
                // Compare adjacent elements, if the number in front is less than the number in back, exchange
                if (list[j] < list[j+1]) {
                    temp = list[j+1];
                    list[j+1] = list[j];
                    list[j] = temp;
                    flag = 0;  //Exchange occurs, mark position 0
                    tempPostion = j;  //Location of record exchange
                }
                System.out.format("The first %d All over%d The results of the trip are as follows:", i+1, j+1);
                for(int count:list) {
                    System.out.print(count);
                }
                System.out.println("");     
            }
            len = tempPostion; //Give len the location of the last exchange to reduce the number of internal cycles
            System.out.format("The first %d The final results are as follows:", i+1);
            for(int count:list) {
                System.out.print(count);
            }
            System.out.println("");     
            if (flag == 1) {//If no elements have been exchanged, the order is already in place
                return;
            }
                   
        }
    }
}

Running results:

It is clear that part of the inner loop redundancy comparison has been removed and the algorithm has been further optimized.

Because of the limited level, there are some shortcomings in the description and analysis of the algorithm, and the selected examples may be inappropriate, so we can try more sequences.

Topics: Java less