zoukankan      html  css  js  c++  java
  • 排序算法(六) —— 归并排序

    归并排序(Merge Sort),又称二路归并排序,是指将一个数组一分为二,对每一个子数组递归排序,最后将排好的子数组合并为一个有序数组的过程。归并排序,是“分治法”应用的完美实现。

    From Wikipedia:https://en.wikipedia.org/wiki/Merge_sort

    1. 归并排序图示

    2. 归并排序流程

    通过图示,可以发现归并排序一共只需要两个步骤:

    • 分:将原数组分为n个子数组,每个子数组长度为1(长度为1的数组自然有序)。
    • 合:依次将两个相邻的有序数组,合并成一个有序数组,重复操作直至剩下一个有序数组。

    3. 代码实现

    归并排序的代码逻辑还是很容易看懂的,数组两分,左右递归,难点在于“合”这一步骤:

    public abstract class BasicMergeSort implements Sort {
    
        @Override
        public void sort(int[] array) {
            sort(array, 0, array.length - 1);
        }
    
        private void sort(int[] array, int left, int right) {
            if (left < right) {
                int mid = (left + right) >>> 1;
                sort(array, left, mid);
                sort(array, mid + 1, right);
                merge(array, left, mid, right);
            }
        }
    
        protected abstract void merge(int[] array, int left, int mid, int right);
    }
    

    合并两个有序数组(长度分别为 n 和 m),可以开辟一个长度为 m+n 的新数组。

    使用两个指针记录数组位置,依次比较指针位置的数字,将较小的数字放入新数组。这样可以在线性的时间内完成合并工作。

    public final class MergeSort1 extends BasicMergeSort {
    
        @Override
        protected void merge(int[] array, int left, int mid, int right) {
            int[] newArray = new int[right - left + 1];
            int startIndex1 = left;
            int startIndex2 = mid + 1;
            for (int i = 0; i < newArray.length; ++i) {
                if (startIndex1 == mid + 1) {
                    newArray[i] = array[startIndex2++];
                } else if (startIndex2 == right + 1) {
                    newArray[i] = array[startIndex1++];
                } else {
                    newArray[i] = array[startIndex1] < array[startIndex2] ? array[startIndex1++] : array[startIndex2++];
                }
            }
            System.arraycopy(newArray, 0, array, left, newArray.length);
        }
    }

    4. 归并排序的时间复杂度和空间复杂度

    显而易见,递归的次数为 m = log2n,合并操作的时间消耗是线性的,所以时间复杂度 T(n) 如下:

    空间复杂度为O(n).

    5. 空间复杂度为 O(1) 的归并排序

    归并排序中,合并的步骤可以采取直接插入排序(因为前半部分已经有序,所以直接插入排序的效率很高)。

    如此一来,可以将空间复杂度由 O(n) 降低至 O(1),然而相对的时间复杂度则由 O(nlog2n) 升至 O(n^2)。

    public final class MergeSort2 extends BasicMergeSort {
    
        @Override
        protected void merge(int[] array, int left, int mid, int right) {
            for (int i = mid + 1; i <= right; ++i) {
                int cur = array[i];
                boolean flag = false;
                for (int j = i - 1; j >= left; --j) {
                    if (cur < array[j]) {
                        array[j + 1] = array[j];
                    } else {
                        array[j + 1] = cur;
                        flag = true;
                        break;
                    }
                }
                if (!flag) {
                    array[left] = cur;
                }
            }
        }
    }

    6. 归并排序的性能分析及优化

    两种归并排序的算法,分别是采取了空间换时间,及时间换空间的策略,其性能各有优劣,但是通过分析可以得出以下特点:

    • 计算机对于频繁开辟小数组空间的消耗,比开辟等价大的单个数组空间,代价要来的更大。
    • 使用直接插入排序(从1/2处开始),由于拥有较小的最高次幂系数,其性能在长度 n 较小的时候,与 T(n) = O(log2n) 相差不大。

    根据以上两个性质,可以在归并排序中,设置一个阈值。

    超过这个给定的阈值,则采取空间换时间的策略;反之,采用时间换空间的策略,从而提高归并排序的效率。

    public class MixedMergeSort implements Sort {
    
        private int threshold = 2 << 4;
    
        private BasicMergeSort sort1 = new MergeSort1();
        private BasicMergeSort sort2 = new MergeSort2();
    
        public int getThreshold() {
            return threshold;
        }
    
        public void setThreshold(int threshold) {
            this.threshold = threshold;
        }
    
        @Override
        public void sort(int[] array) {
            sort(array, 0, array.length - 1);
        }
    
        private void sort(int[] array, int left, int right) {
            if (left < right) {
                int mid = (left + right) >>> 1;
                sort(array, left, mid);
                sort(array, mid + 1, right);
                if (right - left > threshold) {
                    sort1.merge(array, left, mid, right);
                } else {
                    sort2.merge(array, left, mid, right);
                }
            }
        }
    }
    

    Source Code: https://github.com/Gerrard-Feng/algorithm-learning.git

  • 相关阅读:
    毕业五年后的差距
    基于jsonlib.jar包Json程序 实战篇
    Hashtable
    [转]浏览器是怎样工作的:渲染引擎,HTML解析
    Web.config之连接字介绍
    jQuery选择器全解【转】
    JavaScript document属性和方法
    zoj 2316 Matrix Multiplication 夜
    zoj 2318 Get Out! 夜
    hdu 3666 THE MATRIX PROBLEM 夜
  • 原文地址:https://www.cnblogs.com/jing-an-feng-shao/p/9038915.html
Copyright © 2011-2022 走看看