归并排序
整体思路
递归将数组拆分成两份,直到不能拆分(实际就是左边数组一个元素,右边数组一个元素)进行merge,merge 后的结果和另外merge后的数组再merge。
易错点
变量定义: l 左侧起始位置,mid 中间位置,r 结束位置
- 在merge过程中,因为copy了一个数组出来,左侧起始位置时0,右侧数组的起始下标应该等于左侧元素个数的值(因为数组下标是0开始)。我容易写成 r-mid 这样来计算右侧数组的起始位置
- 在比较的过程中,判断右侧数组没有元素时,应该写成 rightIndex >= r
具体代码如下:
// 归并排序思路:将元素划分成两部分进行排序,排好序后进行合并,重复上面的过程
#include <stdio.h>
#include <vector>
using namespace std;
class MergeSortExe2 {
public:
void sort(vector<int>& arr) {
sort(arr, 0, (int)arr.size()-1);
}
private:
void sort(vector<int>& arr, int l, int r) {
if (l>=r) {
return;
}
int mid = l + (r - l)/2;
sort(arr, l, mid);
sort(arr, mid+1, r);
merge(arr, l, mid, r);
}
/// merge过程:
/// 先将[l...r]之间的元素拷贝出来
/// 挨个比较[l...mid]和[mid+1...r]之间的元素大小,将比较后的结果放在原数组中
void merge(vector<int>& arr, int l, int mid, int r) {
vector<int> tempArray;
for (int i=l; i<=r; i++) {
tempArray.push_back(arr[i]);
}
/// 计算左、右两边数组各有多少个数
int leftNum = mid-l+1;
int leftIndex = 0;
int rightNum = r-mid;
// int rightIndex = r-mid-1; ///!!!错误的写法,如果右侧数组只有2个元素,左侧100个,比较及拷贝的过程都是用的左边数组。。。
int rightIndex = leftNum;
int k = l;
/// 只要两个数组中还有数据,就需要做拷贝动作
while (leftIndex < leftNum || rightIndex < rightNum) {
///第一步处理左边数组已经没有元素的情况
///第二部处理右边数组已经没有元素的情况
///第三部比较第一个数组中的元素是否小于第二个数组中的元素
///第四步将第二个数组中的元素拷贝到原数组中
if (leftIndex >= leftNum) {
arr[k] = tempArray[rightIndex];
rightIndex++;
k++;
// } else if(rightIndex >= rightNum) { /// !!!错误的写法,应该比较小于数组大小即可
} else if(rightIndex >= tempArray.size()) {
arr[k] = tempArray[leftIndex];
leftIndex++;
k++;
} else if(tempArray[leftIndex] <= tempArray[rightIndex]) {
arr[k] = tempArray[leftIndex];
leftIndex++;
k++;
} else {
arr[k] = tempArray[rightIndex];
rightIndex++;
k++;
}
}
}
};