插入排序
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', '是個傻逼')
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 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];
}
let r = [];
r = r.concat(find_subsets(S, decisions.concat(S[i]), i+1));
r = r.concat(find_subsets(S, decisions, i+1));
return r;
}
console.log(find_subsets('abc'));
空間優化版本
function * subnets(S){
// 子集有2的N次方個,所以遍歷2的N次方次
for (let i = 0; i < 1 << S.length; i++) {
let s = [];
for (let k = 0; k < S.length; k++) {
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);
b.push(permutation(othersStr, select.concat(str[i])))
}
return r.concat(...b)
}
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));
複製程式碼