从零开始学算法(四)归并排序

归并排序

代码是Javascript语言写的(几乎是伪代码)

算法介绍

归并排序(Merge Sort)把两个排好序的序列合并成一个排好序的序列

算法原理

我们在拿到一个无序的序列时,其实用了“分治”的思想,先“分”后“治”,先将整个序列通过划分为子序列,再将子序列不断划分为子序列,直到只有单个元素为止,再通过两两合并使其有序,再将合并后的数组继续通过比较大小拷贝的方法继续合并,最终实现整体有序。

算法简单记忆说明

在这里插入图片描述
“分” 阶段采用递归的方法,将整个数组不断划分为最小的数组个体

代码实现:

function mergeSort(arr,l,r){
	if (l == r) {
			return;
		}
		var mid = l + ((r - l) >> 1);
		mergeSort(arr, l, mid);
		mergeSort(arr, mid + 1, r);
	}

与我们上一篇文章的递归思想举的例子完全一样,mid为序列的中间数的位置,通过递归让它们不断一分为二。

“治” 阶段将划分好的子序列两两归并排序合并在一起,合并后的序列继续两两合并,最终全部整合形成完整的有序序列

“治”的过程的实现时通是通过准备一个拷贝数组,比较两个待合并的序列,分别给两个索引,谁小拿下来填谁,谁的索引向后移动一位,直到有一边的序列全部被填入拷贝数组,则把剩下的序列的剩下的数填入拷贝数组,最后将拷贝后的数组拷贝回原数组。
待合并两数组

8 3
i j

help辅助拷贝数组

扫描二维码关注公众号,回复: 4083146 查看本文章

3<8,将3填入help数组,j向后移,但是数组耗尽了,所以将8拷入help数组中得到

3 8

同理2和9也通过比较拷贝得到

2 9

继续合并合并后的数组
小的填入而后索引后移直到耗尽一方,然后将剩下的序列的剩下的元素填入数组中。

2 3 8 9
1 4 5 7

最后大合并

1 2 3 4 5 6 7 8 9

算法复杂度和稳定性

归并排序的时间复杂度是O(N*logN)
归并排序采用递归的思想来做
master公式 T(N) = a*T(N/b) + O(Nd)

从父问题与子问题的大层面关系来看,整个数组样本量为N,左边一半,样本量为N/2,右边一般,样本量为N/2,左边跑完跑右边,样本量为N/2的过程发生了2次,得到两个排好序的子样本,剩下要做的是在外排的过程中划过N个数,因为两个下标依此在动,最后整体拷贝回数组,剩下的操作为N
则a=2,b=2,d=1
log(b,a) = d -> 复杂度为O(Nd * logN)
得到归并排序的时间复杂度是O(N*logN)

归并排序是稳定排序算法

算法稳定性的定义: 假设在数列中存在a[i]=a[j],若在排序之前,a[i]在a[j]前面;并且排序之后,a[i]仍然在a[j]前面。则这个排序算法是稳定的。
举例
1,3,2,5,2,4
排序的过程中1,2,3与2,4,5排序后为1,2,2,3,4,5
所以为稳定排序

代码实现

function mergeSort(arr,l,r){ //递归
	if (l == r) {
			return;
		}
		var mid = l + ((r - l) >> 1);
		mergeSort(arr, l, mid);
		mergeSort(arr, mid + 1, r);
		merge(arr, l, mid, r);
	}
function merge(arr,l,m,r){
	var help = new Array(r-l+1);//创立辅助数组用来拷贝数字
	var i = 0;
	var p1 = l;//左边部分首位
	var p2 = m+1;//右边部分首位
	while(p1 <= m&&p2 <= r){
		help[i++] = arr[p1]<arr[p2]?arr[p1++]:arr[p2++];
		//完成比较合并,p1小填p1,p2小填p2,填入数组的那个部分++向后移动一位,辅助数组也要向后移动一位
	} //必定有且只有一个部分越界
	while(p1<=m){
		help[i++] = arr[p1++]; //p2越界,把p1剩下的填了
	}
	while(p2<=r){
		help[i++] = arr[p2++]; //p1越界,把p2剩下的填了
	}
	for(i = 0;i<help.length;i++){
		arr[l+i] = help[i]; //拷贝回原数组
	}

}
function startMerge(arr){
	if(arr == null||arr.length<2){
		return arr;	
	}
	mergeSort(arr,0,arr.length-1);
}

//对数器
function sortNumber(a,b){
return a - b
}

function rightMethod(arr) {
    arr.sort(sortNumber);
}

function generateRandomArray(maxSize, maxValue) {
    var arr = new Array(Math.floor((maxSize + 1) * Math.random()));
    for (var i = 0; i < arr.length; i++) {
        arr[i] = Math.floor((maxValue + 1) * Math.random())-Math.floor(maxValue * Math.random());
    }
    return arr;
}

function isEqual(arr1, arr2) {
    if ((arr1 == null && arr2 != null ) || (arr1 != null && arr2 == null)) {
        return false;
    }
    if (arr1 == null && arr2 == null) {
        return true;
    }
    if (arr1.length != arr2.length) {
        return false;
    }
    for (let i = 0; i < arr1.length; i++) {
        if (arr1[i] != arr2[i]) {
            return false
        }
    }
    return true;
}

function copyArray(arr) {
    if (arr == null) {
        return null;
    }
    return [].concat(arr);
}

function Test() {
    var testTimes = 10000;
    var maxmaxSize = 10;
    var maxValue = 100;
    var succeed = true;
    for (var i = 0; i < testTimes; i++) {
        var arr1 = generateRandomArray(maxmaxSize,maxValue);
        var arr2 = copyArray(arr1);
        var arr3 = copyArray(arr1);
        startMerge(arr1);
        rightMethod(arr2);
        console.log(arr1);
        if (!isEqual(arr1, arr2)) {
            succeed = false;
            console.log(arr3);
            break;
        }
    }
    console.log(succeed ? "Good job!" : "Damn it!");
}
Test();

在这里插入图片描述
通过对数器的验证!Good job!

猜你喜欢

转载自blog.csdn.net/weixin_42507756/article/details/84069751