经典排序算法(五) —— Merge Sort 归并排序

简介

约翰·冯·诺伊曼在 1945 年提出了归并排序。归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。

将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为2-路归并。

归并排序的时间复杂度是O(nlogn)。代价是需要额外的内存空间。

排序过程

实现

版本一
@Test
public void test() {
    int[] nums = new int[]{4, 2, 8, 9, 6, 3, 5, 7, 1, 2};
    int[] mergeSort = mergeSortSimple(nums, 0, nums.length - 1);
    printArray(mergeSort);
}


/*
 * 归并排序
 * @param nums 待排序数组
 * @param start 开始位置
 * @param end   结束位置
 * @return
 */
public int[] mergeSortSimple(int[] nums, int start, int end) {
    if (start == end) {
        return new int[]{nums[start]};
    }
    int mid = (start + end) / 2;
    int[] m1 = mergeSortSimple(nums, start, mid);
    int[] m2 = mergeSortSimple(nums, mid + 1, end);
    return mergeNumsSimple(m1, m2);
}

/**
  * 合并两个有序数组
  * @param num1 待合并数组1
  * @param num2 待合并数组2
  * @return 合并数组
*/
public int[] mergeNumsSimple(int[] num1, int[] num2) {
    int l1 = num1.length;
    int l2 = num2.length;
    int[] resNum = new int[l1 + l2];
    int resIdx = 0;
    int index1 = 0;
    int index2 = 0;
    while (resIdx < resNum.length) {
        while (index1 < l1 && index2 < l2) {
            resNum[resIdx++] = num1[index1] < num2[index2] ? num1[index1++] : num2[index2++];
        }
        while (index1 < l1) {
            resNum[resIdx++] = num1[index1++];
        }
        while (index2 < l2) {
            resNum[resIdx++] = num2[index2++];
        }
    }
    return resNum;
}

	
/**
 * 打印数组
 * @param nums
*/
public void printArray(int[] nums) {
    for (int num : nums) {
        System.out.print(num + "\t");
    }
    System.out.println();
}
版本二

 
@Test
public void test() {
    int[] nums = new int[]{4, 2, 8, 9, 6, 3, 5, 7, 1, 2};
    int[] res = new int[nums.length];
    mergeSortSimpleOpt(nums, 0, nums.length - 1, res);
    printArray(res);
}


/**
 * 归并排序
*/
public void mergeSortSimpleOpt(int[] nums, int start, int end, int[] res) {
        if (start == end) {
            return;
        }
        int mid = (start + end) / 2;
        mergeSortSimpleOpt(nums, start, mid, res);
        mergeSortSimpleOpt(nums, mid + 1, end, res);
        mergeOpt(nums, start, end, res);
}

/**
* 版本一递归过程中会有额外的临时数组开销,在此版本优化,直接传入定义好的结果数组做合并
*/
public void mergeOpt(int[] nums, int start, int end, int[] res) {
    int end1 = (start + end) / 2;
    int start2 = end1 + 1;

    int idx1 = start;
    int idx2 = start2;
    int resIdx = idx1 + idx2 - start2;
    while (idx1 <= end1 && idx2 <= end) {
        res[resIdx++] = nums[idx1] < nums[idx2] ? nums[idx1++] : nums[idx2++];
    }
    while (idx1 <= end1) {
        res[resIdx++] = nums[idx1++];
    }
    while (idx2 <= end) {
        res[resIdx++] = nums[idx2++];
    }
    while (start <= end) {
        nums[start] = res[start++];
    }
}

复杂度

O(nlogn)
posted @ 2021-12-16 22:39  别摸我键盘  阅读(66)  评论(0编辑  收藏  举报