归并排序,是创建在归并操作上的一种有效的排序算法。算法是采用分治法(Divide and Conquer)的一个非常典型的应用,且各层分治递归可以同时进行。归并排序思路简单,速度仅次于快速排序,为稳定排序算法,一般用于对总体无序,但是各子项相对有序的数列。
1.主要思路:归并排序是分治思想,分治模式在每一层递归上有三个步骤:
- 分解:将n个元素分成个含n/2个元素的子序列。
- 解决:用合并排序法对两个子序递归的排序。
- 合并:合并两个已排序的子序列已得到排序结果。
2.实现逻辑
2.1 迭代法
① 申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
② 设定两个指针,最初位置分别为两个已经排序序列的起始位置
③ 比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
④ 重复步骤③直到某一指针到达序列尾
⑤ 将另一序列剩下的所有元素直接复制到合并序列尾
2.2 递归法
① 将序列每相邻两个数字进行归并操作,形成floor(n/2)个序列,排序后每个序列包含两个元素
② 将上述序列再次归并,形成floor(n/4)个序列,每个序列包含四个元素
③ 重复步骤②,直到所有元素排序完毕
3. 动图演示
根据master 公式
master公式(也称主方法)是用来利用分治策略来解决问题经常使用的时间复杂度的分析方法,(补充:分治策略的递归解法还有两个常用的方法叫做代入法和递归树法,以后有机会和亲们再唠),众所周知,分治策略中使用递归来求解问题分为三步走,分别为分解、解决和合并,所以主方法的表现形式:
T [n] = aT[n/b] + f (n)(直接记为T [n] = aT[n/b] + T (N^d))
其中 a >= 1 and b > 1 是常量,其表示的意义是n表示问题的规模,a表示递归的次数也就是生成的子问题数,b表示每次递归是原来的1/b之一个规模,f(n)表示分解和合并所要花费的时间之和。
解法:
①当d<logb a时,时间复杂度为O(n^(logb a))
②当d=logb a时,时间复杂度为O((n^d)*logn)
③当d>logb a时,时间复杂度为O(n^d)
为 2T(N/2)规模
为 O(1)复杂度
为O(N)
所以 T(N) = 2T (N/2) +O (N)
a=2 b=2 d=1 满足 d=logb a
所以 时间复杂度为O((n^d)*logn)= O(N*logn)
复杂度分析
平均时间复杂度:O(nlogn)
最佳时间复杂度:O(n)
最差时间复杂度:O(nlogn)
空间复杂度:O(n)
排序方式:In-place
稳定性:稳定
代码实现:
public class MergeSort {
public static void main(String[] args) {
int[] arr = {1,4,7,8,3,6,9};
sort(arr,0,arr.length-1);
print(arr);
}
private static void sort(int[] arr, int left, int right) {
if (left == right) return;
// 分两半
// 也可以写成 int mid = left + ((right - left) >> 1);
// 注意 不能写成 int mid = left + (right - left) >> 1; 因为 >> 运算符的优先级 低于 +
int mid = left + (right - left)/2;
// 左排序
sort(arr,left,mid);
// 右排序
sort(arr,mid+1,right);
merge(arr,left,mid+1,right);
}
private static void merge(int[] arr,int leftPtr,int rightPtr,int rightBound) {
int mid= rightPtr - 1;
int[] temp = new int[rightBound - leftPtr + 1];
int i=leftPtr;
int j = rightPtr;
int k=0;
while (i <= mid && j <= rightBound){
// i++为先参与运算,再赋值,例如arr[i++],先计算arr[i],再赋值i=i+1;
temp[k++] = arr[i] <= arr[j] ? arr[i++] : arr[j++];
}
while (i <= mid) temp[k++]=arr[i++];
while (j <= rightBound) temp[k++]=arr[j++];
// 将 temp 数组放入到 arr 数组中
for (int m = 0; m < temp.length; m++) arr[leftPtr + m] = temp[m];
}
static void swap(int[] arr,int i,int j){
int temp=arr[i];
arr[i]=arr[j];
arr[j]=temp;
}
static void print(int[] arr){
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i]+" ");
}
}
}