# 1 全排列

``````function permute(input) {
var permArr = [],
usedChars = [];
function main(input) {
for (let i = 0; i < input.length; i++) {
let ch = input.splice(i, 1)[0];
usedChars.push(ch);
if (input.length == 0) {
permArr.push(usedChars.slice());
}
main(input);
input.splice(i, 0, ch);
usedChars.pop();
}
return permArr
}
return main(input);
};

# 2 二分法

``````function BinarySearch (arr, target) {
let from = 0
let to = arr.length - 1
let mid = Math.floor((from + to)/2)
while (from <= to) {
mid = Math.floor((from + to)/2)
if (arr[mid] > target) {
to = mid - 1
} else if (arr[mid] < target) {
from = mid + 1
} else {
return mid
}
}

return -1
}

# 3 大整数加法

``````function sumStrings(a,b){
var res='', c=0;
a = a.split('');
b = b.split('');
while (a.length || b.length || c){
c += ~~a.pop() + ~~b.pop();
res = c % 10 + res;
c = c>9;//c大于9返回1，小于等于为0
}
return res.replace(/^0+/,'');
}

# 4 排序

## 4.1 冒泡排序

``````function bubbleSort(arr) {
if (!Array.isArray(arr) || arr.length <= 1) return;
let lastIndex = arr.length - 1;
while (lastIndex > 0) { // 当最后一个交换的元素为第一个时，说明后面全部排序完毕
let flag = true, k = lastIndex;
for (let j = 0; j < k; j++) {
if (arr[j] > arr[j + 1]) {
flag = false;
lastIndex = j; // 设置最后一次交换元素的位置
[arr[j], arr[j+1]] = [arr[j+1], arr[j]];
}
}
if (flag) break;
}
}

## 4.2 选择排序

``````function SelectionSort (arr) {
const length = arr.length
for (let i = 0; i < length; i++ ) {
let minIndex= i
for (let j = i + 1; j < length; j++) {
minIndex = arr[minIndex] <= arr[j] ? minIndex : j
}
if (minIndex !== i) {
const temp = arr[i]
arr[i] = arr[minIndex]
arr[minIndex] = temp
}
}
return arr
}

## 4.3 插入排序

``````function InsertionSort (arr) {
const length = arr.length
for (let i = 1; i < length; i++) {
const temp = arr[i]
let j
for (j = i - 1; j >= 0 && temp < arr[j]; j--) {
arr[j+1] = arr[j]
}
arr[j+1] = temp
}
return arr
}

## 4.4 希尔排序

``````function ShellSort (arr) {
const length = arr.length
let gap = Math.floor(length)
while (gap) {
for (let i = gap; i < length; i++) {
const temp = arr[i]
let j
for (j = i - gap; j >= 0 && temp < arr[j]; j = j - gap) {
arr[j + gap] = arr[j]
}
arr[j + gap] = temp
}
gap = Math.floor(gap / 2)
}
return arr
}

## 4.5 归并排序

``````function MergeSort (arr, low, high) {
const length = arr.length
if (low === high) {
return arr[low]
}
const mid = Math.floor((low + high)/2)
MergeSort(arr, low, mid)
MergeSort(arr, mid + 1, high)
merge(arr, low, high)
return arr

}

function merge (arr, low, high) {
const mid = Math.floor((low + high)/2)
let left = low
let right = mid + 1
const result = []
while (left <= mid && right <= high) {
if (arr[left] <= arr[right]) {
result.push(arr[left++])
} else {
result.push(arr[right++])
}
}
while (left <= mid) {
result.push(arr[left++])
}
while (right <= high) {
result.push(arr[right++])
}

arr.splice(low, high-low+1, ...result)
}

const test = [2, 34, 452,3,5, 785, 32, 345, 567, 322,5]

console.log(MergeSort(test, 0, test.length - 1))

## 4.6 快速排序

``````function quickSort(array){
if(array.length < 1){
return array;
}
let pivot = array[array.length - 1];
let left = array.filter((v,i)=> v <=pivot && i != array.length - 1)
let right = array.filter(v=> v > pivot)
return [...quickSort(left),pivot,...quickSort(right)]
}

• Js中sort()对长度小于10的数组采用插入排序，对大于10的采用快速排序
• 优化方法1：结合快排和插入排序，当排到集合长度小于某个数值时，采用插入排序等稳定的排序，优化效果；
• 优化方法2：随机取基准值，因为每次取第一个或是最后一个，如果数组本身有序就会带来很多交换，因此随机取基准可以避免这个问题，由于Js的随机某种程度上是伪随机，可以采用随机三次来接近随机；
• 优化方法三：取第一个、最后一个和中间值的中位数，来防止数组本身的顺序干扰；
• 优化方法四：如果代码中有两次递归，可以将前一次递归改为循环，优化效率；
• 优化方法五：将相同的数值聚集在一起，避免重复交换。

