归并排序:只能对两段有序的数组进行归并,归并从更大的有序数组
归并时需要两个下标,分别是指向首部的i,以及指向mid+1的j。
初始化过后,对i和j的元素进行比较,小的放入temp之中。
j<1,将元素1放入temp,k++.j++
在对i和j进行比较,以此往复
当j>r时,直接将剩余部分放到temp中
然后将temp拷贝到arr中
对于中间下标 int mid = (r+l)/2;当r、l两个都为比较大的值时,可能r+l会造成容量溢出超过了int的值范围。因此 使用int mid = l+(r-l)/2;
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 = (r + l) / 2;
int mid = l + (r - l) / 2;
Merge(arr, temp, l, mid, r);
}
// 将arr[l...mid]和arr[mid+1...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;
//左右半边都有元素(将小的放到temp数组中)
while(i<=mid && j <= r)
{
if(arr[i] < arr[j])
temp[k++] = arr[i++];
else //arr[i] >= arr[j]
temp[k++] = arr[j++];
}
//左半边还有元素,右半边用尽(取左半边的元素)
while (i <= mid)
temp[k++] = arr[i++];
//右半边还有元素,左半边用尽(取右半边的元素)
while (j <= r)
temp[k++] = arr[j++];
//将temp数组拷贝回给arr数组,完成arr排序
for (int z = l; z <= r; z++)
arr[z] = temp[z];
}
}
保证左右有序
递归分割
对数组的中间进行分割,变成两个数组,在对两个数组的中间进行分割
6 3 2 8 1 7 5 7
6 3 2 8 | 1 7 5 4
6 3 | 2 8 | 1 7 | 5 4
6 | 3 | 2 | 8 | 1 | 7 | 5 | 4
然后现在都变成了一个个单独的数组,进行比较:
3 6 | 2 8 | 1 7 | 4 5 |
2 3 6 8 | 1 4 5 7
1 2 3 4 5 6 7 8 完成排序
代码递归实现:
首先我们需要将一整个数组一直递归分割,分成一个个的单个数组。
上面代码中 Sort函数可以进行分割,仅分割一次
private static void Sort(int[] arr,int[] temp,int l,int r)
{
//int mid = (r + l) / 2;
int mid = l + (r - l) / 2;
Merge(arr, temp, l, mid, r);//排序
}
我们在Sort里调用Sort自己就可以递归调用
什么时候结束递归呢?当l=>r的时候数组中就只有一个元素了那时进行返回。
调用Merge()进行排序,例如 6 3 2 8变成3 6 2 8。在调用上一层的Merge() 2 3 6 8
private static void Sort(int[] arr,int[] temp,int l,int r)
{
if (l >= r) return;
int mid = l + (r - l) / 2;
//Console.WriteLine("mid = {0}", mid);Sort(arr, temp, l, mid); //将左半边排序
Sort(arr, temp, mid + 1, r); //将右半边排序
Merge(arr, temp, l, mid, r); //归并结果
}
过程如下:
当Sort(0,0)时,l=r 因此返回。执行Sort(arr,temp,mid+1,r)。也就是Sort(0,1)下的Sort(1,1),同理因为1=1,l=r。return。执行Merge(arr,temp,0,0,1)也就是 5 4排序后int[ ] arr ={4,5,3,2,1};
Sort(0,1)执行完毕
执行Sort(0,2)下的Sort(arr,tem,mid+l,r),Sort(2,2)返回执行Merge(arr,temp,0,1,2)
int[ ] arr ={4,5,3,2,1};变成{3,4,5,2,1}
时间复杂度
一组无序数组:归并排序与插入排序比较:
可以看出归并比插入快很多。O(nlgn)比O(n2)快很多
一组几乎有序的数组
归并慢了一些,但也没慢多少。O(nlgn)慢于O(n)