[Data structure and algorithm] Advanced sorting (Hill sorting, merge sorting, quick sorting) complete ideas, and use code to encapsulate sorting functions

This series of articles [Data Structure and Algorithm] All the complete code has been uploaded to github . Friends who want the complete code can go there and get it directly. If possible, please click on Star ~ Put a jump link below

This article will explain more advanced sorting algorithms. As the name suggests, their sorting ideas must be more complicated, and their efficiency must be higher than simple sorting. In order to understand the advanced sorting algorithm more conveniently, it is recommended that you first understand the simple sorting clearly, because the advanced sorting also draws on the idea of ​​simple sorting to some extent, put the article link below

[Data structure and algorithm] Simple sorting (bubble sorting, selection sorting, insertion sorting) complete ideas, and use code to encapsulate the sorting function

So let's take a look at three advanced sorting algorithms

One, Hill sort

Hill sort is an improved version of insertion sort, which makes up for the shortcomings of insertion sort in some cases.

For example, when the length of the array is 100, the length of the array in the front ordered area is 80. At this time, we use the 81st number to compare the size with all the elements in the front ordered area, but it happens that the 81st number is the 100. The smallest in the number, it should be at index 1, as shown in the figure

Insert picture description here
In this example, the value of the 81st data is 1, so the 80 elements in the ordered area in the front must move one position back, which greatly affects the sorting performance.

Therefore, we have to find a way to make the small value forward as early as possible, and make the large value backward, so as to avoid the above situation. This is the problem to be solved by Hill sorting.

Hill sorting is also called reduced incremental sorting. It first sets an increment n, the size of which is half the length of the array, and treats the elements with an interval of n as a group, and then the elements within each group are processed from small to large Insertion sort ; then reduce the increment n by half, and perform grouping and insert sort again until the increment n is 1, because when the increment is 1, all elements are in the same group


To make it easier for everyone to understand, I use an example to show a complete Hill sorting process. First of all, the initial state of the data is shown in the figure. Here, in order to better reflect the advantages of Hill sorting, I specially put elements with larger values. To the left position, the element with the smaller value is placed to the right position

Insert picture description here
The length of the array is 8, so we set the initial increment to be 8 / 2 = 4, then the grouping of the array is shown in the figure below:

Insert picture description here
The elements of the same color in the picture are a group, and the interval of each element in each group is 4, and now each group is sorted from small to large, and the sorting result is shown in the following figure:

Insert picture description here
At this time, we reduce the increment by half, that is 4 / 2 = 2, the same, now regroup all elements, and treat all elements with an interval of 2 as a group, and the grouping result is shown in the following figure:

Insert picture description here

The elements of the same color in the picture are a group, and the interval of each element in each group is 2. Now, sort each group from small to large, and the sorting result is shown in the following figure:

Insert picture description here
We continue to reduce the increment by half, that is 2 / 2 = 1, in the same way, all elements are now regrouped, and all elements with an interval of 1 are regarded as a group. At this time, all elements are in the same group, which is equivalent to all data Performing ordinary insertion sorting, we can see that compared with the initial data, in general, small values ​​are more to the left, and large values ​​are more to the right, so the sorting efficiency is very high. The result is shown below:

Insert picture description here

Next, use an animated picture to demonstrate the complete Hill sorting process

Insert picture description here
After understanding the implementation process of Hill sorting, we will now encapsulate it with code

function shellSort(arr) {
    
    
    // 1. 获取数组长度
    let length = arr.length

    // 2.获取初始的间隔长度
    let interval = Math.floor(length / 2)

    // 3. 不断地缩小间隔的大小,进行分组插入排序
    while(interval >= 1) {
    
    

        // 4. 从 arr[interval] 开始往后遍历,将遍历到的数据与其小组进行插入排序
        for(let i = interval; i < length; i++) {
    
    
            let temp = arr[i]
            let j = i
            while(arr[j - interval] > temp && j - interval >= 0) {
    
    
                arr[j] = arr[j - interval]
                j -= interval 
            }

            arr[j] = temp           
        }

        // 5. 缩小间隔
        interval = Math.floor(interval / 2)
    }

    return arr
}

Let’s use the example we just gave to verify whether the Hill order we encapsulated is correct

let arr = [63, 76, 13, 44, 91, 8, 82, 3]
let res = shellSort(arr)
console.log(res)
/* 打印结果
[3, 8, 13, 44, 63, 76, 82, 91]
*/

In the above case, the worst-case time complexity of Hill sorting is O(n²). In fact, the time complexity of Hill sorting is also related to the increment. The above is the increment obtained by always taking half of the array length. In fact, there are some other increment rules that can make Hill sorting more efficient, such as Hibbard Incremental sequence , Sedgewick incremental sequence , this article will not explain too much about these two increments, you can go to the Internet to search.

Two, merge sort

