思路:将要排序的数组从中间分红两部分,分别排序,再将排序好的两部分合并。算法
这里用到了分治思想:分而治之,讲一个大问题分解成若干个小问题来解决。很明显这里须要用到递归这种编程方式。编程
function mergeSort(array, region1, region2) {
var tempArray = [];
var i = region1[0];
var j = region2[0];
for (; i <= region1[1]; i++) {
for (; j <= region2[1]; j++) {
if (array[i] > array[j]) {
tempArray.push(array[j]);
} else {
break;
}
}
tempArray.push(array[i]);
}
for (; j <= region2[1]; j++) {
tempArray.push(array[j]);
}
var n = 0;
for (let k = region1[0]; k <= region2[1]; k++) {
array[k] = tempArray[n++];
}
return [region1[0], region2[1]];
}
function divide(array, start, end) {
if (start >= end) return [start, end];
var mid = Math.floor((end+start)/2);
return mergeSort(array, divide(array, start, mid),divide(array, mid+1, end));
}
复制代码
分析:数组
稳定的排序;bash
根据递推代码求时间复杂度的方法,分析👆代码时间复杂度:归并排序的执行效率与数据有序度无关,全部时间复杂度O(nlogn);ide
每次合并时,会临时申请一个最大不超过n的内存空间,合并完成释放。而空间复杂度是指算法运行时须要的额外空间,每次执行时函数都仅仅申请了一块内存空间,但并无累积。所以空间复杂度O(n),不是原地排序,这也是归并排序不如快速排序应用普遍的缘由;函数
思路: 一样采用分治思想。 从数组中选一个位置的数据,小于它的放左边,大于它的放右边。以后再分别对左右两边再次进行一样操做以此类推。直到间距缩小到1,说明数据都有序了。其实就是每次经过分区的方式找到一个元素在有序后的数组中的位置。ui
总结就是:spa
// 若是pivot==end则只需从一头开始查找了
function quickSort(array, start, end) {
if (start >= end) return;
var pivot = divide(array, start, end);
quickSort(array, start, pivot-1);
quickSort(array, pivot+1,end);
}
function divide(array, start, end) {
var pivot = end;
// i表明小于pivot的区域边界
var j = 0;
var i = 0;
while (i <= pivot) {
if (array[j] <= array[pivot]) { // 由于此时插入操做不要求保证顺序,用交换就行
if (array[i] != array[j]) {
var temp = array[i];
array[i] = array[j];
array[j] = temp;
}
i++;
}
j++;
if (j >= pivot) break;
}
// 把pivot插入到小于区域的末尾
temp = array[i];
array[i] = array[pivot];
array[pivot] = temp;
pivot = i;
return pivot;
}
复制代码
对数组进行分区操做中,为了进行原地排序,空间复杂度为O(1),又尽量不进行移动,采用了相似插入排序的方式,同时对数组的插入操做采用交换的方式。3d
而这种交换操做就会形成排序算法的不稳定。code
时间复杂度:平均时间复杂度O(nlogn);最坏是O(n2);
能够发现归并是先处理子问题,而后再合并。而快速则是先对大区域分区,在对子区域分区处理。
除了对无序数组进行排序外,还能够解决如:求无序数组中第K大元素,这样的问题。 快速排序每次分区都会找到pivot元素排序的位置,所以能够每次分区后比较K和pivot的位置大小,进行更加细化的查找。时间复杂度= n+n/2+n/4+n/8+...+1 = O(n);