您的位置:首页 > 编程语言 > Java开发

排序算法之归并排序

2017-08-16 14:53 423 查看
归并排序的主要思路分为两个部分:1.分治,2.归并。算法的平均时间复杂度为O(nlogn),需要辅助空间O(n)。

1.分治需要将数组分为两个部分,针对每个部分进行归并排序,待两个子数组都有序后(归并的条件),进行归并。

2.归并的函数需要三个数组位置索引,数组的起始位置L,右子数组的开始位置M(作为左子数组和右子数组的分界线),数组的结束位置R。首先需要将原数组根据三个索引拆分成左子数组和右子数组,从左子数组、右子数组、原数组的第一个位置开始,若左子数组指向元素小于右子数组指向元素,则将左子数组的元素赋值给原数组,左子数组的索引和原数组的索引分别后移1位。否则,将右子数组的索引和原数组的索引后移一位。一旦出现某个子数组越界,则证明该子数组中的元素已经全部填充进原数组中了。只需将另外一个子数组的剩余元素填入原数组中即可,此时整个数组即为有序了。

/**
* 归并排序:需要辅助空间,O(n),平均时间复杂度O(nlogn)
* 主要思想:1.分治 2.归并
*
*/
public class Solution {

/**
* 归并,L,M,R代表待归并的范围,将数组分为两个部分,并且此时假设L-M-1,M-R是排好序的
* 即归并的过程必须保证数组两边是有序的
* @param arr 数组
* @param L 数组的起始位置
* @param M 数组的中间位置,不一定是中位数
* @param R 数组的结束位置
*
*/
public void merge(int[] arr, int L, int M, int R) {
//将原数组拆分成两个子数组,需要额外申请内存空间
//1.确定左、右子数组的长度
int leftLength = M - L;
int rightLength = R - M + 1;

//创建两个子数组
int[] leftArr = new int[leftLength];
int[] rightArr = new int[rightLength];

//从原数组中为子数组赋值
for (int i = L; i < M; i++) {
leftArr[i - L] = arr[i];
}

for (int i = M; i <= R; i++) {
rightArr[i - M] = arr[i];
}

//利用这两个子数组对原数组元素进行调整,形成排序的数组
//是对两个已经排好序的子数组的合并成一个有序数组的过程
int i = 0;//指向左子数组的第一个元素
int j = 0;//指向右子数组的第一个元素
int k = L;//指向原数组的起始位置

//在保证子数组不越界的情况下
while (i < leftLength && j < rightLength) {
if (leftArr[i] < rightArr[j]) {
//如果左子数组的元素值较小,则赋值到原数组中
arr[k] = leftArr[i];
i++;
k++;
} else {
//如果右子数组的元素值较小,则赋值到原数组中
arr[k] = rightArr[j];
j++;
k++;
}
}

//跳出循环时
while (i < leftLength) {
arr[k] = leftArr[i];
i++;
k++;
}

while (j < rightLength) {
arr[k] = rightArr[j];
j++;
k++;
}

}

/**
* 归并排序,先进行分治,在排序
* @param arr 数组
* @param L 数组的起始位置
* @param R 数组的结束位置
*/
public void merge_Sort(int[] arr, int L, int R) {

if (arr == null || L < 0 || R > arr.length - 1) {
return;
}

//递归的结束条件
if(L == R) {
return;
}

int M = (L + R) / 2;
//递归地对前半部分进行归并排序
merge_Sort(arr, L, M);
//递归地对后半部分进行归并排序
merge_Sort(arr, M + 1, R);
//将已经排好序的两部分合并起来
merge(arr, L, M + 1, R);
}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息