The implementation of merge sort uses a divide-and-conquer idea, that is, an array is continuously compared by grouping in pairs, and finally until all elements are grouped into a group, it is naturally ordered as a whole.

Let's take a look at the main idea of ​​merge sort. First, there is a set of data arranged as shown in the figure below:

Insert picture description here
First, from left to right, every two elements are regarded as a group. Before combining, judge the size of these two elements separately, the smaller is on the left and the larger is on the right, as shown in the figure.

Insert picture description here
Continue to take two more elements to form a group and compare the sizes, as shown in the figure:

Insert picture description here
Continue to the previous step, as shown in the figure:

Insert picture description here
At this point, all the elements of the original array have been grouped by two, and now the length of the entire array has become 3, as shown in the following figure:

Insert picture description here
At this time, we need to re-take two elements from left to right to form a group each time, and compare the sizes of all sub-elements in the two elements. Because the two elements are in order at this time, so we To compare the sizes of the two, you only need to compare the first element of the array each time, as shown in the following figure:

Insert picture description here
At this time, there is only one element left in the original array, so no combination processing is performed on it. The array at this time is like this:

Insert picture description here
At this time, there are only two elements in the array, so we only need to constantly compare the size of the sub-elements inside the two elements to obtain a complete ordered array. The process is shown in the following figure:

Insert picture description here
This is a complete merging and sorting process, let's use code to implement it

function mergeSort(arr) {
    
    
    
    // 将所有元素不断地两两组合,直到所有元素都被组合成一个组
    while(arr.length > 1){
    
    
        // 获取一下遍历前的数组长度,方便下面判断需要组合几次
        let length = arr.length
        
        // 创建空的新数组,用于存放所有组合后的元素
        let next_arr = []
        
        // 取两个元素进行组合,一共取 length / 2 次
        for(let i = 0; i < Math.floor(length / 2); i++){
    
    
            // 取出第一个元素
            let left = [].concat(arr.shift())
            // 取出第二个元素
            let right = [].concat(arr.shift())
            // 创建另一个新的空数组,用于存放组合后的所有元素
            let new_arr = []

            // 取两个数组中头部最小的值放到新数组中,直到一个数组为空
            while(left.length > 0 && right.length > 0){
    
    
                let min = left[0] > right[0]? right.shift() : left.shift()
                new_arr.push(min)
            }
            // 将合并好的数组添加到新的数组中
            next_arr.push(new_arr.concat(left.length == 0? right : left))
        }
        // 判断是否有一个未成组的数组
        if(arr.length == 1) next_arr.push(arr[0]);
        
        // 将所有组合后的元素构成的新数组作为下一次遍历的对象
        arr = next_arr
    }

    // 返回完整有序数组
    return arr[0]
}

Let's use this method to see if it is correct. To facilitate everyone's understanding, I added a printed code to the merge sort function. You can see the array after each traversal. The results are as follows

let arr = [19, 97, 9, 17, 1, 8]
mergeSort(arr)

/* 打印结果:
第一次组合后:[ [ 19, 97 ], [ 9, 17 ], [ 1, 8 ] ]
第二次组合后:[ [ 9, 17, 19, 97 ], [ 1, 8 ] ]
第三次组合后:[ [ 1, 8, 9, 17, 19, 97 ] ]
*/

Looking at the code, it is not difficult to find that the merge sort operation takes up a lot of memory, because in the process of combining, we constantly have to create new arrays and then merge them. However, the number of comparisons is very small, and the comparison is only performed each time the elements are merged, so the efficiency of merge sort is still very high.

Three, quick sort

I believe you are familiar with quick sorting . Even if you haven't used it, you must have heard it. With such a reputation, then its sorting efficiency must be very high. And quick sort is often asked in interviews, and you may be able to write by hand on the spot~ so everyone must master its core ideas

Quicksort also uses the idea of ​​divide and conquer, and its implementation ideas are very interesting:

  1. First select an element as the base point pivot
  2. Put all values ​​smaller than pivot on the left side of pivot in the remaining elements; put all values ​​larger than pivot on the right side of pivot
  3. Then sort all elements on the left of pivot and all elements on the right of pivot from step 1 in order, until all the elements are completely ordered

The idea seems very simple, so let's take an example to see the whole process of quick sort in detail.

First, there is such a set of data, as shown in the figure below:

Insert picture description here
First, we need to select an element as the base point pivot. After sorting, all the elements on the left of the pivot are smaller than the pivot, and all the elements on the right are larger than the pivot. Therefore, we want to average the two sides as much as possible, so here will One way to find an approximate midpoint is to take the indexes of the two ends of the array, which are left and right, and then take a midpoint center. The result is as follows:

Insert picture description here

Then find a medium-sized value among these three elements and place it at the beginning of the array, as shown in the following figure:

Insert picture description here

This, we can first element of the array as the pivot point of the traverse, and we will introduce two pointers, i.e. i, and j, respectively, pointing left and right, shown in FIG.

Insert picture description here

