归并排序整体的代码实现与二叉树的后序遍历有异曲同工之处,即先遍历左子树(i.e. 排序[left, mid]数组元素),后遍历右子树(i.e. 排序[mid + 1, right]数组元素),最后遍历根节点(i.e.合并排序后的左、右子树)。
代码实现中涉及到的常用编程思路:
- 递归: 归并排序
sort(array, left, right)
代码实现的整体思路
涉及到树的遍历,一般与递归算法或栈相关
public void mergeSort(int[] array) {
sort(array, 0, array.length - 1);
}
public void sort(int[] array, int left, int right) {
if (left < right) {
int mid = left + (right - left) / 2;
sort(array, left, mid); // 将[left, mid]的元素排序
sort(array, mid + 1, right); // 将[mid + 1, right]的元素排序
merge(array, left, mid, right); // 合并并排序两个排序好的数组
}
}
- 双指针:合并两个数组
merge(array, left, mid, right)
的主要思路
合并两个排序数组通常会联想到用双指针分别索引两个数组的元素(leetcode有类似的题目)
public void merge(int[] array, int left, int mid, int right) {
int[] arr = new int[right - left + 1]; // 储存合并后的数组
int p = 0, p1 = left, p2 = mid + 1; // p: arr; p1: array[left : mid]; p2: array[mid + 1 : right]
1. 两两比较放入arr中
while (p1 <= mid && p2 <= right) { // arr[p] = array[p1] > array[p2] ? array[p2] : [p1]
if (array[p1] > array[p2]) {
arr[p] = array[p2];
p2++;
} else {
arr[p] = array[p1];
p1++;
}
p++;
/* 等价:
if (array[p1] > array[p2]) {
arr[p++] = array[p2++];
} else {
arr[p++] = array[p1++];
}
*/
}
2. 将剩余没有进行两两比较的元素(如果有的话)依次放入arr中
while (p1 <= mid) { // 如果[left, mid]还有元素未放入arr中
arr[p] = array[p1];
p1++;
p++;
// 等价:arr[p++] = array[p1++];
}
while (p2 <= right) {// 如果[mid + 1, right]还有元素未放入arr中
arr[p] = array[p2];
p2++;
p++;
// 等价:arr[p++] = array[p2++];
}
3. arr放入array[left, right]
for (int i = left; i <= right; i++) {
array[i] = arr[i - left];
}
}
注意事项
因为函数直接对数组进行修改,而不是新生成一个数组,所以与此有关要注意的方面:
- 排序后数组元素的索引发生改变
在遇到需要返回元素索引而不是元素本身的问题时需要注意,可以考虑将索引构造一个数组,在元素排序的同时索引数组也相应的改变。 - 返回类型为
void
,数组要作为传参
当然大佬们可以按照自身需求尝试修改代码实现,这里展示其中一种代码实现思路。