一、需求
- 利用归并排序实现一个数组的升序排列,一般来说要求数组满足[左数组 | 右数组];
- 其中左、右数组是有序的,例如[2,8,9,10,4,5,6,7],其中左数组为[2,8,9,10],右数组为[4,5,6,7];
二、归并排序
2.1 思路分析
- 原理就是合并两个有序数组,可以利用三指针来解决;
2.2 代码实现
class Solution {
public static void merge(int[] arr,int left,int mid,int right) {
//初始化左、右子数组长度
int leftSize = mid - left;
int rightSize = right - mid + 1;
//新建左、右子数组
int[] leftArr = new int[leftSize];
int[] rightArr = new int[rightSize];
//初始化左、右子数组
for(int i = 0; i < leftSize; i++) {
leftArr[i] = arr[i];
}
for(int j = 0; j < rightSize; j++) {
rightArr[j] = arr[j+mid];
}
//新建三指针
int i = 0;
int j = 0;
int k = 0;
//开始合并有序数组
while(i < leftSize && j < rightSize) {
if(leftArr[i] < rightArr[j]) {
arr[k] = leftArr[i];
i++;
k++;
} else {
arr[k] = rightArr[j];
j++;
k++;
}
}
while(i < leftSize) {
arr[k] = leftArr[i];
i++;
k++;
}
while(j < rightSize) {
arr[k] = rightArr[j];
j++;
k++;
}
}
public static void main(String[] args) {
int[] arr = {2,8,9,10,4,5,6,7};
merge(arr,0,4,7);
for(int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
}
三、归并排序+分治法
3.1 思路分析
- 上面的实现中要求待排序数组的左、右子数组是有序的,能不能去除这个限制条件,即能不能利用归并排序使得任意一个无序数组是有序的;
- 这就需要用到分治思想,如图所示,通过将无序数组逐步分解成子数组,一直分解到数组中只有一个值为止,然后就合并子数组,一直返回到最上层;
- 归并排序的时间复杂度为,空间复杂度为;
3.2 代码实现
class Solution {
/**
* 该方法是合并两个有序数组,即归并思想
* 假定左子数组区间[left,mid),右子数组区间[mid,right]
* @param arr
* @param left
* @param mid
* @param right
*/
public static void merge(int[] arr,int left,int mid,int right) {
//初始化左、右子数组长度
int leftSize = mid - left;
int rightSize = right - mid + 1;
//新建左、右子数组
int[] leftArr = new int[leftSize];
int[] rightArr = new int[rightSize];
//初始化左、右子数组
for(int i = left; i < mid; i++) {
leftArr[i-left] = arr[i];
}
for(int j = mid; j <= right; j++) {
rightArr[j-mid] = arr[j];
}
//新建三指针
int i = 0;
int j = 0;
int k = left;
//开始合并有序数组
while(i < leftSize && j < rightSize) {
if(leftArr[i] < rightArr[j]) {
arr[k] = leftArr[i];
i++;
k++;
} else {
arr[k] = rightArr[j];
j++;
k++;
}
}
while(i < leftSize) {
arr[k] = leftArr[i];
i++;
k++;
}
while(j < rightSize) {
arr[k] = rightArr[j];
j++;
k++;
}
}
/**
* 该方法用来实现对无序数组的排序
* 假定左子数组区间[left,mid],右子数组区间[mid+1,right]
* @param arr
* @param left
* @param right
*/
public static void mergeSort(int[] arr,int left,int right) {
if(left == right) return;
int mid = left + (right - left)/2;
mergeSort(arr,left,mid);
mergeSort(arr,mid+1,right);
merge(arr,left,mid+1,right);
}
public static void main(String[] args) {
int[] arr = {6,8,9,10,1,5,2,7};
mergeSort(arr,0,7);
//merge(arr,0,4,7);
for(int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
}
四、归并排序+分治法2
4.1 思路分析
- 这一次修改边界条件做一下,将M前移一位;
4.2 代码实现
class Solution {
/**
* 该方法是合并两个有序数组,即归并思想
* 假定左子数组区间[left,mid],右子数组区间[mid+1,right]
* @param arr
* @param left
* @param mid
* @param right
*/
public static void merge(int[] arr,int left,int mid,int right) {
//初始化左、右子数组长度
int leftSize = mid - left + 1;
int rightSize = right - mid ;
//新建左、右子数组
int[] leftArr = new int[leftSize];
int[] rightArr = new int[rightSize];
//初始化左、右子数组
for(int i = left; i <= mid; i++) {
leftArr[i-left] = arr[i];
}
for(int j = mid+1; j <= right; j++) {
rightArr[j-mid-1] = arr[j];
}
//新建三指针
int i = 0;
int j = 0;
int k = left;
//开始合并有序数组
while(i < leftSize && j < rightSize) {
if(leftArr[i] < rightArr[j]) {
arr[k] = leftArr[i];
i++;
k++;
} else {
arr[k] = rightArr[j];
j++;
k++;
}
}
while(i < leftSize) {
arr[k] = leftArr[i];
i++;
k++;
}
while(j < rightSize) {
arr[k] = rightArr[j];
j++;
k++;
}
}
/**
* 该方法用来实现对无序数组的排序
* 假定左子数组区间[left,mid],右子数组区间[mid+1,right]
* @param arr
* @param left
* @param right
*/
public static void mergeSort(int[] arr,int left,int right) {
if(left == right) return;
int mid = left + (right - left)/2;
mergeSort(arr,left,mid);
mergeSort(arr,mid+1,right);
merge(arr,left,mid,right);
}
public static void main(String[] args) {
int[] arr = {6,8,9,10,1,5,2,7};
mergeSort(arr,0,7);
//merge(arr,0,4,7);
for(int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
}
4.3 重载merge方法试一试
class Solution {
/**
* 该方法是合并两个有序数组,即归并思想
* 假定左子数组区间[left,mid],右子数组区间[mid+1,right]
* @param arr
* @param left
* @param mid
* @param right
*/
public static void merge(int[] arr,int left,int mid,int right) {
//初始化左、右子数组长度
int leftSize = mid - left + 1;
int rightSize = right - mid ;
//新建左、右子数组
int[] leftArr = new int[leftSize];
int[] rightArr = new int[rightSize];
//初始化左、右子数组
for(int i = left; i <= mid; i++) {
leftArr[i-left] = arr[i];
}
for(int j = mid+1; j <= right; j++) {
rightArr[j-mid-1] = arr[j];
}
//新建三指针
int i = 0;
int j = 0;
int k = left;
//开始合并有序数组
while(i < leftSize && j < rightSize) {
if(leftArr[i] < rightArr[j]) {
arr[k] = leftArr[i];
i++;
k++;
} else {
arr[k] = rightArr[j];
j++;
k++;
}
}
while(i < leftSize) {
arr[k] = leftArr[i];
i++;
k++;
}
while(j < rightSize) {
arr[k] = rightArr[j];
j++;
k++;
}
}
/**
* 该方法更具一般性,不用传入中间值mid
* @param arr
* @param left
* @param right
*/
public static void merge(int[] arr, int left, int right) {
//获取中间值
int mid = left + (right - left) / 2;
// 初始化左、右子数组长度
int leftSize = mid - left + 1;
int rightSize = right - mid;
// 新建左、右子数组
int[] leftArr = new int[leftSize];
int[] rightArr = new int[rightSize];
// 初始化左、右子数组
for (int i = left; i <= mid; i++) {
leftArr[i - left] = arr[i];
}
for (int j = mid + 1; j <= right; j++) {
rightArr[j - mid - 1] = arr[j];
}
// 新建三指针
int i = 0;
int j = 0;
int k = left;
// 开始合并有序数组
while (i < leftSize && j < rightSize) {
if (leftArr[i] < rightArr[j]) {
arr[k] = leftArr[i];
i++;
k++;
} else {
arr[k] = rightArr[j];
j++;
k++;
}
}
while (i < leftSize) {
arr[k] = leftArr[i];
i++;
k++;
}
while (j < rightSize) {
arr[k] = rightArr[j];
j++;
k++;
}
}
/**
* 该方法用来实现对无序数组的排序
* 假定左子数组区间[left,mid],右子数组区间[mid+1,right]
* @param arr
* @param left
* @param right
*/
public static void mergeSort(int[] arr,int left,int right) {
if(left == right) return;
int mid = left + (right - left)/2;
mergeSort(arr,left,mid);
mergeSort(arr,mid+1,right);
merge(arr,left,right);
}
public static void main(String[] args) {
int[] arr = {6,8,9,10,1,5,2,7};
mergeSort(arr,0,7);
//merge(arr,0,4,7);
for(int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
}