插入排序
function insert_sort(A) {
// 每次循环,0-i都是有序的
for (let j = 1; j < A.length; j++) {
const key = A[j];
let t = j - 1;
// 此处有优化,没必要每次都交换两个变量的位置,key最后填上坑就行
while (t >= 0 && A[t] > key) {
A[t + 1] = A[t];
t = t - 1
}
A[t + 1] = key;
}
}
选择排序
/********************************************************************************************/
function select_sort(A) {
const exchange = (arr, j, min) => {
const temp = arr[j];
arr[j] = arr[min];
arr[min] = temp;
}
for (let j = 0; j < A.length; j++) {
let min = j;
for (let i = j + 1; i < A.length; i++) {
if (A[min] > A[i]) min = i;
}
exchange(A, j, min);
}
}
快速排序
/**************************************快速排序*************************************************/
function swap(A, i, j) {
[A[i], A[j]] = [A[j], A[i]];
}
function partition(A, low, high) {
const center = A[high - 1];
let i = low; j = high - 1;
while (i !== j) {
if (A[i] <= center) {
i++;
} else {
swap(A, i, --j);
}
}
swap(A, j, high - 1);
return j;
}
function qsort(A, low = 0, high = A.length) {
if (high - low < 2) return;
const p = partition(A, low, high);
qsort(A, low, p);
qsort(A, p + 1, high);
}
let A = [3, 5, 7, 13, 22, 25, 4, 6, 8, 14, 23, 18];
qsort(A, 0, A.length);
console.log(A);
快速排序习题
<!--这是求前6(N)个数的修改模块-->
function qsort(A, low = 0, high = A.length) {
if (high - low < 2) return;
const p = partition(A, low, high);
if(p + 1 > 6){
return qsort(A, low, p);
}else if(p + 1 < 4){
return qsort(A, p + 1, high);
}else{
return A.slice(0, 6);
}
}
归并排序
/**********************************归并*********************************************/
function merge(A, p, q, r) {
const A1 = A.slice(p, q);
const A2 = A.slice(q, r);
A1.push(Number.MAX_SAFE_INTEGER);
A2.push(Number.MAX_SAFE_INTEGER);
let j = 0, k = 0;
// 这个地方台坑了,i=p i< r
for (let i = p; i < r; i++) {
A[i] = A1[j] < A2[k] ? A1[j++] : A2[k++];
}
}
function merge_sort(A, p, r) {
if (r - p < 2) return;
const q = Math.ceil((p + r) / 2);
merge_sort(A, p, q);
merge_sort(A, q, r);
merge(A, p, q, r);
}
let A = [3, 5, 7, 13, 22, 25];
merge_sort(A, 0, A.length);
console.log(A);
非递归版本排序
/* *******************************非递归版本******************************************* */
function merge_sort2(A) {
for (let i = 1; i < A.length; i += i) {
const step = i * 2;
for (let start = 0; start < A.length; start += step) {
const end = Math.min(start + step, A.length);
if (end - start > 1) {
const mid = start + i;
merge(A, start, mid, end);
}
}
}
}
非递归版本平衡优化
/* *******************************平衡优化******************************************* */
const L = 16;
function merge_sort3(A) {
// 计算一个scale系数
const p2 = 2 ** Math.floor(Math.log2(A.length));
const scale = A.length / p2;
// 优化2 在比较小的一个区间使用插入排序先对其排一下
for (let i = 0; i < p2; i += L) {
start = Math.floor(i * scale);
end = Math.floor(start + L * scale);
insertion_sort(A, start, end);
}
for (let i = 1; i < p2; i += i) {
for (let m = 0; m < p2; m += i * 2) {
const start = Math.floor(m * scale);
const mid = Math.floor((m + i) * scale);
const end = Math.floor((m + i * 2) * scale);
if (A[end - 1] < A[start]) {
// 优化1 如果两组数组,end比start小,直接整体挪位置就行了
rotate(A, mid - start, start, end);
} else {
merge(A, start, mid, end);
}
}
}
}
二分查找
/* *******************************二分查找******************************************* */
function bsearch(A, x) {
let l = 0, r = A.length - 1, guess;
while (l <= r) {
guess = Math.floor((l + r) / 2);
if (x === A[guess]) return guess;
else if (x < A[guess]) {
r = guess + 1;
} else {
l = guess + 1;
}
}
return 'not found'
}
// digui
function bsearch2(A, l, r, x) {
if (l > r) return 'not found';
guess = Math.floor((l + r) / 2);
if (x === A[guess]) {
return guess;
} else if (x < A[guess]) {
return bsearch(A, l, guess + 1, x);
} else {
return bsearch(A, guess + 1, r, x);
}
}
计数排序
/* ************************计数排序********************* */
function counting_sort(A) {
const max = Math.max(...A);
const B = Array(max + 1).fill(0);
const C = Array(A.length);
A.forEach((_, i) => B[A[i]]++);
for (let i = 1; i < B.length; i++) {
B[i] = B[i - 1] + B[i];
}
for (let i = 0; i < A.length; i++) {
const p = B[A[i]] - 1;
B[A[i]]--;
C[p] = A[i];
}
return C;
}
let A = [3, 5, 7, 13, 22, 25, 4, 6, 8, 14, 23, 18, 3];
let c1 = counting_sort(A);
console.log(c1);
最大子数组
/* ************************最大子数组********************* */
function find_cross(A, low, mid, high) {
let leftSum = Number.MIN_SAFE_INTEGER;
let sum = 0, maxLeft, maxRight;
for (let i = mid; i >= low; i--) {
sum += A[i];
if (sum > leftSum) {
leftSum = sum;
maxLeft = i;
}
}
let rightSum = Number.MIN_SAFE_INTEGER;
let sum2 = 0;
for (let i = mid + 1; i < high; i++) {
sum2 += A[i];
if (sum2 > rightSum) {
rightSum = sum2;
maxRight = i;
}
}
return { maxLeft, maxRight, crossSum: maxLeft === maxRight ? A[maxLeft] : rightSum + leftSum }
}
function find_max_sub_array(A, low, high) {
if (high - low < 2) {
return {
low,
high,
v: A[low]
}
} else {
let mid = Math.floor((low + high) / 2);
let { low: leftLow, high: leftHigh, v: leftSum } = find_max_sub_array(A, low, mid);
let { low: rightLow, high: rightHigh, v: rightSum } = find_max_sub_array(A, mid, high);
let { maxLeft, maxRight, crossSum } = find_cross(A, low, mid, high);
if (leftSum >= rightSum && leftSum >= crossSum) {
return { low: leftLow, high: leftHigh, v: leftSum }
} else if (rightSum >= leftSum && rightSum >= crossSum) {
return { low: rightLow, high: rightHigh, v: rightSum }
} else {
return { low: maxLeft, high: maxRight, v: crossSum }
}
}
}
桶排序
<!--桶排序-->
function insert_sort(A) {
// 每次循环,0-i都是有序的
for (let j = 1; j < A.length; j++) {
const key = A[j];
let t = j - 1;
// 此处有优化,没必要每次都交换两个变量的位置,key最后填上坑就行
while (t >= 0 && A[t] > key) {
A[t + 1] = A[t];
t = t - 1
}
A[t + 1] = key;
}
}
function bucket_sort(A, k, S) {
const buckets = Array.from({ length: k }, () => []);
// 放入桶中
for (let i = 0; i < A.length; i++) {
const index = ~~(A[i] / S);
buckets[index].push(A[i]);
}
// 排序每只桶
for (let i = 0; i < buckets.length; i++) {
insert_sort(buckets[i]);
}
// 取出数据
return [].concat(...buckets);
}
const A = [29, 25, 3, 49, 9, 37, 21, 43];
console.log(bucket_sort(A, 5, 10));
基数排序
<!--基数排序-->
function radix_sort(A) {
const max = Math.max(...A);
const buckets = Array.from({ length: 10 }, () => []);
let m = 1;
while (m < max) {
A.forEach(number => {
const digit = ~~((number % (m * 10)) / m);
buckets[digit].push(number);
});
let j = 0;
buckets.forEach(bucket => {
while (bucket.length > 0) {
A[j++] = bucket.shift();
}
});
m *= 10;
}
}
const A = [10, 200, 13, 12, 7, 88, 91, 24];
radix_sort(A)
console.log(A);
function sort(A) {
let buckets = Array.from({ length: 27 }, () => []);
let j = 7;
while (j > -1) {
A.forEach(item => item[j] ? buckets[item[j].charCodeAt() - 96].push(item) : buckets[0].push(item));
A = [].concat(...buckets);
buckets = Array.from({ length: 27 }, () => []);
j -= 1;
}
return A;
}
let A = ['xyz', 'okr', 'oop', 'ofo', 'abc', 'bu', 'nlju', 'ab'];
console.log(sort(A));
求前N大的数字, 1.基于比较的排序,先排序。 nlogn 2.快速排序 on 3.先建堆取N次。
单项链表
双向链表
大根堆
<!--大根堆-->
// 二叉树 性质
// 一个节点的左索引 left = index*2 + 1
// 一个节点的右索引 right = index*2 + 2
// 如果索引 >= floor(arr.length/2) ---> 是叶子节点
// 反过来除以2,floor可以拿到父亲
// 堆 二叉树的一种 最大堆 or 最小堆
// 构建最大堆
class Heap {
constructor(arr) {
this.data = [...arr];
this.size = this.data.length;
}
/**
* 所有节点都不满足堆的性质
* 1
* 2 3
* 4 5
*/
rebuildHeap() {
const L = Math.floor(this.size / 2);
for (let i = L - 1; i >= 0; i--) {
this.maxHeadpify(i);
}
}
ifHeap() {
const L = Math.floor(this.size / 2);
for (let i = 0; i < L; i++) {
const l = this.data[left(i)] || Number.MIN_SAFE_INTEGER;
const r = this.data[right(i)] || Number.MIN_SAFE_INTEGER;
const max = Math.max(this.data[i], l, r);
if (max !== this.data[i]) {
return false;
}
}
return true;
}
/**
* 建堆后,能能确定的就是顶上的那个是最大的,左右不确定,
* 我们就取顶上的那个,然后把最后一个元素放顶上,
* 由于之前是贱好堆的,符合其他地方都满足堆的性质,就直接其他地方都满足堆的性质
*/
sort() {
for (let i = this.size - 1; i > 0; i--) {
swap(this.data, 0, this.size - 1);
this.size--;
this.maxHeadpify(0);
}
}
/**
* 假设堆的其他地方都满足堆的性质
* 唯独根节点(三个哈),重构堆性质
*/
maxHeadpify(i) {
let max = i;
if (i >= this.size) {
return;
}
const leftIndex = left(i);
const rightIndex = right(i);
if (leftIndex < this.size && this.data[leftIndex] > this.data[max]) {
max = leftIndex;
}
if (rightIndex < this.size && this.data[rightIndex] > this.data[max]) {
max = rightIndex;
}
if (max === i) return;
swap(this.data, i, max);
this.maxHeadpify(max);
}
}
function left(i) { return i * 2 + 1 }
function right(i) { return i * 2 + 2 }
function swap(arr, i, j) {
const t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
const heap = new Heap([15, 2, 8, 12, 5, 2, 3, 4, 7])
heap.maxHeadpify(1)
console.log(heap.data);
const heap1 = new Heap([1, 2, 3, 4, 5])
heap1.rebuildHeap()
console.log(heap1.data);
const heap2 = new Heap([5, 4, 3, 2, 1]);
heap2.rebuildHeap();
console.log(heap2.data);
heap2.sort();
console.log(heap2.data);
HashTable
<!--hashTable-->
// hash冲突链表版本
// 1.还有开放地址版本,插入时候22 -》 %10 得到2放入数组中索引为2的地方, 再来一个12模10还是2,发现2的位置有了,就看3有没有,没有就放3,有就继续看4
// 2.查找的时候12模后,去2位置对比,看相不相等,不相等就看3的位置
// 3.布隆过滤器,原理,拿着值‘hello’,定义三个hash函数,算出三个hash值,算出三个位置,在然后按位置为1,假如存储空间是4字节32位,
// 查找的时候,拿着三个值,虽然不能判断是否存在某个元素,但是如果一旦发现某个位是0,就能够过滤某个元素肯定不存在的情况,作为数据处理的第一次处理,减小数据量
class HashTable {
constructor(num = 1000) {
this.M = num;
this.slots = new Array(num);
}
hash(str) {
return [...str].reduce((hash, c) => {
hash = (331 * hash + c.charCodeAt(0)) % this.M;
return hash;
}, 1);
}
add(key, value) {
const hashCode = this.hash(key);
if(!this.slots[hashCode]){
this.slots[hashCode] = [];
}
this.slots[hashCode].unshift({ key, value });
}
delete(key){
const hashCode = this.hash(key);
this.slots[hashCode] = this.slots[hashCode].filter(item => item.key !== key);
}
search(key){
const hashCode = this.hash(key);
const target = this.slots[hashCode].find((item) => item.key === key);
return target? target.value: null;
}
}
// const handler = {
// get function(target) {
// },
// set function(target) {
// }
// }
// const mp = new Proxy(map, handler)
const map = new HashTable();
map.add('jack', '是个xx')
console.log(map.search('jack'));
经典组合问题
经典组合问题
function combination(S, k) {
if (k === 0 || S.length === k) {
return [S.slice(0, k)];
}
const [first, ...others] = S;
let r = [];
const A2 = combination(others, k - 1).map(c => [first, ...c]);
const A3 = combination(others, k);
r = r.concat(A2);
r = r.concat(A3);
return r;
}
const S = ['a', 'b,', 'c', 'd'];
console.log(combination(S, 2));
//解法二
function combination(s, k, decisions=[], result=[]) {
if (k === 0) {
return result.push(decisions.join(''));
}
for(let i=0; i<s.length; i++) {
//如果ab跟ba算两种的话就用这一行
//const left = s.slice(0, i).concat(s.slice(i+1));
const left = s.slice(i+1);
combination(left, k-1, decisions.concat(s[i]), result);
}
return result;
}
const S = ['a', 'b', 'c', 'd'];
console.log(combination(S, 2));
//语言特性干掉结果收集
function *combination(s, k, decisions=[]) {
if (k === 0) {
yield decisions.join('');
return;
}
for(let i=0; i<s.length; i++) {
//如果ab跟ba算两种的话就用这一行
//const left = s.slice(0, i).concat(s.slice(i+1));
const left = s.slice(i+1);
yield *combination(left, k-1, decisions.concat(s[i]));
}
}
const S = ['a', 'b', 'c', 'd'];
console.log(...combination(S, 2));
子集问题 遍历决策树
function find_subsets(S, decisions=[]) {
// 所有决策已经完成
if (S.length === decisions.length) {
// 返回递归结果
return [decisions];
}
let r = [];
r = r.concat(find_subsets(S, decisions.concat(true)));
r = r.concat(find_subsets(S, decisions.concat(false)));
return r;
}
//自己写出来了
function find_subsets(S, decisions = [], i=0) {
// 所有决策已经完成
if (S.length === i) {
// 返回递归结果
return [decisions.join('')];
}
const r1 = find_subsets(S, decisions.concat(S[i]), i+1);
const r2 = find_subsets(S, decisions, i+1);
return [].concat(r1, r2);
}
console.log(find_subsets('abc'));
//空间优化版本
function * subnets(S){
// 子集有2的N次方个,所以遍历2的N次方次, 000 - 111
for (let i = 0; i < 1 << S.length; i++) {
let s = [];
for (let k = 0; k < S.length; k++) {
// 001 010 100 看i的哪一位是1,是1就push进那个字母,完了就join
const take = i & (1<<k);
take && s.push(S[k]);
}
yield s.join('');
}
}
const S = ['a', 'b', 'c'];
console.log([...subnets(S)])
全排列问题 遍历决策树
function permutation(str, select = []) {
if (str.length === 0) {
return select.join('')
}
let r = []
let b = []
for (let i = 0; i < str.length; i++) {
const prev = str.slice(0, i);
const next = str.slice(i + 1);
const othersStr = prev.concat(next);
const res = permutation(othersStr, select.concat(str[i]));
b.push(res)
//最好的是使用concat降维
// b = b.concat(res);
// 末尾就可以直接return b;r就可以不要了
}
//因为b是个数组['acb'],每次都往数组里push数组[['acb'],['bca']]不好降下维
return r.concat(...b)
}
console.log(permutation('abc'))
// 变下写法收集结果的写法
function permutation(str, select = [], result = []) {
if (str.length === 0) {
return result.push(select.join(''))
}
for (let i = 0; i < str.length; i++) {
const prev = str.slice(0, i);
const next = str.slice(i + 1);
const othersStr = prev.concat(next);
permutation(othersStr, select.concat(str[i]), result)
}
return result;
}
console.log(permutation('abc'))
if(所有决策都完成){
返回结果
}
根据当前状态算出所有可能的决策
递归调用这些决策
收集递归的结果,返回
搜索问题
function compatible(p, q, n) {
const [x1, y1] = [~~(p / n), p % n];
const [x2, y2] = [~~(q / n), q % n];
return x1 !== x2 && y1 !== y2 && Math.abs(x1 - x2) !== Math.abs(y1 - y2);
}
// 4*4 转成的一维数组, 里面的值,就等于索引
function queen(n, decisions = []) {
if (decisions.length === n) {
return [decisions];
}
let r = [];
const start = decisions[decisions.length - 1] || -1;
for (let i = start + 1; i < n * n; i++) {//0-16
// decision里就是选择的一个决策,我们每次都跟最后的一个比一下是不是ok的
if (decisions.every(j => compatible(j, i, n))) {
r = r.concat(queen(n, decisions.concat(i)))
}
}
return r;
}
console.log(queen(10));