时间复杂度 / 额外空间复杂度(O)
递归行为及其时间复杂度估计
-
递归行为:
递归行为实际是一个压栈过程,它将无法获取具体值的一个过程依次放入栈中,直到获取到具体值,再以出栈方式执行之前保存的过程,若再次遇到无法获取的,再重复上述,直到栈空,返回具体的值。
-
其时间复杂度估计:
master公式:T(N) = a*T(N/b) + O(N^d)
a: 一个过程中包含多少个子过程
对应三种结果:
- log(b,a) > d -> 复杂度为O(N^log(b,a))
- log(b,a) = d -> 复杂度为O(N^d * logN)
- log(b,a) < d -> 复杂度为O(N^d)
例子:归并排序 / 快速排序
排序算法
时间复杂度(O(N^2))
冒泡排序
思路:临近的数字两两进行比较,按照从小到大或者从大到小的顺序进行交换。
const bubbleSort = (arr) => {
for (let i = 0; i < arr.length; i += 1) {
let temp
for (let j = 0; j < arr.length - i - 1; j += 1) {
if (arr[j] > arr[j + 1]) {
temp = arr[j]
arr[j] = arr[j + 1]
arr[j + 1] = temp
}
}
}
return arr
}
// 给定范围排序
const bubbleRangeSort = (arr, L, R) => {
if (L >= R) return arr
if (L < 0) L = 0
if (R > arr.length - 1) R = arr.length - 1
for (let i = L; i < R + 1; i += 1) {
let temp
for (let j = L; j < R - (i - L); j += 1) {
if (arr[j] > arr[j + 1]) {
temp = arr[j]
arr[j] = arr[j + 1]
arr[j + 1] = temp
}
}
}
return arr
}
选择排序
思路:从所有序列中先找到最小的,然后放到第一个位置。之后再看剩余元素中最小的,放到第二个位置……以此类推
const chooseSort = (arr) => {
for(let i = 0; i< arr.length - 1; i+=1) {
let temp = arr[i]
for(let j = i + 1; j< arr.length; j+=1) {
if (temp > arr[j]) {
arr[i] = arr[j]
arr[j] = temp
}
}
}
return arr
}
插入排序
思路:将一个记录插入到已经排好序的有序表中,从而一个形成一个有序表
const insertSort = (arr) => {
for(let i = 0; i < arr.length; i+=1) {
for(let j = 0; j < i; j+=1) {
if (arr[i] < arr[j]) {
let temp = arr[i]
arr[i] = arr[j]
arr[j] = temp
}
}
}
return arr
}
时间复杂度(O((N*logN))
加强理解问题
小和问题:
问题: 在一个数组中,每一个数左边比当前数小的数累加起来,叫做这个数组的小和。求一个数组的小和。
思路: 中线分组,分成最小元素(形式二叉树),组之前的小和,与前后树杈的小和。
解答:
const smallSum = (arr) => {
if (arr === undefined || arr === null || arr.length < 2) {
return arr
}
return PartSmallSum(arr, 0 , arr.length - 1)
}
const PartSmallSum = (arr, L, R) => {
if (L === R) return 0
const middle = Math.floor(L + ((R - L) >> 1))
return PartSmallSum(arr, L, middle) + PartSmallSum(arr, middle + 1, R) + margeSmallSum(arr, L, middle, R)
}
// 求出合并后的和*
const margeSmallSum = (arr, L, middle, R) => {
let help = new Array(R - L + 1)
let i = 0
let pl = L
let pr = middle + 1
let res = 0
while(pl <= middle && pr <= R) {
res += arr[pl] < arr[pr] ? arr[pl]*(R-pr+1) : 0
help[i++] = arr[pl] < arr[pr] ? arr[pl++] : arr[pr++]
}
while(pl <= middle) {
help[i++] = arr[pl++]
}
while(pr <= R) {
help[i++] = arr[pr++]
}
for(let i = 0; i < help.length; i+=1) {
arr[L + i] = help[i]
}
return res
}
荷兰问题:
问题: 给定一个数组arr,和一个数num,请把小于num的数放在数组的左边,等于num的数放在数组的中间,大于num的数放在数组的 右边。
思路: 前中后划范围,第一个数大于num,与最后一个数交换,再比较,小就与前一个数交换,等于就不交换。
解答:
const flagQuestion = (arr, num) => {
if (arr === null || arr === undefined || arr.length < 2) {
return arr
}
let cur = 0
let pl = 0
let pr = arr.length -1
while (cur <= pr) {
if (arr[cur] > num) {
arr = changePosition(arr,cur,pr--)
}
if (arr[cur] < num) {
changePosition(arr,cur++,pl++)
}
if (arr[cur] === num) {
cur++
}
}
return arr
}
const changePosition = (arr, l, r) => {
if (l === r) return arr
const temp = arr[l]
arr[l] = arr[r]
arr[r] = temp
return arr
}
归并排序
思路:如小和所示
const mergeSort = (arr) => {
if (arr === undefined || arr === null || arr.length < 2) {
return arr
}
return partMergeSort(arr, 0 , arr.length - 1)
}
const partMergeSort = (arr, L, R) => {
if (L === R) return arr
const middle = Math.floor(L + ((R - L) >> 1))
arr = partMergeSort(arr, L, middle)
arr = partMergeSort(arr, middle + 1, R)
arr = margePart(arr, L, middle, R)
return arr
}
const margePart = (arr, L, middle, R) => {
let help = new Array(R - L + 1)
let i = 0
let pl = L
let pr = middle + 1
while(pl <= middle && pr <= R) {
help[i++] = arr[pl] < arr[pr] ? arr[pl++] : arr[pr++] // [3]
}
while(pl <= middle) {
help[i++] = arr[pl++]
}
while(pr <= R) {
help[i++] = arr[pr++]
}
for(let i = 0; i < help.length; i+=1) {
arr[L + i] = help[i]
}
return arr
}
快速排序(随机长期为O((N*logN),不随机可能为O(N^2))
思路:如荷兰国旗
const quickSort = arr => {
if (arr === null || arr === undefined || arr.length < 2) {
return arr
}
return mergePartition(arr, 0, arr.length - 1)
}
const mergePartition = (arr, l, r) => {
if (l < r) {
// 加上就是随机快速排序
arr = changePosition(arr, l + Math.floor(Math.random() * (r - l + 1)), r)
const p = partition(arr, l, r)
arr = p.arr
arr = mergePartition(arr, l, p.l - 1)
arr = mergePartition(arr, p.more + 1, r)
}
return arr
}
const partition = (arr, l, r) => {
// 以最后一个数作为划分
let cur = l
let more = r - 1
while (cur <= more) {
if (arr[cur] > arr[r]) {
arr = changePosition(arr, cur, more--)
}
if (arr[cur] < arr[r]) {
arr = changePosition(arr, cur++, l++)
}
if (arr[cur] === arr[r]) {
cur++
}
}
arr = changePosition(arr, ++more, r)
return { arr, l, more }
}
const changePosition = (arr, l, r) => {
if (l === r) return arr
const temp = arr[l]
arr[l] = arr[r]
arr[r] = temp
return arr
}