排序算法之归并排序
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);
}
}
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);
}
}
相关文章推荐
- 排序算法-归并排序
- 常用排序算法(七)归并排序
- 排序算法Python(冒泡、选择、快速、插入、希尔、归并排序)
- 归并排序 -- 高效、稳定的排序算法
- 排序算法--归并排序Java实现 .
- 排序算法之归并排序
- 排序算法复习(Java实现)(二): 归并排序,堆排序,桶式排序,基数排序
- 排序算法系列----归并排序(C++)
- 排序算法之归并排序
- 常用排序算法实现——归并排序
- 排序算法——归并排序
- 排序算法之归并排序
- 【十八】排序算法(四)--归并排序
- 排序算法之归并排序
- 排序算法之归并排序
- 排序算法系列——归并排序
- 排序算法---归并排序
- 排序算法——归并排序
- 排序算法第三篇——归并排序
- 基本排序算法(冒泡排序 选择排序 插入排序 快速排序 归并排序 基数排序 希尔排序)