前提:把数组元素都当成傻子,他们并不会排序。
把一个无序的数组分成两半,但是,左半部分的数组是傻子,不知道怎么排序,右边也是。那我们就再把左边的数组再分成两半,此时两部分还是不会排序,那就再分...直到什么程度呢?直到把一个长度为2的数组分成两个长度为1的数组为止,只有一个数字的数组可以把它当成是排好序的。
这时,我们能做的操作就是,把两个有序的数组合并成一个有序的数组。怎么合并呢?左手从数组1的起始位置开始,右手从数组2的起始位置开始,比较,如果1的小,那就把数组1的这个数放进新数组里,然后左手往后移;如果2的小,就把2的这个数放进新数组里,右手往后移。直到其中一个数组全部判断结束,另一个数组就可以不用判断了,直接连到新数组后边。
注意:只能合并两个已经排好序的数组。
归并排序看起来我们什么也没做,其实重点在合并上。合并的前提是两个数组都是排好序的,但是,数组自己并不知道如何排序,只能不断地把数组分成左右两个部分,8变4和4,4变成2和2,2变成1和1,只有当数组被拆分成了两个长度为1的数组时,才有解决方法,因为只有1个数的本来就是有序的,这时,通过调用merge函数,把1和1合并成长度为2的数组,然后依次如此。。。最后4个和4个,合并成8个元素的有序数组。
let mergeSort=arr =>{
let k=arr.length
if (k===1){return arr} //递归的出口条件
let left=arr.slice(0,Math.floor(k/2))
let right=arr.slice(Math.floor(k/2))
return merge(mergeSort(left),mergeSort(right))
}
let merge=(a1,a2) => {
let i=0
let j=0
let result=[]
while (i<a1.length && j<a2.length){
if (a1[i]<a2[j]){
result.push(a1[i])
i+=1
}else{
result.push(a2[j])
j+=1
}
}
return result.concat(i===a1.length?a2.slice(j):a1.slice(i))
} //依次比较两个数组的当前元素,谁小,谁就进新数组;因为两个数组都是排好序的,所以当前的元素都是自己数组里的最小值。
归并排序和快速排序:
快速排序是给出一个基准点,比它小的都去前边,比它大的都去后边。
归并排序是不断拆分成两半,直到拆到就剩一个了,通过合并操作,合并成有序的数组。合并才是重点。
时间复杂度:O(n log2n)
如:n=8,八个分成四个和四个,拆分这个操作只需要1次,是常数级的,所以不需要考虑。但是把两个数组合并这个操作,需要依次比较两个数组的当前元素,两个数组一共有多少个元素,就需要比较几次。第一轮拆分成四个和四个,需要比较8次。第二轮四个又拆分成2个和2个,合并要比较4次;另一份四个也是四次,加一次还是8次。第三轮四份2个的数组再拆成8份一个的数组,也是比较八次。所以,每次合并要比较n次,一共合并多少次呢?8个元素的数组,要合并3次,因为是对半分,就是log2n次。
所以归并排序的时间复杂度就是O(n log2n)