算法

199 阅读2分钟

二分查找:

function bsearch (A, x) {
	let l = 0,	// 查找的左边界
      r = A.length - 1,	// 查找的右边界
      guess	// 猜测位置
  
  while (l <= r) {
  	guess = Math.floor( (l + r) / 2 )
    // 循环不变式
    // guess等于l,r的中间位置
    if (A[guess] === x) return guess
    else if (A[guess] > x) r = guess - 1
    else l = guess + 1
  }
}
function bsearch (A, x, l = 0, r = A.length) {
	const guess = Math.floor( (l + r) / 2 )
  if (l > r) return -1
  if (A[guess] === x) return guess
  return A[guess] < x ?
    bsearch(A, x, guess + 1, r) :
  	bsearch(A, x, l, guess - 1)
}

插入排序:

1、向已排序数组中插入值:

const A = [2,4,7,9,13]
const x = 8
const b = A.find(a => a > x)
if (b === undefined) {
	A.push(x)
} else {
	const idx = A.indexOf(b)
  A.splice(idx, 0, x)
}
console.log(A)
const A = [2,4,7,9,13]
const x = 8
const b = A.find(a => a > x)
A.splice(idx === -1 ? A.length : idx, 0, x)
console.log(A)
function insert (A, x) {
	let p = A.length - 1
  while (p >= 0 && A[p] > x) {
  	A[p + 1] = A[p]
    p--
  }
  A[p + 1] = x
}
const A = [2,4,7,9,13]
const x = 8
insert(A, x)
console.log(A)

2、未排序数组排序

function insert (A, i, x) {
	let p = i - 1
  while (p >= 0 && A[p] > x) {
  	A[p + 1] = A[p]
    p--
  }
  A[p + 1] = x
}
function insertion_sort (A) {
	for (let i = 1; i < A.length; i++) {
  	insert(A, i, A[i])
  }
}
const A = [5,8,1,2,3,4,9]
insertion_sort(A)
console.log(A)

冒泡排序:

function swap (A, i, j) {
	const t = A[i]
  A[i] = A[j]
  A[j] = t
}

function bubble_sort (A) {
	for (let i = A.length - 1; i >= 1; i--) {
  	for (let j = 1; j <= i; j++) {
    	if (A[j - 1] > A[j]) {
      	swap(A, j - 1, j)
      }
    }
  }
}

3、合并排序

先用前闭后开区间来描述一个有序数组

再定义以下变量:

p: 左半边开始,闭区间的值

q:左半边结束,右半边开始

r:右半边结束

function merge (A, p, q, r) {
  // 采用前闭后开的方式,符合slice方法
	let A1 = A.slice(p, q)
	let A2 = A.slice(q, r)
  A1.push(Number.MAX_SAFE_INTEGER)
  A2.push(Number.MAX_SAFE_INTEGER)
  // 往A中回写
  for (let k = p, i = 0, j = 0; k < r; k++) {
  	A[k] = A1[i] < A2[j] ? A1[i++] : A2[j++]
  }
}

如果是冒泡排序,时间复杂度是O(n2)

假如有100个数,要排序10000次

但如果把这100个数分成两组,每组分别排序,要排序5050 + 5050 = 5000次 再加上合并走的for循环的次数

// p和r是前闭后开
function merge_sort (A, p, r) {
	if (r - p < 2) return
  // 数组长度是奇数时,算出的q也是符合左闭右开区间的
  const q = Math.ceil( (p + r) / 2 )
  merge_sort(A, p, q)
  merge_sort(A, q, r)
  merge(A, p, q, r)
}

快速排序

下面的代码中各种范围还是采用左闭右开的区间,这么做的好处是:

对于[i, j),j - i就是要处理的长度

partition方法的执行思路:

  • 小于中心点范围:[lo, i)
  • 未确认范围:[i, j)
  • 大于中心点范围:[j, hi - 1)
  • 随着每次循环的执行,未确认范围越来越小,终止条件为i > j或i === j
function swap (A, i, j) {
    [A[i], A[j]] = [A[j], A[i]]
}

function partition (A, lo, hi) {
    // 随便指定一个中心点
    const pivot = A[hi - 1]
    let i = lo, j = hi - 1
    
  while (i !== j) {
  	if (A[i] <= pivot) {
    	i++
    } else {
    	swap(A, i, --j)
    }
  }
  swap(A, j, hi - 1)
  return j
}

function qsort (A, lo = 0, hi = A.length) {
	if (hi - lo <= 1) return
  const p = partition(A, lo, hi)
  qsort(A, lo, p)
  qsort(A, p + 1, hi)
}

汉诺塔:

思路:

moveTower n A->C use B {
  // 先借助C将n-1个盘子从A移动到B
	moveTower n-1 A->B use C
  // 再将第n个盘子从A移动到C
  moveDisk n A->C
  // 再借助A将n-1个盘子从B移动到C
  moveTower n-1 B->C use A
}
function moveTower (n, from, to, use) {
	if (n === 1) {
  	moveDisk(from, to)
    return
  } else {
  	moveTower(n - 1, from, use, to)
    moveDisk(from, to)
    moveTower(n - 1, use, to, from)
  }
}

枚举子集:

// decisions是布尔型数组
function find_subsets (S, decisions) {
	if (S.length === decisions.length) {
  	return [decisions.map((v, i) => v ? S[i] : '').join('')]
  }
  let r = []
  r = r.concat(find_subsets(S, decisions.concat(true)))
  r = r.concat(find_subsets(S, decisions.concat(false)))
  return r
}
const r = find_subsets('abc', [])
console.log(r)

全排列:

function permutation(str, list) {
  if(list.length === str.length) {
    return [ list.map(i => str[i]).join('') ]
  }

  let r = []
  for(let i = 0; i < str.length ; i++) {
    if(list.indexOf(i) === -1) {
      r = r.concat( permutation(str, list.concat(i)) ) 
    }
  }
  return r
}
console.log(permutation1('abc', []))

数组扁平化:

function fn(arr){
  let arr1 = []
  arr.forEach((val)=>{
    if(val instanceof Array){
      arr1 = arr1.concat(fn(val))
    }else{
      arr1.push(val)
    }
  })
  return arr1
 }