Next, you can traverse. Here we call the traversal process as the hole filling method , because now we have got the first value of the array pivot, so we can assume that there is no element at this position, leaving a hole, we need us Fill in other elements. So we want the pointer jto start looking for the right to a smaller value than the pivot, if found, will be found to fill the pit value, but note that the pointer jcan not find the pointer ito the left to go, that is, when the pointer jwith the pointer iStop moving when they overlap.

The process is shown in the figure below:

Insert picture description here

At this point we can see that the pointer jto find a value less than the pivot 8, and the value found to fill the pit, so this time the pointer jis pointing it left a pit, and other elements needed to fill the hole, so this time we will make a pointer ito find a value greater than the pivot to the right, and the value to fill in the pit, the same, the pointer ican not find the pointer jon the right, that is, when the pointer ito the pointer jwhen it coincides stop moving.

The process is shown in the figure below:

Insert picture description here

Pointer ito find a value greater than the pivot 97and fill it to the pit, then pointer ilocation of a pit on the left, so we have to let the pointer jto the left to find a value of less than pivot and fill in the pit, as the process The picture shows:

Insert picture description here

Then, the pointer ibut also to find the right value is greater than the pivot, but moved two positions are not found, and at this point pointer ipointers joverlap, and this time we only need to pivot to fill in the pit on the realization of the left pivot All elements are smaller than it, and all elements on the right are larger than it, as shown in the figure:

Insert picture description here

The next operation is that we have to perform the above series of operations on all the elements on the left and all elements on the right of the pivot at this time separately, and then the quick sort can be realized. In this example, the number of elements in the left and right areas is less than or equal to 3. Therefore, it is more convenient to directly compare these values ​​with each other. The process is as follows:

Insert picture description here

Understand the realization idea of ​​quick sort, let's use code to realize it

function quickSort(arr) {
    
    
    // 两个数据进行交换
    function exchange(v1, v2) {
    
    
        let temp = arr[v1]
        arr[v1] = arr[v2]
        arr[v2] = temp
    }
    
    // 找到相对合适的元素放到数组索引为0的位置作为基点pivot
    function init(left, right) {
    
    
        let center = Math.floor((left + right) / 2)

        // 比较索引为left、center、right三个值的大小,从小到大排列
        if(arr[left] > arr[right]) exchange(left, right)
        if(arr[center] > arr[right]) exchange(center, right)
        if(arr[left] > arr[center]) exchange(left, center)

        // 判断数组长度是否大于3,若小于3,则数组已经排序好了,不需要做任何处理
        if(right - left > 2) exchange(left, center)
    }

    function quick(left, right) {
    
    
        init(left, right)
        // 若数组长度小于等于2,则不需要做任何操作了,因为init函数已经排序好了
        if(right - left <= 2) return;
        
        // 创建指针i和j,分别指向left和right
        let i = left
        let j = right
        // 将该数组区域的第一个元素作为基点pivot
        let pivot = arr[i]

        // 不断让指针i和j寻找合适的值填坑,直到两个指针重合
        while(i < j) {
    
    
            // 指针j不断向左找小于pivot的值,但指针j不能找到指针i的左边
            while(arr[j] > pivot && j > i) {
    
    
                j --
            }
            // 将找到的小于pivot的值填到指针i所指向的坑中
            arr[i] = arr[j]

            // 指针i不断向右找大于pivot的值,但指针i不能找到指针j的右边
            while(arr[i] < pivot && i < j) {
    
    
                i ++
            }
            // 将找到的大于pivot的值填到指针j所指向的坑中
            arr[j] = arr[i]
        }

        // 将pivot填到指针i和指针j共同指向的坑中
        arr[i] = pivot

        // 对此时pivot的左边所有元素进行快排
        quick(left, i - 1)
        // 对此时pivot的右边所有元素进行快排
        quick(i + 1, right)
    }

    quick(0, arr.length - 1)

    return arr  
}

We can simply verify the correctness of the quick sort

let arr = [19, 97, 9, 17, 8, 1]
console.log(quickSort(arr))
/* 打印结果为:
	[1, 8, 9, 17, 19, 97]
*/

Fourth, the conclusion

The advanced sorting in the sorting algorithm (Hill sorting, merge sorting, quick sorting) is over, the next article is dynamic programming

You can follow me, and I will continue to update other data structure and algorithm articles for everyone to learn from, and I will put these articles in the [Data Structure and Algorithm] column for everyone to learn and use.

Then you can pay attention to my WeChat official account: Front-end Impressions . After the article in this column is finished, I will put the notes of each data structure and algorithm on the official account, and you can go there to get it.

Or you can go to my github to get the complete code , welcome everyone to order Star

I'm Lpyexplore, an explorer who entered the front end because of a Python crawler. It’s not easy to create, please pay attention to those you like, click a favorite, give a like~

Guess you like

Origin blog.csdn.net/l_ppp/article/details/108855298