排序
归并排序
思想
归并排序算法有两个基本的操作,
一个是分,也就是把原数组划分成两个子数组的过程。
一个是治,它将两个有序数组合并成一个更大的有序数组。
它将数组平均分成两部分: center = (left + right)/2,当数组分得足够小时—数组中只有一个元素时,只有一个元素的数组自然而然地就可以视为是有序的,此时就可以进行合并操作了。
因此,上面讲的合并两个有序的子数组,是从 只有一个元素 的两个子数组开始合并的。
当合并两个数组的时候,如果遇到待比较的元素是相同的值时,那么先拷贝
右边
的,后拷贝左边
的。其实先后无所谓,主要是为了方便统一,在类似的题目:找数组小和的题目。考虑右边有多少比自己大的,那么需要先把右边的拷贝进去,因为拷贝左边进数组也不能确定右边数组有多少比自己大的元素。所以在比较的时候,只有左边严格的小于右边,才会进行拷贝。
代码实现
package com.vitamin.sort;
import org.junit.Test;
import java.util.Arrays;
/**
* 归并排序
*/
public class MergeSort {
@Test
public void test() {
MergeSort mergeSort = new MergeSort();
mergeSort.sort();
mergeSort.print();
}
int arr[] = new int[]{5, 6, 7, 10, 2, 30, 8, 9, 10, 2};
public void print() {
Arrays.stream(arr).forEach(System.out::println);
}
public void sort() {
if (arr == null || arr.length <= 1) {
return;
}
sortArr(0, arr.length - 1);
}
private void sortArr(int begin, int end) {
if (end == begin) {
return;
}
// 分治,分别排序
int middle = begin + ((end - begin) >> 1);
sortArr(begin, middle);
sortArr(middle + 1, end);
// 进行归并操作
merge(begin, middle, end);
}
/**
* 归并操作
* 传递数值: 0,1,2
* @param begin
* @param middle
* @param end
*/
private void merge(int begin, int middle, int end) {
if (end == begin) {
return;
}
//创建辅助数组
int helpArr[] = new int[end - begin + 1];
int i = 0;
int leftStart = begin;
int rightStart = middle + 1;
while (leftStart <= middle && rightStart <= end) {
helpArr[i++] = arr[leftStart] < arr[rightStart] ? arr[leftStart++] : arr[rightStart++];
}
while (leftStart <= middle) {
helpArr[i++] = arr[leftStart++];
}
while (rightStart <= end) {
helpArr[i++] = arr[rightStart++];
}
// 原数组实现排序
for (int j = 0; j < helpArr.length; j++) {
arr[j + begin] = helpArr[j];
}
}
}
数组小和
注意
:小和的求解一定要在数组排序之前进行。
private int sortArr(int begin, int end) {
if (end == begin) {
return 0;
}
// 分治,分别排序
int middle = begin + ((end - begin) >> 1);
return sortArr(begin, middle) +
sortArr(middle + 1, end) +
// 进行归并操作
merge(begin, middle, end);
}
/**
* @param begin
* @param middle
* @param end
*/
private int merge(int begin, int middle, int end) {
// 只有一个元素的时候是不会产生小和的
if (end == begin) {
return 0;
}
//创建辅助数组
int helpArr[] = new int[end - begin + 1];
int i = 0;
int leftStart = begin;
int rightStart = middle + 1;
// 小和
int sum = 0;
// 1 1 1 2 5 1 1 5 5 6
// 1 1
while (leftStart <= middle && rightStart <= end) {
sum += arr[leftStart] < arr[rightStart] ? arr[leftStart] * (end - rightStart + 1) : 0;
helpArr[i++] = arr[leftStart] < arr[rightStart] ? arr[leftStart++] : arr[rightStart++];
}
while (leftStart <= middle) {
helpArr[i++] = arr[leftStart++];
}
while (rightStart <= end) {
helpArr[i++] = arr[rightStart++];
}
// 原数组实现排序
for (int j = 0; j < helpArr.length; j++) {
arr[j + begin] = helpArr[j];
}
return sum;
}