排序(二):希尔排序、归并排序、快速排序

希尔排序原理:
1.选定一个增长量h,按照增长量h作为数据分组的依据,对数据进行分组。
2.对分好的每一组进行插入排序。
3.减小增长量,最小减为1,重复2操作。


图解.png
        //希尔排序:O(log(n))
        let arr = [4, 3, 2, 1, 6, 5, 7, 9, 8, 5]
        //1.首先根据数组的长度,确定增长量h的值
        let h = 1
        while (h < arr.length / 2) {
            h = 2 * h + 1
        }
        //2.开始排序
        while (h >= 1) {
            for (let i = h; i < arr.length; i++) {
                for (let j = i; j >= h; j -= h) {
                    if (arr[j] > arr[j - h]) {
                        let temp = arr[j]
                        arr[j] = arr[j - h]
                        arr[j - h] = temp
                    } else {
                        break
                    }
                }
            }
            //取整
            h = parseInt(h / 2)
        }

归并排序原理:
1.尽可能的一组数据拆分成两个元素相等的子组,并对每一个子组继续拆分,直到拆分后的每个子组的元素个数是1为止。
2.将相邻的两个子组进行合并成一个有序的大组。
3.不断的重复步骤2,直到最终只有一个组为止。


图解.png
        //归并排序:O(nlogn)        
        //辅助数组
        let assist = null
        function sort(arr) {
            //初始化辅助数组
            assist = new Array(arr.length)
            //定义一个lo变量和hi变量,分别记录数组中最小索引和最大索引
            let lo = 0
            let hi = arr.length - 1
            sortGroup(arr, lo, hi)
            document.getElementById("div1").innerText = arr
        }

        //分组排序
        function sortGroup(arr, lo, hi) {
            if (lo >= hi) {
                return
            }
            //拆分
            //对lo和hi之间的数组分为两组
            let mid = parseInt(lo + (hi - lo) / 2)
            sortGroup(arr, lo, mid)
            sortGroup(arr, mid + 1, hi)

            console.log("lo:"+lo+"||"+"mid:"+mid+"||"+"hi:"+hi);
            //再把两个组中的数据进行归并
            merge(arr, lo, mid, hi)           
        }

        //归并
        function merge(arr, lo, mid, hi) {
            //定义三个指针
            let i = lo
            let p1 = lo
            let p2 = mid + 1

            //遍历,移动p1,p2指针,比较对应索引处的值,找出小的那个,放到辅助数组对应的索引处
            while (p1 <= mid && p2 <= hi) {
                if (arr[p1] < arr[p2]) {
                    assist[i++] = arr[p1++]
                } else {
                    assist[i++] = arr[p2++]
                }
            }
            //遍历走完p1还没走完的索引
            while (p1 <= mid) {
                assist[i++] = arr[p1++]
            }
            //遍历走完p2还没走完的索引
            while (p2 <= hi) {
                assist[i++] = arr[p2++]
            }        
            //将辅助数组中的数据拷贝到原数组
            for (let index = lo; index <= hi; index++) {
                arr[index] = assist[index]
            }
        }
        //待排序数组
        let arr = [8, 4, 5, 7, 1, 3, 6, 2]
        //调用排序方法
        sort(arr)

快速排序原理:
1.首先设定一个分界值,通过该分界值将数组分成左右两部分。
2.将大于或等于分界值的数据放到到数组右边,小于分界值的数据放到数组的左边。此时左边部分中各元素都小于
或等于分界值,而右边部分中各元素都大于或等于分界值。
3.然后,左边和右边的数据可以独立排序。对于左侧的数组数据,又可以取一个分界值,将该部分数据分成左右两
部分,同样在左边放置较小值,右边放置较大值。右侧的数组数据也可以做类似处理。
4.重复上述过程,可以看出,这是一个递归定义。通过递归将左侧部分排好序后,再递归排好右侧部分的顺序。当
左侧和右侧两个部分的数据排完序后,整个数组的排序也就完成了。

图解.png
       //快速排序
        function sort(arr) {
            //初始化辅助数组
            assist = new Array(arr.length)
            //定义一个lo变量和hi变量,分别记录数组中最小索引和最大索引
            let lo = 0
            let hi = arr.length - 1
            sortGroup(arr, lo, hi)
            document.getElementById("div1").innerText = arr
        }
        function sortGroup(arr, lo, hi) {
            if (hi <= lo) {
                return
            }
            //需要对数组中lo索引到hi索引处的元素进行分组
            let partition = partition(arr, lo, hi)
            //让左子树有序
            sortGroup(arr, lo, partition - 1)
            //让右子树有序
            sortGroup(arr, partition + 1, hi)
        }

        //对数组a中,从索引lo到索引hi之间的元素进行分组,并返回分组界限对应的索引
        function partition(arr, lo, hi) {
            //确定分界值
            let key = a[lo]
            //定义两个指针,分别指向带切分元素的最小索引处和最大索引处的下一个位置
            let right = hi + 1
            let left = lo

            //切分
            while (true) {
                //先从右往左扫描,移动right指针,找到一个比分界值小的元素,停止
                while (key < a[--right]) {
                    if (right == lo) {
                        break
                    }
                }
                //在从左往右扫描,移动left指针,找到一个比分界值大的元素,停止
                while (a[++left] < key) {
                    if (left == hi) {
                        break
                    }
                }
                //判断 left >=right,如果是,扫描完毕,否,交换元素

                if (left >= right) {
                    break
                } else {
                    let temp = arr[left]
                    arr[left] = arr[right]
                    arr[right] = temp
                }
            }

            let temp = arr[right]
            arr[right] = arr[lo]
            arr[lo] = temp
            return right
        }

注:以上均由JS代码编写。

你可能感兴趣的:(排序(二):希尔排序、归并排序、快速排序)