Detailed discussion on several common sorting algorithms (with java cases)

Tip: after the article is written, the directory can be generated automatically. Please refer to the help document on the right for how to generate it

preface

In the past few days, I have had time to study several common sorting algorithms. After in-depth understanding and research, I found that the previous understanding is too one-sided and superficial. The results and algorithm stability after each sorting are too vague... I don't say much and open the whole directly.

1, Common part of sample code

The following code is the common part outside the sorting code. Please understand it in combination with the code here. Here are just some public methods to make the following sorting methods logical and clear. (the cases here are sorted in ascending order)
public static void main(String[] args) {
	Integer[] array = {57,68,59,52,72,28,96,33,24,19};
	System.out.println("Sequence initial data:"+arrayToString(array));
	//Bubble sorting
	bubblingSort1(array);
	bubblingSort2(array);
	//Select sort
	selectSort1(array);
	fakeSelectSort(array);
	//Insert sort
	insertionSort(array);
	//Shell Sort 
	shellSort(array);
	//Heap sort
	heapSort(array);
	//Quick sort
	quickSort(array,0,array.length-1,0);
	//Merge sort
	mergerSort(array);
	//Cardinality sort
	radixSort(array);
}
/**
 * Exchange element
 * @param arr
 * @param a Subscript of element
 * @param b Subscript of element
 */
private static void swap(Integer[] arr, int a, int b) {
    int temp = arr[a];
    arr[a] = arr[b];
    arr[b] = temp;
}
/**
 * Array to set, easy to print sorting results
 * @param array
 * @return
 */
private static String arrayToString(Integer[] array) {
	return new ArrayList<Integer>(Arrays.asList(array)).toString();
}

2, Various algorithms

1. Bubble sorting

Bubble sorting is the simplest and most common sorting algorithm. When we learn sequence sorting, bubble sorting is a necessary algorithm, and its principle is also very simple. Let's take a look at the basic principle of this algorithm:


To put it bluntly, it is to compare the sizes of two adjacent elements, and then decide whether the two elements exchange positions. If we just limit our perspective and thinking here, then this blog is very worth reading.
The code is as follows (example):

/**
 * Bubble sort (from front to back)
 * @param array
 */
private static void bubblingSort1(Integer[] array) {
	for(int i =0; i<array.length-1 ; i++) { 
        for(int j=0; j<array.length-1-i ; j++) {  
           if(array[j] > array[j+1]) {
               swap(array, j, j+1);
           }
        }
        System.out.println("The first"+(i+1)+"Secondary bubble sorting result:"+arrayToString(array));
    }
}

Sorting results:

Here we explain the conditional statements of the inner loop in the code, because this case is from the front to the back, which shows that each loop ranks the current largest element behind. If it is the first loop, take the current maximum value and rank first from the bottom; For the second cycle, the current maximum value is taken and ranked second to last... So the number of elements at the end of the array sorted in the previous cycle needs to be vacated. Let's look at the results here. Look at the first sorting results and the changes of the original data. It's not difficult to see from the results to the code logic that the maximum value (we call it the target element) is dynamic every time. How to understand this sentence? Let's look at the initial data of the sequence. Suppose that our current target element is 68. When 68 and 59 are compared, 68 is large, so our target element is still 68, but the subscript has reached 2. Then continue to compare the following elements. 68 is larger than 52, our target element is still 68, but the subscript has reached 3, and then continue to compare the following elements, 68 is smaller than 72. Here, note that 68 will not appear later. Its subscript is set at 3 in this sorting. Our target element is changed to 72, and then we will continue to appear later with 72... So the target element of bubble sorting is dynamic, which is one of the biggest differences between it and insert sorting. In this way, if you look at the sorting results each time, in addition to the largest element, the position of other elements in the middle will also change greatly.

The code is as follows (example):

/**
 * Bubble sort (from back to front)
 * @param array
 */
private static void bubblingSort2(Integer[] array) {
	for(int i=0;i<array.length;i++){
		for(int j=array.length-1;j>i;j--){
			if(array[j]<array[j-1]){
				swap(array, j, j-1);
			}
		}
		System.out.println("The first"+(i+1)+"Secondary bubble sorting result:"+arrayToString(array));
	}
}

Sorting results:

This also belongs to bubble sorting, but what we usually call bubble sorting generally refers to from the front to the back. It is picked out here because its code logic is simpler than from the front to the back. The outer loop represents the sorted end subscript, and the inner loop pushes forward from the end subscript of the array, As long as you ensure J > I, you can ensure that the target element is behind the ordered sequence.

