您的位置:首页 > 理论基础 > 数据结构算法

【数据结构与算法之排序】归并排序

2017-09-14 17:35 603 查看

1.归并排序过程

归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。

首先考虑下如何将将二个有序数列合并。这个非常简单,只要从比较二个数列的第一个数,谁小就先取谁,取了后就在对应数列中删除这个数。然后再进行比较,如果有数列为空,那直接将另一个数列的数据依次取出即可。

合并两个有序数列经典实现代码【合并两个有序数列的时间复杂度为O(n)】:

//将有序数组a[]和b[]合并到c[]中
void MemeryArray(int a[], int n, int b[], int m, int c[])
{
int i, j, k;

i = j = k = 0;
/*分别一次比较a数组和b数组中的元素,直到其中一个数组元素取完*/
while (i < n && j < m)
{
if (a[i] < b[j])
c[k++] = a[i++];
else
c[k++] = b[j++];
}
/*如果b数组取完,就把a数组剩下的元素直接合并到c数组中*/
while (i < n)
c[k++] = a[i++];
/*如果a数组取完,就把b数组剩下的元素直接合并到c数组中*/*/
while (j < m)
c[k++] = b[j++];
}


解决了上面的合并有序数列问题,再来看归并排序,其的基本思路就是将数组分成二组A,B,如果这二组组内的数据都是有序的,那么就可以很方便的将这二组数据进行排序。如何让这二组组内数据有序了?

可以将A,B组各自再分成二组。依次类推,当分出来的小组只有一个数据时,可以认为这个小组组内已经达到了有序,然后再合并相邻的二个小组就可以了。这样通过先递归的分解数列,再合并数列就完成了归并排序。【此段出处

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

归并排序的时间复杂度为最好、最坏、平均都为O(nlogn),空间复杂度为O(1),由于都是相邻元素作比较,因此是稳定的。

3.归并排序递归实现

public class MergeSort {
/**
* 归并排序
* 简介:将两个(或两个以上)有序表合并成一个新的有序表 即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列
* 时间复杂度为O(nlogn)
* 稳定排序方式
* @param nums 待排序数组
* @return 输出有序数组
*/
public static int[] sort(int[] nums, int low, int high) {
int mid = (low + high) / 2;
if (low < high) {
// 左边
sort(nums, low, mid);
// 右边
sort(nums, mid + 1, high);
// 左右归并
merge(nums, low, mid, high);
}
return nums;
}

/**
* 将数组中low到high位置的数进行排序
* @param nums 待排序数组
* @param low 待排的开始位置
* @param mid 待排中间位置
* @param high 待排结束位置
*/
public static void merge(int[] nums, 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 (nums[i] < nums[j]) {
temp[k++] = nums[i++];
} else {
temp[k++] = nums[j++];
}
}

// 把左边剩余的数移入数组
while (i <= mid) {
temp[k++] = nums[i++];
}

// 把右边边剩余的数移入数组
while (j <= high) {
temp[k++] = nums[j++];
}

// 把新数组中的数覆盖nums数组
for (int k2 = 0; k2 < temp.length; k2++) {
nums[k2 + low] = temp[k2];
}
}


4.归并排序非递归实现

非递归的思路【出处

1.从归并段的长度为1开始,一次使归并段的长度变为原来的2倍。

2.在每趟归并的过程中,要注意处理归并段的长度为奇数和 最后一个归并段的长度和前面的不等的情况,需要做一下处理

// 程序边界的处理非常重要
while (len <= t.length) {
for (int i = 0; i + len <= t.length - 1; i += len * 2) {
//    System.out.println("len="+len);
low = i;
mid = i + len - 1;
high = i + len * 2 - 1;
if (high > t.length - 1)
high = t.length - 1;
merge(t, i, mid, high);
}
//长度加倍
len += len;
}
return true;
}


完整代码:

public class MergeSort {
/**
* 二路归并排序的递归算法-入口
*
* @param <T>
* @param t
* @return
*/
public static <T extends Comparable> boolean mergeSortRecursive(T[] t) {
if (t == null || t.length <= 1)
return true;
MSortRecursive(t, 0, t.length - 1);
return true;
}
/**
* 二路归并排序的递归算法-递归主体
*
* @param <T>
* @param t
* @param low
* @param high
* @return
*/
private static <T extends Comparable> boolean MSortRecursive(T[] t,
int low, int high) {
if (t == null || t.length <= 1 || low == high)
return true;
int mid = (low + high) / 2;
MSortRecursive(t, low, mid);
MSortRecursive(t, mid + 1, high);
merge(t, low, mid, high);
return true;
}
public static <T extends Comparable> boolean mergeSortNonRecursive(T[] t) {
if (t == null || t.length <= 1)
return true;
int len = 1;
int low = 0;
int mid;
int high;
// 程序边界的处理非常重要 while (len <= t.length) { for (int i = 0; i + len <= t.length - 1; i += len * 2) { // System.out.println("len="+len); low = i; mid = i + len - 1; high = i + len * 2 - 1; if (high > t.length - 1) high = t.length - 1; merge(t, i, mid, high); } //长度加倍 len += len; } return true; }
/**
* 将两个归并段合并成一个归并段
*
* @param <T>
* @param t
* @param low
* @param mid
* @param high
* @return
*/
private static <T extends Comparable> boolean merge(T[] t, int low,
int mid, int high) {
T[] s = t.clone();// 先复制一个辅助数组
int i, j, k;// 三个指示器,i指示t[low...mid],j指示t[mid+1...high],k指示s[low...high]
for (i = low, j = mid + 1, k = low; i <= mid && j <= high; k++) {
if (t[i].compareTo(t[j]) <= 0) {
s[k] = t[i++];
} else {
s[k] = t[j++];
}
}
// 将剩下的元素复制到s中
if (i <= mid) {
for (; k <= high; k++) {
s[k] = t[i++];
}
} else {
for (; k <= high; k++) {
s[k] = s[j++];
}
}
for (int m = low; m <= high; m++) {// 将辅助数组中的排序好的元素复制回原数组
t[m] = s[m];
}
return true;
}
public static void main(String[] args) {
Integer[] arr = new Integer[] { 2, 3, 6, 8, 9, 2, 0, 1 };
long startTime = System.currentTimeMillis(); // 获取开始时间
mergeSortRecursive(arr);
long endTime = System.currentTimeMillis(); // 获取开始时间
System.out.println("执行时间:" + (endTime - startTime));
for (int i : arr) {
System.out.println(i);
}
startTime = System.currentTimeMillis(); // 获取开始时间
mergeSortNonRecursive(arr);
endTime = System.currentTimeMillis(); // 获取开始时间
System.out.println("执行时间:" + (endTime - startTime));
for (int i : arr) {
System.out.println(i);
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息