【Java 算法】归并排序(递归版&非递归版)

import java.util.Arrays;
import java.util.Stack;

public class 归并排序 {

	public static void main(String[] args) {
		int[] arr = {99, 88, 77, 66, 55, 44, 33, 22, 11};
		mergeSortStack(arr);
		System.out.println(Arrays.toString(arr));
	}
	
	public static void mergeSort(int[] arr) {
		mergeSort(arr, new int[arr.length], 0, arr.length - 1);
	}
	
	private static void mergeSort(int[] arr, int[] temp, int left, int right) {
		if (left < right) { //直至剩余一个时,则弹栈,然后就是从最小两个开始合并
			int mid = (left + right) / 2;
			mergeSort(arr, temp, left, mid);
			mergeSort(arr, temp, mid + 1, right);
			merge(arr, temp, left, mid, right);
		}
	}
	
	private static void merge(int[] arr, int[] temp, int left, int mid, int right) {
		int i = left; //左边范围指针
		int j = mid + 1; //右边范围指针
		int k = 0; //temp 指针
		while (i <= mid && j <= right) {
			if (arr[i] < arr[j]) { //排序,要么左范围数据小,要么右范围数据小
				temp[k++] = arr[i++];
			} else {
				temp[k++] = arr[j++];
			}
		}
		// 两个其中之一有一个没有移动完成,则全部放进 temp 排序
		while (i <= mid) {
			temp[k++] = arr[i++];
		}
		while (j <= right) {
			temp[k++] = arr[j++];
		}
		// 最后移动到 arr
		for (int x = 0; x < k; x++) { //直接小于 k 范围,而不是 temp.length,要的是比较后的元素个数
			arr[left++] = temp[x];
		}
	}
	
	static class Area {
		public int left;
		public int right;
		public boolean isBinary; //重要标识,判断是否已经二分过,没有则继续二分,然后标识 true
		public Area (int left, int right) {
			this.left = left;
			this.right = right;
		}
	}
	
	static class Merge extends Area {
		public int mid;
		public Merge(int left, int mid, int right, boolean isBinary) {
			super(left, right);
			this.mid = mid;
			this.isBinary = isBinary;
		}
	}
	
	public static void mergeSortStack(int[] arr) { //非递归版
		Stack<Area> stack = new Stack<>();
		stack.push(new Merge(0, (arr.length - 1) / 2, arr.length - 1, false)); //初始化最底层合并对象
		int[] temp = new int[arr.length];
		while (! stack.isEmpty()) {
			Area peek = stack.peek(); //归并需要拿出来看,而不是直接取,最后需要合并
			if (peek.isBinary) { //判断是否已二分
				if (peek instanceof Merge) { //此时要么是 Merge 对象,要么是 Area 对象
					merge(arr, temp, peek.left, ((Merge) peek).mid, peek.right);
					stack.pop();
				} else { //Area 直接弹栈
					stack.pop();
				}
			} else {
				int left = peek.left;
				int right = peek.right;
				if (left < right) { //直至剩余一个时,则弹栈,然后就是从最小两个开始合并
					peek.isBinary = true; //标记已二分操作
					int mid = (left + right) / 2;
					// 添加对象入栈时,要按常规递归版倒序入栈,把合并对象先压栈,区域对象后入栈,是为了查看区域对象是否已经二分,没有则继续二分
					// 最关键是,存储的压栈关系,Area <—— Area <—— Merge <—— Area <—— Area <—— Merge <—— Area <—— Area <—— Merge(这个是最开始入栈的合并对象)
					stack.push(new Merge(left, mid, right, true)); //之后的操作一直为是已经二分的状态 true
					stack.push(new Area(mid + 1, right)); //右区间
					stack.push(new Area(left, mid)); //左区间
				} else { //在等于 1 个元素时,直接弹出栈
					stack.pop();
				}
			}
		}
	}

	
}

输出

[11, 22, 33, 44, 55, 66, 77, 88, 99]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

虚妄狼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值