数据结构笔记_17 归并排序

一、简介

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

二、基本思想

先“分”再“治”。

先将数组分割,再进行有序合并。

三、实例

第一个例子:

在这里插入图片描述

参数及含义解释:

arr : 排序的原始数组

left :左边有序序列的初始索引

mid :中间索引
这个mid 在仅有8个元素时,第一次(0+7) / 2 = 3,即为其最初的下标。所以,mid 就表示左边有序序列的最后一个元素。mid + 1,就表示右边有序序列的第一个元素。

right :右边索引

temp: 做中转的数组

“分”的步骤1:mid = (0+7)/2 = 3,分成了下标left ~ mid和(mid+1) ~ right。即下标0 ~ 3(8,4,5,7) 和 4 ~ 7(1,3,6,2)。

“分”的步骤2:下标0 ~ 3 ,mid = (0 + 3)/2 = 1,分成了8,4和5,7。

“分”的步骤3:mid = (0 + 1)/2 = 0,0 ~ 0 ,分成了:8和4。此时已经递归到最里层了。这时就触发后面的“治”(此处略)。

以最后一次“治”为例:

图1:i 指向数字4,j 指向数字1,temp为临时数组。

“治”的步骤1:先把左右两边(有序)的数据,按照规则填充到temp 数组。直到左右两边的有序序列,有一边处理完毕为止。

1)4和1分别是有序子序列的首元素,二者比较大小。4>1,将1填充进temp 数组。并将j 后移。
2)4和2进行比较,2<4,将2填充进temp,并将j 后移。
3)4和3进行比较,3<4,将3填充进temp,并将j 后移。

在这里插入图片描述
图2:

4)4和6进行比较,4<6,填充进tmep,并将 i 后移。
5)5和6比较,5<6,填充进tmep,并将 i 后移。

“治”的步骤2:把有剩余数据的一边的数据依次全填充到temp。

7和6比较,6<7,填充进temp,并将 i 后移。此时,右侧有序列已经没有元素了。接下来,把有剩余数据的一边的数据(7,8)依次全填充到 temp。

“治”的步骤3:将tmep数组的元素拷贝到arr(并不是每次都拷贝所有)。

在这里插入图片描述

第二个例子:

序列元素个数为奇数时,亦同理
在这里插入图片描述

输出结果:
在这里插入图片描述

分的时候,递归到最里层时,完成分割。之后开始从最里层往外面合。

进入递归,被分开的元素序列中,例如5,3,7被分成了5,3和7。再深入一层把5,3分成了5和3。这就直捣黄龙,递归到底了。开始合!把5和3合成3,5。再退回到上一层递归(5,3和7),对这俩序列进行合并!得到结果:3,5,7。其他的类似。

先深入,再出来,这便是递归的奥义吧。(~ ̄▽ ̄)~

【分、合(递归)全过程】:5,3,7,2,0分为5,3,7和2,0。进入左子树把5,3,7分为5,3和7。再进入左子树分为5和3。触发合并语句:最先把3,5给合了。回溯,再把3,5,7给合了。这时,进入右子树2,0,把2,0分为2和0,触发合并语句,合成了0,2。回溯,合并根节点的左右子树,触发合并语句,合成了0,2,3,5,7。程序执行结束~

代码:

package com.huey.sort;

import java.util.Arrays;

public class MergeSort {

	public static void main(String[] args) {
		int[] arr = { 8, 4, 5, 7, 1, 3, 6, 2 };
		int[] temp = new int[arr.length];// 归并排序需要一个额外空间
		mergeSort(arr, 0, arr.length - 1, temp);
		System.out.println("归并排序后:" + Arrays.toString(arr));
	}

	// 分+合方法
	public static void mergeSort(int[] arr, int left, int right, int[] temp) {
		if (left < right) {// mid 在递归时就是right.mid+1就是left
			int mid = (left + right) / 2;// 中间索引
			// 向左递归 进行分解
			mergeSort(arr, left, mid, temp);
			// 向右递归 进行分解
			mergeSort(arr, mid + 1, right, temp);
			// 到合并【8,4,5,7是栈顶,先合并】
			merge(arr, left, mid, right, temp);
		}
	}

	// 合并的方法
	/**
	 * @param arr   排序的原始数组
	 * @param left  左边有序序列的初始索引
	 * @param mid   中间索引
	 * @param right 右边索引
	 * @param temp  做中转的数组
	 */
	public static void merge(int[] arr, int left, int mid, int right, int[] temp) {
		int i = left;// 初始化i,左边有序序列的初始索引
		int j = mid + 1;// 初始化j,右边有序序列的初始索引
		int t = 0;// 指向temp数组的当前索引

		// (一)
		// 先把左右两边(有序)的数据,按照规则填充到temp 数组
		// 直到左右两边的有序序列,有一边处理完毕为止
		while (i <= mid && j <= right) {// 继续
			// 如果左边的有序序列的当前元素,小于等于右边有序序列的当前元素
			// 即将左边的当前元素,填充到temp数组
			// 然后t++,i++
			if (arr[i] <= arr[j]) {
				temp[t] = arr[i];
				t += 1;
				i += 1;
			} else {// 反之
				temp[t] = arr[j];
				t += 1;
				j += 1;
			}
		}

		// (二)
		// 把有剩余数据的一边的数据依次全填充到temp
		while (i <= mid) {
			// 左边的有序序列还有剩余的元素,就全部填充到temp
			temp[t] = arr[i];
			t += 1;
			i += 1;
		}
		while (j <= right) {
			// 右边的有序序列还有剩余的元素,就全部填充到temp
			temp[t] = arr[j];
			t += 1;
			j += 1;
		}

		// (三)
		// 将tmep数组的元素拷贝到arr
		// 并不是每次都拷贝所有
		t = 0;
		int tempLeft = left;//
		System.out.println("tempLeft = " + tempLeft + ", right = " + right);
		while (tempLeft <= right) {// 第一次合并 tempLeft = 0,right = 1// tempLeft = 2,right = 3 // t= 0,r= 3.......
			// 【最后一次】tempLeft = 0,right = 7
			arr[tempLeft] = temp[t];
			t += 1;
			tempLeft += 1;
		}

	}
}


输出结果:
在这里插入图片描述

速度测试:

八千万:
在这里插入图片描述
大致在11~12s.

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值