zoukankan      html  css  js  c++  java
  • 归并排序

    归并排序(Merge)是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。

    归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。 将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为2-路归并。

    归并排序算法稳定,数组需要O(n)的额外空间,链表需要O(log(n))的额外空间,时间复杂度为O(nlog(n)),算法不是自适应的,不需要对数据的随机读取。

    工作原理:

    1、申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列

    2、设定两个指针,最初位置分别为两个已经排序序列的起始位置

    3、比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置

    4、重复步骤3直到某一指针达到序列尾

    5、将另一序列剩下的所有元素直接复制到合并序列尾

    1、基本思想:

    归并排序就是利用归并的思想实现的排序方法。而且充分利用了完全二叉树的深度是这里写图片描述的特性,因此效率比较高。其基本原理如下:对于给定的一组记录,利用递归与分治技术将数据序列划分成为越来越小的半子表,在对半子表排序,最后再用递归方法将排好序的半子表合并成为越来越大的有序序列。 
    经过第一轮比较后得到最小的记录,然后将该记录的位置与第一个记录的位置交换;接着对不包括第一个记录以外的其他记录进行第二次比较,得到最小记录并与第二个位置记录交换;重复该过程,知道进行比较的记录只剩下一个为止。

    2、复杂度分析

    一趟归并需要将数组 a[]中相邻的长度为h的有序序列进行两两归并.并将结果放到temp[]中,这需要将待排序列中的所有记录扫描一遍,因此耗费O(n),而又完全二叉树的深度可知,整个归并排序需要进行(这里写图片描述)次,因此总的时间复杂度为O(nlogn),而且这是归并排序算法中最好、最坏、平均的时间性能。 
    由于归并排序在归并过程中需要与原始序列同样数量的存储空间存放归并结果以及递归时深度为这里写图片描述的栈空间,因此空间复杂度为O(n+logn)
    另外,对代码进行仔细研究,发现merge函数中有if (a[i] < a[j]) 的语句,说明它需要两两比较,不存在跳跃,因此归并排序是一种稳定的排序算法。 
    也就是说,归并排序是一种比较占内存,但却效率高且稳定的算法。

    3、工作原理:

    (1)申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列 
    (2)设定两个指针,最初位置分别为两个已经排序序列的起始位置 
    (3)比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置 
    (4)重复步骤3直到某一指针达到序列尾 
    (5)将另一序列剩下的所有元素直接复制到合并序列尾

    4、归并排序过程如下:

    以数组{50,10,90,30,70,40,80,60,20}为例,

    这里写图片描述

    最后的排序结果: 
    10,20,30,40,50,60,70,80,90

    4、Java实现如下:

    public class MergeSort {
    
        public static void merge(int[] a, int low, int mid, int high) {
            int[] temp = new int[high - low + 1];
            int i = low;// 左指针
            int j = mid + 1;// 右指针
            int k = 0;
            // 把较小的数先移到新数组中
            while (i <= mid && j <= high) {
                if (a[i] < a[j]) {
                    temp[k++] = a[i++];
                } else {
                    temp[k++] = a[j++];
                }
            }
            // 把左边剩余的数移入数组
            while (i <= mid) {
                temp[k++] = a[i++];
            }
            // 把右边边剩余的数移入数组
            while (j <= high) {
                temp[k++] = a[j++];
            }
            // 把新数组中的数覆盖nums数组
            for (int k2 = 0; k2 < temp.length; k2++) {
                a[k2 + low] = temp[k2];
            }
        }
    
        public static void mergeSort(int[] a, int low, int high) {
            int mid = (low + high) / 2;
            if (low < high) {
                // 左边
                mergeSort(a, low, mid);
                // 右边
                mergeSort(a, mid + 1, high);
                // 左右归并
                merge(a, low, mid, high);
                System.out.println(Arrays.toString(a));
            }
    
        }
    
        public static void main(String[] args) {
            int a[] = { 51, 46, 20, 18, 65, 97, 82, 30, 77, 50 };
            mergeSort(a, 0, a.length - 1);
            System.out.println("排序结果:" + Arrays.toString(a));
        }
    }



    int mid = (low + high) / 2; 
    很明显这里有个bug,可能会溢出
    应该改为int mid = low + (high - low)/2;
  • 相关阅读:
    awk去重以某列重复的行
    awk 统计文件中按照某列统计某列的和(sum)
    使用jdk压缩war包
    histoty显示时间戳
    awk统计文件中某关键词出现次数
    Jbox帮助文档,默认的属性含义
    net之session漫谈及分布式session解决方案
    StackExchange.Redis 基本使用 (一) (转)
    Sql Server 表创建以及Ef浅谈
    数据验证(自定义特性)
  • 原文地址:https://www.cnblogs.com/Pjson/p/8229250.html
Copyright © 2011-2022 走看看