## 4.7 堆排序

``````function HeapSort (arr) {
const length = arr.length

// 调整初始堆，调整完其实也确定了最大值
// 但此时最大值是在 arr[0] 中
for (let i = Math.floor(length/2) - 1; i >= 0; i--) {
}

// 把 arr[0](最大值)换到后面
for (let i = length - 1; i >=0; i--) {
const temp = arr[0]
arr[0] = arr[i]
arr[i] = temp
}

return arr
}

// size 是还需要调整的堆的大小
// 随着一个个最大值的确定，size 会越来越小
function adjustHeap (arr, position, size) {
const left = position * 2 + 1
const right = left + 1
let maxIndex = position
if (left < size && arr[left] > arr[maxIndex]) {
maxIndex = left
}
if (right < size && arr[right] > arr[maxIndex]) {
maxIndex = right
}
if (maxIndex !== position) {
const temp = arr[position]
arr[position] = arr[maxIndex]
arr[maxIndex] = temp
}
return arr
}

## 4.8 在大集合中找前K大的数

• 最容易想到的方法是将数据全部排序，然后在排序后的集合中进行查找，最快的排序算法的时间复杂度一般为O（nlogn），如快速排序。但是在32位的机器上，每个float类型占4个字节，1亿个浮点数就要占用400MB的存储空间，对于一些可用内存小于400M的计算机而言，很显然是不能一次将全部数据读入内存进行排序的。其实即使内存能够满足要求（我机器内存都是8GB），该方法也并不高效，因为题目的目的是寻找出最大的10000个数即可，而排序却是将所有的元素都排序了，做了很多的无用功。

• 第二种方法为局部淘汰法，该方法与排序方法类似，用一个容器保存前10000个数，然后将剩余的所有数字——与容器内的最小数字相比，如果所有后续的元素都比容器内的10000个数还小，那么容器内这个10000个数就是最大10000个数。如果某一后续元素比容器内最小数字大，则删掉容器内最小元素，并将该元素插入容器，最后遍历完这1亿个数，得到的结果容器中保存的数即为最终结果了。此时的时间复杂度为O（n+m^2），其中m为容器的大小，即10000。

• 第三种方法是分治法，将1亿个数据分成100份，每份100万个数据，找到每份数据中最大的10000个，最后在剩下的100*10000个数据里面找出最大的10000个。如果100万数据选择足够理想，那么可以过滤掉1亿数据里面99%的数据。100万个数据里面查找最大的10000个数据的方法如下：用快速排序的方法，将数据分为2堆，如果大的那堆个数N大于10000个，继续对大堆快速排序一次分成2堆，如果大的那堆个数N大于10000个，继续对大堆快速排序一次分成2堆，如果大堆个数N小于10000个，就在小的那堆里面快速排序一次，找第10000-n大的数字；递归以上过程，就可以找到第1w大的数。参考上面的找出第1w大数字，就可以类似的方法找到前10000大数字了。此种方法需要每次的内存空间为10^6*4=4MB，一共需要101次这样的比较。

• 第四种方法采用最小堆。首先读入前10000个数来创建大小为10000的最小堆，建堆的时间复杂度为O（mlogm）（m为数组的大小即为10000），然后遍历后续的数字，并于堆顶（最小）数字进行比较。如果比最小的数小，则继续读取后续数字；如果比堆顶数字大，则替换堆顶元素并重新调整堆为最小堆。整个过程直至1亿个数全部遍历完为止。然后按照中序遍历的方式输出当前堆中的所有10000个数字。该算法的时间复杂度为O（nmlogm），空间复杂度是10000（常数）。

• 第五种方法是Hash法。如果这1亿个书里面有很多重复的数，先通过Hash法，把这1亿个数字去重复，这样如果重复率很高的话，会减少很大的内存用量，从而缩小运算空间，然后通过分治法或最小堆法查找最大的10000个数。

## 4.9 老鼠试毒

1000个杯子,均装有无色无味透明液体,其中1杯盛有毒性很强的溶液,其他均无毒。可以派老鼠试毒，老鼠可以喝很多杯子里的水，喝到有毒的会在12小时之后身亡，请问在12小时内找到有毒的杯子需要几只老鼠？

...

## 5.关于JS的写算法的一些常用手段和坑

• sort()默认是字典序，如果需要按数字大小，可以载入函数(a,b)=>{return a-b}，或是载入其他规则；

## 6.时间复杂度简介

• 最好情况时间复杂度（best case time complexity）
• 最坏情况时间复杂度（worst case time complexity）
• 平均情况时间复杂度（average case time complexity）

• 均摊时间复杂度（amortized time complexity）