算法自学笔记:归并排序(1)

归并排序(merge sort)是一种利用分治思想的排序算法,它的时间复杂度在所有完全依靠交换进行排序的排序算法中是最快的

归并排序基本过程:
1 把数组分为左右两半,再递归对子数组进行二分,直到每个子数组只剩下一个元素。
2 对数组进行两两合并,合并方法具体如下:

  1. 创建一个辅助数组aux并把原数组的元素依次存储在辅助数组
  2. 变量j,k指向辅助数组中对应原数组两部分([low, mid]和[mid + 1, high])的开头,变量i指向原数组,从0开始
  3. 判断aux[j]和aux[k]大小,将较小的元素放入a[i]位置,并将aux j或者k索引加一
  4. 重复操作直到遍历整个数组

3 递归重复合并操作,直到整个数组合并完成

示例程序如下:

public class MergeSort extends Sorting{
	
	public static Comparable[] aux;
	
	public static boolean isSorted(Comparable[] a, int low, int high) {
		for (int i = low + 1; i <= high; i++) {
			if (less(a[i], a[i - 1])) {
				return false;
			}
		}
		return true;
	}
	

	// merge two sorted arrays
	public static void merge (Comparable[] a, int low, int mid, int high) {
		// copy the array to aux
		for (int i = low; i <= high; i++) {
			aux[i] = a[i];
		}
		
		
		// merge the two arrays
		int j = low;
		int k = mid + 1;		
		for (int i = low; i <= high; i++) {
			if (j > mid) {	// left array is exhausted
				a[i] = aux[k++];
			} else if (k > high) {	// right array is exhausted
				a[i] = aux[j++];
			} else if (less(aux[j], aux[k])) {
				a[i] = aux[j++];
			} else {
				a[i] = aux[k++];
			}
		}
	}
	
	
	// recursively operate the merging
	public static void sort (Comparable[] a, int low, int high) {
		if (high <= low) {
			return;
		}
		int mid = (low + high) / 2;
		sort(a, low, mid);	// sort the left hand side
		sort(a, mid + 1, high);	// sort the right hand side
		merge(a, low, mid, high);	// merge the two parts of array
	}
	
	
	// allocate memory for the aux[] only once
	public static void sort (Comparable[] a) {
		aux = new Comparable[a.length];
		sort(a, 0, a.length - 1);
	}
	
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Integer[] test = new Integer[] {2, 4, 5, 1, 11, 7, 9, 6, 10, 8, 3};
		sort(test);
		show(test);
	}

}

在该程序中,merge 方法把两有序数组合并为一个数组,sort(Comparable[] a, int low, int high)方法对数组进行二分,并在分散后进行合并。

sort(Comparable[] a)方法用于预先划定辅助数组aux的大小,注意aux一定要在最开始时创建好,一个常见错误是在merge方法里创建aux,这么做会导致在递归中创建大量数组,占用空间

归并排序时间复杂度O(NlogN), 证明:
由程序可知:O(N) = 2O(N / 2) + N // 两个子数组时间复杂度加一次合并操作
O(1) = 0

O(N) / N = 2(O(N / 2) / N) + 1
= O(N / 2) / (N / 2) + 1
所以: O(N / 2) / N = O(N / 4) / (N / 4) + 1
O(N) / N = O(N / 4) / (N / 4) + 2
假设N为偶数
O(N) / N = O(N / N) / (N / N) + logN
O(N) = NlogN

归并排序空间复杂度O(N) (使用一个长度为N辅助数组)
归并排序有原地排序的实现,但是此类实现一般过于复杂,平时写程序用不上
(原地排序:排序需要额外空间小于等于clogN)

对归并排序的改进:
1 在数组大小很小时,递归式的归并排序效率会低于基础排序算法(插入排序,选择排序),因此可以在数组长度小于一定值时(如7),把这一步排序改为插入排序。
2 在进行合并前比较第一个子数组最大元素和第二个子数组最小元素,如果第一个子数组最大元素较小,说明两个数组已经有序,不需要再排序

public class MergeSortImproved extends Sorting{
	
	public static Comparable[] aux;
	
	public static void insertionSort(Comparable[] a, int low, int high) {
		for (int i = low; i <= high; i++) {
			for (int j = i; j > 0; j--) {
				if (less(a[j], a[j - 1])) {
					exch(a, j, j - 1);
				} else {
					break;	// end this comparing
				}
			}
		}
	}
	
	public static boolean isSorted(Comparable[] a, int low, int high) {
		for (int i = low + 1; i <= high; i++) {
			if (less(a[i], a[i - 1])) {
				return false;
			}
		}
		return true;
	}
	

	// merge two sorted arrays
	public static void merge (Comparable[] a, int low, int mid, int high) {
		// when the length of array is smaller than 7, use insertion sort instead
		if (high - low <= 7) {
			insertionSort(a, low, high);
		}
		
		
		// copy the array to aux
		for (int i = low; i <= high; i++) {
			aux[i] = a[i];
		}
		
		
		// merge the two arrays
		int j = low;
		int k = mid + 1;	
		
		// test if the order is already correct
		if (less(a[mid], a[k])) {
			return;
		}
		
		for (int i = low; i <= high; i++) {
			if (j > mid) {	// left array is exhausted
				a[i] = aux[k++];
			} else if (k > high) {	// right array is exhausted
				a[i] = aux[j++];
			} else if (less(aux[j], aux[k])) {
				a[i] = aux[j++];
			} else {
				a[i] = aux[k++];
			}
		}
	}
	
	
	// recursively operate the merging
	public static void sort (Comparable[] a, int low, int high) {
		if (high <= low) {
			return;
		}
		int mid = (low + high) / 2;
		sort(a, low, mid);	// sort the left hand side
		sort(a, mid + 1, high);	// sort the right hand side
		merge(a, low, mid, high);	// merge the two parts of array
	}
	
	
	// allocate memory for the aux[] only once
	public static void sort (Comparable[] a) {
		aux = new Comparable[a.length];
		sort(a, 0, a.length - 1);
	}
	
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Integer[] test = new Integer[] {2, 4, 13, 12, 5, 1, 11, 7, 14, 9, 6, 15, 10, 8, 3, 16};
		sort(test);
		show(test);
	}

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值