左神算法笔记之基于比较的排序——归并排序及其应用(小和问题、打印逆序对)

一、归并排序

①先将数组分成两半,再分别将左边排好序,右边排好序。

②然后申请一个辅助数组,用两个标志指向左右数组的第一个元素。

③首先两个头元素对比,较小的归入辅助数组,然后将指标往后移,较大的不动,继续比较。

④当一边数组完全归入辅助数组后,因为数组是有序的,另一个数组剩下的元素直接归入辅助数组。

⑤最后将辅助数组copy回原数组,就排好序了。

时间复杂度O(N*logN),额外空间复杂度O(N)

	public mergeSort(int[] arr){
		if(arr == null || arr.length < 2){
			return;
		}
		sortProcess(arr, 0, arr.length-1);
	}
	
	public static void sortProcess(int[] arr, int L, int R){
		//当左节点等于右节点时,不能再分了,返回
		if(L == R){
			return;
		}
		//中间节点
		int mid = L+(R-L)/2;
		//左边排序
		sortProcess(arr, L, mid);
		//右边排序
		sortProcess(arr, mid+1, R);
		//左右两边结合时merge的过程
		merge(arr, L, mid, R);
	}
	
	public static void merge(int[] arr, int L, int mid, int R){
		//申请一个和原来数组长度相同的辅助数组
		int[] help = new int[R-L+1];
		//i代表当前辅助数组的元素下标,初始指向0
		int i = 0;
		//左边排好序的数组指针,初始指向第一个元素
		int p1 = L;
		//右边排好序的数组指针,初始指向第一个元素
		int p2 = mid+1;
		//当左边数组指针还没到mid并且右边数组指针还没到R时
		while(p1 <= mid && p2 <= R){
			//辅助数组里存放p1和p2里较小的数,存放完,较小数的指针往后移一位,较大的不变
			help[i++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++];
		}
		//当p2全部放入数组后,p1继续放进数组
		while(p1 <= mid){
			help[i++] = arr[p1++];
		}
		//当p1全部放入数组后,p2继续放进数组
		while(P2 <= R){
			help[i++] = arr[p2++];
		}
		//将辅助数组排好序的元素拷贝回原数组
		for(i = 0; i < help.length; i++){
			arr[L+i] = help[i];
		}
	}


二、小和问题

求给定所以数的左边小于其自身的数的和

换一个角度想:就是所有大于k的数的小和里都会有k

	public static int smallSum(int[] arr){
		if(arr = null || arr.length < 2){
			return 0;
		}
		return mergeSort(arr, 0, arr.length-1);
	}
	public static int mergeSort(int[] arr, int l, int r){
		if(l == r){
			return 0;
		}
		int mid = L + (R-L)/2;
		return mergeSort(arr, l, mid)
			+ mergeSort(arr, mid+1, r)
			+ merge(arr. l, r);
	}
	public static int merge(int[] arr, int l, int m, int r){
		int[] help = new int[r-l+1];
		int i = 0;
		int p1 = l;
		int p2 = m+1;
		int res = 0;
		while(p1 <= m && p2 <= r){
			res += arr[p1] < arr[p2] ? (r-p2+1)*arr[p1] : 0;
			help[i++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++];
		}
		while(p1 <= m){
			help[i++] = arr[p1++];
		}
		while(p2 <= r){
			help[i++] = arr[p2++];
		}
		for(i = 0; i < help.length; i++){
			arr[l+i] = help[i];
		}
		return res;
	}

三、逆序对问题

如果数组中任意一个数的左边的数比它大,就构成了一对逆序对,求打印逆序对个数。

换一个角度想就是求一个数右边有多少个数比它小

	public static int backWard(int[] arr){
		if(arr = null || arr.length < 2){
			return 0;
		}
		return mergeSort(arr, 0, arr.length-1);
	}
	public static int mergeSort(int[] arr, int l, int r){
		if(l == r){
			return 0;
		}
		int mid = L + (R-L)/2;
		return mergeSort(arr, l, mid)
			+ mergeSort(arr, mid+1, r)
			+ merge(arr. l, r);
	}
	public static int merge(int[] arr, int l, int m, int r){
		int[] help = new int[r-l+1];
		int i = 0;
		int p1 = l;
		int p2 = m+1;
		int res = 0;
		while(p1 <= m && p2 <= r){
			res += arr[p1] > arr[p2] ? (r-p2+1) : 0;
			help[i++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++];
		}
		while(p1 <= m){
			help[i++] = arr[p1++];
		}
		while(p2 <= r){
			help[i++] = arr[p2++];
		}
		for(i = 0; i < help.length; i++){
			arr[l+i] = help[i];
		}
		return res;
	}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值