归并排序两种方法(递归和非递归)
时间:2020-10-08 19:03:04
收藏:0
阅读:21
归并排序的两种方法
递归(自顶向下)
-
将一个大的无序的数组分成两个,然后拿出其中一个再分为两个···一直这样下取,直到最后剩下一个,那么这只包含一个元素的数组就是有序的了,然后将两个这样的数组通过merge方法有序的合并,一级一级,直到最开始的两个合并了就排序完了
-
先实现一个merge用来将两个有序的数组再有序合并在一起,然后通过mergeSort方法递归调用,不断将数组分割,有序合并,最后排序完成
-
代码实现
import java.util.Arrays; /** * @Description: 归并排序第一种 * @Author: LinZeLiang * @Date: 2020-10-06 */ public class MergeSortFirst { public static void main(String[] args) { int[] a = {9, 8, 7, 6, 5, 4, 3, 2, 1, 0}; mergeSort(a, 0, 9); System.out.println(Arrays.toString(a)); } /** * * @param a 待排序数组 * @param left 指向第一个元素 * @param right 指向最后一个元素 */ public static void mergeSort(int[] a, int left, int right) { if (left < right) { int mid = (left + right) / 2; mergeSort(a, left, mid); mergeSort(a, mid + 1, right); merge(a, left, mid, right); } } /** * 将左右两边有序子数组合并 * * @param a 待排序数组 * @param left 指向第一个索引 * @param mid 指向中间的索引 * @param right 指向最后一个索引 */ public static void merge(int[] a, int left, int mid, int right) { //创建辅助数组 int[] arr = new int[right - left + 1]; //左指针 int l = left; //右指针 int r = mid + 1; //临时数组的指针 int k = 0; //将两个数组有序合并到辅助数组中 while (l <= mid && r <= right) { if (a[l] < a[r]) { arr[k++] = a[l++]; } else { arr[k++] = a[r++]; } } //将剩下的还没有存入arr的数按顺序存入临时数组 while (l <= mid) { arr[k++] = a[l++]; } while (r <= right) { arr[k++] = a[r++]; } //将排序好的临时数组替换掉原来的数组 for (int i = 0; i < k; i++) { a[left++] = arr[i]; } } }
非递归(自底向上)
-
非递归就是先从最底层开始先把数组分割成每个子数组包含两个元素,利用merge方法,对这个子数组进行排序,等这些子数组都排序完成,然后下一轮就是将数组分割成每个子数组包含4个元素,再排序···按照1、2、4、8···顺序来分割,直到最后只剩下一个数组就排序完了
-
代码实现
import java.util.Arrays; /** * @Description: 归并排序第二种 * @Author: LinZeLiang * @Date: 2020-10-07 */ public class MergeSortSecond { public static void main(String[] args) { int[] a = {9, 8, 7, 6, 5, 4, 3, 2, 1, 0}; mergeSort(a); System.out.println(Arrays.toString(a)); } /** * 归并排序 * * @param a 待排序数组 */ public static void mergeSort(int[] a) { //先求出数组的长度 int len = a.length; //子数组大小分别为1、2、4、8···,刚开始是1然后2(自底向上,即先按照一个数组两个个元素排序然后四个) //i为1时是两个元素一个数组,2时四个元素一个数组,进行排序 for (int i = 1; i < len; i += i) { //left为0从最左端开始 int left = 0; //mid和right是固定的 int mid = left + i - 1; int right = mid + i; //进行合并,将数组两两有序合并 while (right < len) { //merge方法和递归用的一样 merge(a, left, mid, right); //移到下一组继续合并 left = right + 1; mid = left + i - 1; right = mid + i; } //由于并不是所有的数组大小都刚好一样大,最后一组不一定满了,所以对最后一组再来合并 if (left < len && mid < len) { merge(a, left, mid, len - 1); } } /* //对上面的进行优化 //这一个循环没有变化 for (int i = 1; i < len; i += i) { //这个循环替换了上面的一大串代码 //left为0也从最左边开始 //left < len - 1就是循环终止条件,写出过程推断一下不难发现 //left += (i + i)就是跳到下一个数组继续合并数组去(不难发现,数组的跨度大小总是当前i的两倍) for (int left = 0; left < len - i; left += (i + i)) { //left + i - 1代表mid //Math.min(left + i -1 + i, len - 1)代表合并数组的右端right,因为最后一个可能没填满,所以通过min找到最右端即可 merge(a, left, left + i - 1, Math.min(left + i + i - 1, len - 1)); } } */ } /** * 将左右两边有序子数组合并 * * @param a 待排序数组 * @param left 指向第一个索引 * @param mid 指向中间的索引 * @param right 指向最后一个索引 */ public static void merge(int[] a, int left, int mid, int right) { int[] arr = new int[right - left + 1]; int l = left; int r = mid + 1; int k = 0; while (l <= mid && r <= right) { if (a[l] < a[r]) { arr[k++] = a[l++]; } else { arr[k++] = a[r++]; } } while (l <= mid) { arr[k++] = a[l++]; } while (r <= right) { arr[k++] = a[r++]; } k = 0; while (k < arr.length) { a[left++] = arr[k++]; } } }
评论(0)