归并排序的引入
归并排序的命名来自它的实现原理: 把一系列排好序的子序列合并成一个大的完整有序序列。 从理论上讲, 这个算法很容易实现。 我们需要两个排好序的子数组, 然后通过比较数据大小, 先从最小的数据开始插入, 最后合并得到第三个数组。 然而, 在实际情况中, 归并排序还有一些问题, 当我们用这个算法对一个很大的数据集进行排序时, 我们需要相当大的空间来合并存储两个子数组。 就现在来讲, 内存不那么昂贵, 空间不是问题, 因此值得我们去实现一下归并排序, 比较它和其他排序算法的执行效率。
自底向上的排序方法
采用非递归或者迭代版本的归并排序是一个自底向上的过程。 这个算法首先将数据集分解为一组只有一个元素的数组。 然后通过创建一组左右子数组将它们慢慢合并起来, 每次合并都保存一部分排好序的数据, 直到最后剩下的这个数组所有的数据都已完美排序。 下图演示了自底向上的归并排序算法如何运行的。
代码测试:
1.所调用的函数:
function CArray(numElements) {
this.dataStore = [];
this.toString = toString;
this.numElements = numElements;
this.setData = setData; //随机生成一组数
this.mergeSort=mergeSort;
this.mergeArrays=mergeArrays;
for (var i = 0; i < numElements; ++i) {
this.dataStore[i] = i;
}
}
function setData() {
for (var i = 0; i < this.numElements; ++i) {
this.dataStore[i] = Math.floor(Math.random() *
(this.numElements+1));
}
}
function toString() {
var retstr = "";
for (var i = 0; i < this.dataStore.length; ++i) {
retstr += this.dataStore[i] + " ";
if (i > 0 && i % 10 == 0) {
retstr += "\n";
}
}
return retstr;
}
function mergeSort() {
if (this.dataStore.length < 2) {
return;
}
var step = 1;
var left, right;
while (step < this.dataStore.length) {
left = 0;
right = step;
while (right + step <= this.dataStore.length) {
mergeArrays(this.dataStore, left, left+step, right, right+step);
left = right + step;
right = left + step;
}
if (right < this.dataStore.length) {
mergeArrays(this.dataStore, left, left+step, right, this.dataStore.length);
}
step *= 2;
}
}
function mergeArrays(arr,startLeft, stopLeft, startRight, stopRight) {
var rightArr = new Array(stopRight - startRight + 1);
var leftArr = new Array(stopLeft - startLeft + 1);
k = startRight;
for (var i = 0; i < (rightArr.length-1); ++i) {
rightArr[i] = arr[k];
++k;
} k
= startLeft;
for (var i = 0; i < (leftArr.length-1); ++i) {
leftArr[i] = arr[k];
++k;
}
rightArr[rightArr.length-1] = Infinity; // 哨兵值
leftArr[leftArr.length-1] = Infinity; // 哨兵值
var m = 0;
var n = 0;
for (var k = startLeft; k < stopRight; ++k) {
if (leftArr[m] <= rightArr[n]) {
arr[k] = leftArr[m];
m++;
}
else {
arr[k] = rightArr[n];
n++;
}
}
alert("left array - ", leftArr[m]);
alert("right array - ", rightArr[n]);
}
说明:函数中的关键点就是 step 这个变量, 它用来控制 mergeArrays() 函数生成的leftArr 和 rightArr 这两个子序列的大小。 通过控制子序列的大小, 处理排序是比较高效的, 因为它在对小数组进行排序时不需要花费太多时间。 合并之所以高效, 还有一个原因, 由于未合并的数据已经是排好序的, 将它们合并到一个有序数组的过程非常容易
测试
var nums = new CArray(10);
nums.setData();
alert(nums.toString());
nums.mergeSort();
alert(nums.toString());