排序规则:
1、申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
2、设定两个指针,最初位置分别为两个已经排序序列的起始位置
3、比较两个指针所指向的元素,选择相对小的元素放入合并空间,并移动指针到下一位置。
4、重复步骤3直到指针达到序列末尾
5、将另一序列剩下的所有元素直接复制到合并序列末尾
using System; using System.Collections.Generic; using System.Text; namespace 排序算法 { class MergeSort1 { public static void Sort(int[] arr) { int n = arr.Length; int[] temp = new int[n]; Sort(arr, temp, 0, n - 1); } private static void Sort(int[] arr, int[] temp, int l,int r) { int mid = l + (r - l) / 2; Merge(arr, temp, l, mid, r); } private static void Merge(int[] arr, int[] temp, int l, int mid, int r) { int i = l; int j = mid + 1; int k = l; //左右半边都有元素 while(i <= mid && j <= r) { if(arr[i] < arr[j]) { temp[k++] = arr[i++]; } else { temp[k++] = arr[j++]; } } //左半边还有元素,右半边用尽 while(i <= mid) { temp[k++] = arr[i++]; } //右半边还有元素,左半边用尽 while(j <= r) { temp[k++] = arr[j++]; } //将temp数组覆盖到arr数组中 for(int z = l; z <= r; z++) { arr[z] = temp[z]; } } } }
上面代码中左右两边都必须有序才能排序。如何保证左右有序?
递归分割:
将数组拆分成许多小的子数组每个子数组中只有两个元素,这样子数组的左右半边只有一个元素
当左右半边只有一个元素时,即可看作左右有序
using System; using System.Collections.Generic; using System.Text; namespace 排序算法 { class MergeSort2 { public static void Sort(int[] arr) { int n = arr.Length; int[] temp = new int[n]; Sort(arr, temp, 0, n - 1); } //递归分割 private static void Sort(int[] arr, int[] temp, int l,int r) { if(l >= r) { return; } int mid = l + (r - l) / 2; Sort(arr, temp, l, mid); Sort(arr, temp, mid + 1, r); Merge(arr, temp, l, mid, r); } private static void Merge(int[] arr, int[] temp, int l, int mid, int r) { int i = l; int j = mid + 1; int k = l; //左右半边都有元素 while(i <= mid && j <= r) { if(arr[i] < arr[j]) { temp[k++] = arr[i++]; } else { temp[k++] = arr[j++]; } } //左半边还有元素,右半边用尽 while(i <= mid) { temp[k++] = arr[i++]; } //右半边还有元素,左半边用尽 while(j <= r) { temp[k++] = arr[j++]; } //将temp数组覆盖到arr数组中 for(int z = l; z <= r; z++) { arr[z] = temp[z]; } } } }
优化归并排序:
1、对小规模的子数组时使用插入排序,因为递归会在小规模数组中调用过于频繁
2、子数组本来就是有序的,就不再进行归并操作就是子数组的左半边最后一个元素小于右半边第一个元素
namespace 排序算法 { /* * 时间复杂度O(nlogn) * 空间复杂度O(n)需要借助一个同样长度的辅助数组进行排序。 * 1、对小规模的子数组时使用插入排序,因为递归会在小规模数组中调用过于频繁 * 2、子数组本来就是有序的,就不再进行归并操作就是子数组的左半边最后一个元素小于右半边第一个元素 */ class MergeSort { public static void Sort(int[] arr) { int n = arr.Length; int[] temp = new int[n]; Sort(arr, temp, 0, n - 1); } //递归分割 private static void Sort(int[] arr, int[] temp, int l,int r) { //优化点1 if(r - l + 1 <= 15) { InsertSort(arr, l, r); return; } int mid = l + (r - l) / 2; Sort(arr, temp, l, mid); Sort(arr, temp, mid + 1, r); // 优化点2 if(arr[mid] > arr[mid + 1]) { Merge(arr, temp, l, mid, r); } } private static void Merge(int[] arr, int[] temp, int l, int mid, int r) { int i = l; int j = mid + 1; int k = l; //左右半边都有元素 while(i <= mid && j <= r) { if(arr[i] < arr[j]) { temp[k++] = arr[i++]; } else { temp[k++] = arr[j++]; } } //左半边还有元素,右半边用尽 while(i <= mid) { temp[k++] = arr[i++]; } //右半边还有元素,左半边用尽 while(j <= r) { temp[k++] = arr[j++]; } //将temp数组覆盖到arr数组中 for(int z = l; z <= r; z++) { arr[z] = temp[z]; } } //插入排序 public static void InsertSort(int[] arr, int l, int r) { for (int i = l + 1; i <= r; i++) { int e = arr[i]; int j; for (j = i; j > l; j--) { if (e < arr[j - 1]) { arr[j] = arr[j - 1]; } else { break; } } arr[j] = e; } } } }