2. Select Sorting directly

Direct selection sorting is also a very common and simple sorting algorithm. Let's take a look at the basic principle of this algorithm:


The code is as follows (example):

/**
 * Select sort
 * @param array
 */
private static void selectSort1(Integer[] array) {
	//The outer loop represents the end subscript of the subsequence that has been successfully sorted
	for(int i = 0; i < array.length; i++){
		int minIndex = i; //The default i is the smallest element
		//Find the smallest element in the remaining sequence
		for(int j = i + 1; j < array.length; j++){
			if(array[j] < array[minIndex]){                   
                minIndex = j;               
            } 
		}
		swap(array,i,minIndex);
		System.out.println("The first"+(i+1)+"Secondary selection sorting results:"+arrayToString(array));
	}
}

Sorting results:

The logic of this code is very clear, and the sorting result is also very clear. Each sorting is to exchange the selected target element with the element at the target location, and other elements do not move. Moreover, only one target element is determined for each sorting and will not change.
The code is as follows (example):

/**
 * Pseudo selection sort
 * @param array
 */
public static void fakeSelectSort(Integer[] array){
	for(int i=0;i<array.length;i++){
		for(int j=i+1;j<array.length;j++){
			if(array[i]>array[j]){
				swap(array,i,j);
			}
		}
		System.out.println("The first"+(i+1)+"Secondary selection sorting results:"+arrayToString(array));
	}
}

Sorting results:

Pay attention to this sort. At first glance, it also ranks the smallest element in front of each sort, but note that this sort changes the position of other elements, so it can not be regarded as selective sort.

3. Insert sort

Insertion sorting is also a very common and simple sorting algorithm. Let's take a look at the basic principle of this algorithm:

Generally speaking, we default that the first few elements have been sorted. I get the next element and insert it into a position in front.
The code is as follows (example):

/**
 * Insert sort
 * @param array
 */
private static void insertionSort(Integer[] array) {
	//The outer loop represents the subscript of the target element, and the inner loop represents the sorted subsequence
	for(int i=1;i<array.length;i++){
		for(int j=i-1;j>=0;j--){
			if(array[j+1]<array[j]){
				swap(array,j,j+1);
			}else{
				break;
			}
		}
		System.out.println("The first"+i+"Insert sorting results:"+arrayToString(array));
	}
}

Sorting results:

Look at the results of each sort of insertion sort. The first sort 68 is larger than 57, so 68 is equivalent to being inserted in its original position. The next sort takes the target element 59 and inserts it in the middle of 57 and 68... It is not difficult to see that insertion sort has its own characteristics: 1. The target element is fixed each time. 2. It is possible that the sequence does not change after a sorting. 3. The sorted elements are at the front of the sequence, and the unordered elements at the back are still fixed under their own subscripts.

4. Hill sort

Hill sorting is less common and more complex than the above sorting algorithms. It is equivalent to inserting an upgraded version of sorting. Let's take a look at the basic principle of this algorithm:

In the case, an increment d1 is defined first. In the first grouping, 57 and 28 are divided into one group, 68 and 96 are divided into one group, 59 and 33 are divided into one group... After grouping, insert and sort in the group, and then reduce the increment to reduce the number of groups. In d2, insert and sort the sequence into 3 groups, and then repeat the above operations again until the increment becomes 1.
The code is as follows (example):

/**
 * Shell Sort 
 * @param array
 */
private static void shellSort(Integer[] array) {
	int count = 0;
	//Number of representative components of the outer cycle (every gap is a group)
	for(int gap=array.length/2;gap > 0;gap /= 2){
		count++;
		//From the gap element, directly insert and sort the groups one by one
		for(int i=gap; i<array.length; i++){
			for(int j = i; j-gap>=0 && array[j] < array[j-gap]; j-=gap){
				swap(array,j,j - gap);
			}
		}
		System.out.println("The first"+count+"Sub Hill sort every"+gap+"A group of elements sorts the results:"+arrayToString(array));
	}
}

Sorting results:

Hill sort is actually an upgraded version of insert sort. In Hill sort, first group the sequences as many as possible, with fewer elements in each group, and then insert sort will be more efficient, because the sequence will be more orderly the later times.

5. Heap sorting

Heap sorting is the least common and most difficult sorting algorithm in sorting algorithms. Its underlying principle involves the concept of small top heap or large top heap. Babies who do not understand small top heap first make up for the relevant knowledge of binary tree and complete binary tree.


