归并
问:
- 归并排序
- master公式求递归函数的时间复杂度
- 在一个数组中,每一个数左边比当前数小的数累加起来,叫做这个数组的小和。求一个数组的小和。
解: 1.
function mergeOrder(arr) {
function getRes(left, right) {
if (left === right) {
return arr.slice(left, right + 1)
}
const midIdx = Math.floor((left + right) / 2)
const leftValue = getRes(left, midIdx)
const rightValue = getRes(midIdx + 1, right)
return merge(leftValue, rightValue)
}
function merge(leftArr, rightArr) {
const res = []
while (leftArr.length && rightArr.length) {
leftArr[0] <= rightArr[0] ? res.push(leftArr.shift()) : res.push(rightArr.shift())
}
res.push(...leftArr, ...rightArr)
return res
}
return getRes(0, arr.length - 1)
}
-
splitArr函数中,left部分递归和right部分递归是相等的,同为数组长度的一半,满足子过程规模相当条件。适用于master公式。merge函数遍历了一次数组。表达式为T(N) = 2 * T(N/2) + O(N)。 根据master公式,时间复杂度为O(N * logN)。
-
这个问题可以看作,当前数的右边,有多少个数大于它,那么就意味着产生了几次当前数的小和,譬如在const arr = [1,5,6] 中,1这个数的右边有5,6两个数都大于1,那么1产生了两次小和。那么在归并解法中,当leftArr[0] < rightArr[0]时,由于arr已经是排序过后的结果了,这意味着rightArr全部都大于leftArr[0],所以leftArr[0]这个数产生了rigthArr.length次小和
const arr = [3,1,4,2,5,10, 11]
const left = 0
const right = arr.length-1
let totalNumber = 0
function splitArr(left, right) {
const midIdx = Math.floor((right + left) / 2)
if (left < right) {
const leftValue = splitArr(left, midIdx) // 0,2
const rightValue = splitArr(midIdx + 1, right) // 3 4
return merge(leftValue, rightValue)
}
return arr.slice(left, right + 1)
}
function merge(leftArr, rightArr) {
const arr = []
// 比较左右两个数组,小的数组弹出一个放入新数组
while (leftArr.length > 0 && rightArr.length > 0) {
if (leftArr[0] < rightArr[0]) {
// 若右边有比左大的数,那么我们就计左边这个数会产生多少次小和,累加上去
totalNumber += leftArr[0] * rightArr.length
arr.push(leftArr.shift())
} else {
arr.push(rightArr.shift())
}
}
// 剩余数组直接放到结果里面
return arr.concat(leftArr).concat(rightArr)
}