The code is as follows (example):

/**
 * Heap sort
 * @param array
 */
private static void heapSort(Integer[] array) {
	class Heap{
	 	/**
	     * Adjusting the heap is the most critical part of the whole heap sequencing
	     * @param array Stack to be assembled
	     * @param i Starting node
	     * @param length Length of heap
	     */
	    public void adjustHeap(Integer[] array, int i, int length) {
	        // Take out the current element first, because the current element may have to move all the time
	        int temp = array[i];
	        //2*i+1 is the left subtree of left subtree i (because i starts from 0), and 2*k+1 is the left subtree of K
	        for (int k = 2 * i + 1; k < length; k = 2 * k + 1) {  
	            // Let k point to the largest of the child nodes first
	            if (k + 1 < length && array[k] < array[k + 1]) {  //If there is a right subtree, and the right subtree is greater than the left subtree
	                k++;
	            }
	            //If it is found that the node (left and right child nodes) is larger than the root node, the values are exchanged
	            if (array[k] > temp) {
	            	swap(array, i, k);
	                // If the child node is replaced, the subtree with the child node as the root will be affected. Therefore, the cycle continues to judge the tree where the child node is located
	                i  =  k;
	            } else {  //No exchange, just terminate the loop
	                break;
	            }
	        }
	    }
	}
	
	//Here, the index of the element starts from 0, so the last non leaf node array.length/2 - 1
       int count = 0;
	for (int i = array.length/2-1; i >= 0; i--) {  
       	new Heap().adjustHeap(array, i, array.length);  //Adjustment reactor
       	System.out.println("The first"+(++count)+"Initialize the large top heap for times, and the result is:"+arrayToString(array));
       }
       System.out.println("Initialization of the large top heap is completed, and the result is:"+arrayToString(array));
       // After the above logic, the heap building is completed
       // Next, start sorting logic
       count = 0;
       for (int j = array.length - 1; j > 0; j--) {
           // Element exchange is used to remove the large top heap
           // Put the root element of the big top heap at the end of the array; In other words, after each heap adjustment, an element will reach its final position
           swap(array, 0, j);
           // After the element exchange, there is no doubt that the last element does not need to consider the sorting problem.
           // The next thing we need to sort is to remove the heap of some elements, which is why this method is placed in the loop
           // Here, in essence, it is adjusted from top to bottom and from left to right
           new Heap().adjustHeap(array, 0, j);
           System.out.println("The first"+(++count)+"Secondary heap sort result:"+arrayToString(array));
       }
}

Sorting results:

The core idea of heap sorting is to first construct the sequence into an ordinary complete binary tree according to the subscript order, then convert the ordinary complete binary tree into a small top heap or a large top heap by the operation of binary tree, and then exchange the elements to the specified subscript by the root removal method. For the sorting results, we can see the clue as long as we convert the results after each sorting into an ordinary complete binary tree.

6. Quick sort

Quick sort is one of the less common algorithms, but its basic idea is relatively simple:

The code is as follows (example):

/**
 * Quick sort
 * @param array
 */
private static Integer[] quickSort(Integer[] array,int start,int end,int count) {
	//First of all, the count parameter here is not a necessary parameter of the sorting algorithm. This parameter is added here to facilitate the statistics of array changes
	//Define a datum first
	int pivot = array[start];
    int i = start, j = end;
    while (i < j) {
    	//If the element pointed to by the pointer behind the datum is larger than the datum, move the pointer
        while (i < j && array[j] > pivot) {
            j--;
        }
        //Similarly, if the element pointed to by the pointer in front of the datum is smaller than the datum, move the pointer
        while (i < j && array[i] < pivot) {
            i++;
        }
        //After the above two cycles, the two pointers on the surface are decomposed to point to elements greater than and less than the reference,
		//In fact, because the benchmark we defined at the beginning is the first element, one of the two pointers here must point to the benchmark
		//Just exchange directly here
		swap(array, i, j);
		System.out.println("The first"+(++count)+"Secondary quick sort results:"+arrayToString(array));
    }
    //Sort left subsequence
    if (i - 1 > start) array = quickSort(array,start,i-1,count);
    //Sort right subsequence
    if (j + 1 < end) array = quickSort(array,j+1,end,count);
	return array;
}

Sorting results:

Here, there are two sorts in the sorting results, the 9th to the 13th because the benchmark element has reached the middle after the 8th sort. From the 9th sorting, the left and right subsequences are sorted at the same time.

7. Merge and sort

Merge sort has one thing in common with Hill sort and quick sort, that is, they all want to split the complex sequence into simple subsequence sort.

Here we explain how merge sort sorts two sets of subsequences. First, we define an array R with the same length as the original sequence to undertake the sorted sequence. In the figure above, the first row is the original sequence, and the second row divides every two elements of the original sequence into a group and sorts them (the sorting method here is the same as that from the second row to the third row). The focus is on the merging of the two subsequences from the second row to the third row. We define two pointers i and j, pointing to 57 and 52 respectively. Compare the sizes of 57 and 52. If 52 is small, put 52 into R and move the pointer J to 59. Then compare 57 and 59,57, put 57 into R and move the pointer i to 68
The code is as follows (example):

/**
 * Merge sort
 * @param array
 */
private static void mergerSort(Integer[] array) {
	Integer[] temp = new Integer[array.length];
	class Sort{
		int count;
		public void mergerSort(Integer[] array, int first, int last, Integer[] temp) {
			if (first < last) {
				int mid = (first + last) / 2;
				mergerSort(array, first, mid, temp); // Recursive merge left element
				mergerSort(array, mid + 1, last, temp); // Recursive merge right element
				mergerArray(array, first, mid, last, temp); // Then merge the two ordered sequences
			}
		}
		public void mergerArray(Integer array[], int first, int mid, int last, Integer[] temp) {
			int i = first, j = mid + 1; // i is the starting point of the first group and j is the starting point of the second group
			int m = mid, n = last; // m is the end point of the first group and n is the end point of the second group
			int k = 0; // k is used to point to where the temp array is currently placed
			while (i <= m && j <= n) { // Compare the two ordered sequences circularly and fill in the array temp
				if (array[i] <= array[j])
					temp[k++] = array[i++];
				else
					temp[k++] = array[j++];
			}
			while (i <= m) { // If the comparison is completed and there are still numbers left in the first group, fill them all in temp
				temp[k++] = array[i++];
			}
			while (j <= n) {// If the comparison is completed and there are still numbers left in the second group, fill them all in temp
				temp[k++] = array[j++];
			}
			for (i = 0; i < k; i++) {// Fill the ordered numbers back to the corresponding position of the array array
				array[first + i] = temp[i];
			}
			System.out.println("The first"+(++count)+"Secondary merge sort result:"+arrayToString(array));
		}
	}
	new Sort().mergerSort(array, 0, array.length - 1, temp);
}

Sorting results:

8. Cardinality sorting

Cardinality sorting is the least common of these algorithms, but its unique processing method is worth studying,

The code is as follows (example):

/**
 * Cardinality sort
 * @param array
 */
private static void radixSort(Integer[] array) {
	//Maximum value of waiting sequence
	int max = array[0];
	//Calculate maximum
	for (Integer anArr : array) {
		if (anArr > max) {
			max = anArr;
		}
	}

	int k = 0;
	int n = 1;
	int m = 1; //Which bit is the sorting basis of control key values
	int[][] temp = new int[10][array.length]; //The first dimension of the array represents the possible remainder 0-9
	int[] order = new int[10]; //The array order[i] is used to represent the number of I bits
	while(m <= String.valueOf(max).length()) {
		for(int i = 0; i < array.length; i++)
		{
			int lsd = ((array[i] / n) % 10);
			temp[lsd][order[lsd]] = array[i];
			order[lsd]++;
		}
		for(int i = 0; i < 10; i++) {
			if(order[i] != 0) {
				for (int j = 0; j < order[i]; j++) {
					array[k] = temp[i][j];
					k++;
				}
			}
			order[i] = 0;
		}
		n *= 10;
		k = 0;
		System.out.println("The first"+m+"Sub cardinality sorting result:"+arrayToString(array));
		m++;
	}
}

Sorting results:

The idea of cardinality sorting is completely different from the previous algorithm. It first defines a two-dimensional array, then splits each element of the sequence according to the number of bits, and then puts the element into the two-dimensional array according to the number of bits, followed by ten bits, followed by hundreds

3, Summary


Here is a brief explanation of how to judge the stability of the algorithm. If there is such a sequence {25,69,45,69,23}. We can see that two elements in the sequence are 69. If the front 69 can be ensured to be before the rear 69 after sorting, the algorithm is stable. If the front and rear positions of the two elements cannot be guaranteed after sorting, the algorithm is unstable.

Tags: Java Algorithm

Posted on Sun, 21 Nov 2021 01:26:57 -0500 by DK_Sun