0、基础

(1)异或

相异为1,相同为0。
性质

  • N^0=N N^N=0
  • 满足交换律和结合律

(2)与

1&1=1,其他都=0。

//利用临时变量实现交换
function swap(arr, i, j){let temp = arr[i];arr[i] = arr[j];arr[j] = arr[i];
}
//利用异或实现交换
//前提:i和j地址值不同(相当于自己地址值里的内容跟自己地址值里的内容异或=0)。
function swap(arr, i, j){arr[i] = arr[i]^arr[j];arr[j] = arr[i]^arr[j];arr[i] = arr[i]^arr[j];
}
//利用解构赋值实现交换
function swap(arr, i, j){[arr[i], arr[j]] = [arr[j], arr[i]];
}//数组中有一个数只出现奇数次,其余都出现偶数次。请找出这个数。
//leetcode-136
//时间复杂度O(n),空间复杂度O(1)
var singleNumber = function(nums) {let i = 0;nums.forEach(n=>{i ^= n;});return i;
};//数组中有两个数只出现奇数次,其余都出现偶数次。请找出这两个数。
//leetcode-260
//时间复杂度O(n),空间复杂度O(1)
var singleNumber = function(nums) {let eor = 0;nums.forEach(n=>{eor ^= n;});//此时eor=a^b,且不等于0//找出eor二进制表示中为1的最低位let right = eor & (~eor+1);let one = 0;//得到a或b//根据right位,可以将所有数分成两部分:others1和a,others2和bnums.forEach(n=>{//将和right位相同的数异或,得到的结果就是a或bif(n & right){one ^= n;}});return Array.of(one, eor^one);
};

(3)生成随机数

//0-1
Math.random()
//0-x
Math.around(Math.random()*x)
//1-10
Math.around(Math.random()*9+1)
//x-y
Math.around(Math.random()*(y-x) + x)

(4)master公式

T(N) = a*T(N/b) + O(N^d)

  • log b a < d,时间复杂度为O(N^d)
  • log b a > d,时间复杂度为O(N^(log b a))
  • log b a = d,是件复杂度为O(N^d * logN)

(5)比较器

//返回负数时,a排在前;返回正数时,b排在前;返回0,不变。
function cmp(a, b){//按id升序/*if(a.id < b.id){return -1;}else if(a.id > b.id){return 1;}else{return 0;}*/return a.id - b.id;//按id降序//return b.id - a.id;
}arr.sort(cmp);

(6)定义节点

//单链表
function Node(value){this.value = value;this.next = null;
}
//二叉树
function Node(value){this.value = value;this.left = null;this.right = null;
}

1、排序

  • 快选快排;空间少选堆排序;稳定选归并排序。
  • sort(),基础类型使用快排,非基础类型使用归并:是为了稳定性。

基于比较的排序

(1)选择排序

  • 时间复杂度O(n^2)
  • 空间复杂度O(1)
  • 不稳定
  • 每趟从后面未排序序列中选出一个最小的。
function selectSort(arr){if(arr === null || arr.length < 2) return;//长度n,下标0~n-1//遍历i~n-2for(let i = 0; i < arr.length-1; i++){//以i为标杆let minIndex = i;//遍历i+1~n-1for(let j = i+1; j < arr.length; j++){minIndex = arr[j] < arr[minIndex] ? j : minIndex;}swap(arr, i, minIndex);}
}

(2)冒泡排序

  • 时间复杂度O(n^2)
  • 空间复杂度O(1)
  • 稳定
  • 从前往后,两两比较,大的往后放,每趟比较后冒出一个最大的元素。
function bubbleSort(arr){if(arr === null || arr.length < 2) return;//长度n,下标0~n-1//i放每趟排序后冒出来的元素。for(let i = arr.length - 1; i > 0; i--){//遍历0~i-1for(let j = 0; j < i; j++){if(arr[j] > arr[j+1]){swap(arr, j, j+1);}}}
}

(3)插入排序

  • 时间复杂度最差O(n^2),最佳(n)
  • 空间复杂度O(1)
  • 稳定
  • 将元素从后往前插入已排序序列中,比较直到前一个元素比元素小结束。
function insertSort(arr){if(arr === null || arr.length < 2) return;//长度n,下标0~n-1//i指向待插入元素。for(let i = 0; i < arr.length; i++){//j指向i前面元素,比较j和j+1for(let j = i - 1; j > 0 && arr[j] > arr[j+1];j--){swap(arr, j, j+1);}}
}

(4)归并排序

  • 时间复杂度O(NlogN)
  • 空间复杂度O(N)
  • 稳定
  • 分成左右两序列,使左右序列分别有序;合并左右序列,小的插入result,指针后移。
function mergeSort(arr){if(arr === null || arr.length < 2) return arr;process(arr, 0, arr.length-1);
}
function process(arr, L, R){if(L === R) return;//mid = (R-L)/2;为了防止溢出,mid = L + (R-L)/2;位运算更快。let mid = L + (R-L)>>1;process(arr, L, mid);process(arr, mid+1, R);merge(arr, L, mid, R);
}
function merge(arr, L, M, R){let result = new Array(R-L+1);let i = 0;//指向result当前要放入元素的位置let p1 = L;//指向左序列当前遍历到哪里let p2 = M+1;//指向右序列当前遍历到哪里//当指针p1、p2还未超出范围时while(p1 <= M && p2 <= R){//注意是<=(相等的时候把左边的先插入)result[i++] = arr[p1] <= arr[p2] ? arr[p1++] : arr[p2++];}//遍历结束还有元素未插入while(p1 <= M){result[i++] = arr[p1++];}while(p2 <= R){result[i++] = arr[p2++];}for(let i = 0; i < result.length; i++){arr[L+i] = result[i];}
}//应用
//小和问题:在一个数组中,遍历每个数,把每个数左边比当前数小的数累加起来,叫做这个数组的小和。
function smallSum(arr){if(arr === null || arr.length < 2) return 0;return process(arr, 0, arr.length-1);
}
function process(arr, l, r){if(l === r) return 0;let mid = l + ((r - l)>>1);//左边处理完的累积小和+右边处理完的累积小和+这趟处理产生的累积小和。return process(arr, 1, mid)+ process(arr, mid+1, r)+ merge(arr, 1, mid, r);
}
function merge(arr, l, m, r){let result = new Array(r-l+1);let i = 0;let p1 = l;let p2 = m+1;let res = 0;while(p1 <= m && p2 <= r){res += arr[p1] < arr[p2] ? (r-p2+1)*arr[p1] : 0;//注意是<result[i++] = arr[p1] < arr[p2]? arr[p1++] : arr[p2++];}while(p1 <= m){result[i++] = arr[p1++];}while(p2 <= r){result[i++] = arr[p2++];}for(let j = 0; j < result.length; j++){arr[l+i] = result[i];}return res;
}//逆序对问题:在一个数组中,左边的数如果比右边的数大,则这两个数构成一个逆序对,请求出逆序对的总数。
//leetcode-剑指offer51
var reversePairs = function(nums) {if(nums === null || nums.length < 2) return 0;return process(nums, 0, nums.length-1);
};
function process(arr, l, r){if(l === r) return 0;let mid = l + ((r-l)>>1);return process(arr, l, mid)+ process(arr, mid+1, r)+ merge(arr, l , mid, r);
}
function merge(arr, l, m, r){let result = new Array(r-l+1);let i = 0;let p1 = l;let p2 = m+1;let res = 0;while(p1<=m && p2<=r){//注意比较符号res += arr[p1] > arr[p2] ? r-p2+1 : 0;result[i++] = arr[p1] > arr[p2]? arr[p1++] : arr[p2++]; }while(p1<=m){result[i++] = arr[p1++];}while(p2<=r){result[i++] = arr[p2++];}for(let i = 0; i < result.length; i ++){arr[l+i] = result[i];}return res;
}

(5)快速排序

  • 时间复杂度O(NlogN)
  • 空间复杂度O(logN)
  • 不稳定
//问题1:给定一个数组arr和一个数num,请把小于等于num的数放在数组的左边,大于num的数放在数组的右边。
//时间复杂度O(N),空间复杂度O(1)
function divide(arr, num){//指向≤区的最后一个元素。let i = -1;let p = 0;while(p < arr.length){//≤num,和i的下一个数交换(插入≤区,≤区右扩)。if(arr[p] <= num){swap(arr, ++i, p);}//>num,不处理;p++;}return arr;
}//荷兰国旗问题:给定一个数组arr和一个数num,请把小于num的数放在数组的左边,等于num的数放在数组的中间,大于num的数放在数组的右边。
//时间复杂度O(N),空间复杂度O(1)
function dutchNationalFlag(arr, num){let i = -1;let j = arr.length;let p = 0;while(p < j){//<num,和i的下一个数交换(≤区右扩),p右移。if(arr[p]<num){swap(arr, ++i, p++);}//=num,p右移。else if(arr[p]===num){p++;}//>num,和j的前一个数交换(>区左扩),p不动(交换后p还未被比较过)。else if(arr[p]>num){swap(arr, p, --j);}}
}//快排1.0:最后一个数为num,以问题1为partition算法,不断递归。
//快排2.0:最后一个数为num,以荷兰国旗问题为partition算法,不断递归。
//快排3.0:随机一个数为num,以荷兰国旗问题为partition算法,不断递归。
function quickSort(arr){if(arr === null || arr.length < 2)return;process(arr, 0, arr.length-1);
}
function process(arr, L, R){if(L < R){//从R-L之间随机选一个数和R交换,作为标杆。swap(arr, L + Math.around(Math.random()*(R-L)), R);//p[0]是=区的左边界,p[1]是=区的右边界。let p = [...partition(arr, L, R)];//<区再做快排process(arr, L, p[0]-1);//>区再做快排process(arr, p[1]+1, R);}
}
function partition(arr, L, R){let i = L-1;let j = R;//R位置是标杆let p = L;while(p < j){if(arr[p] < arr[R]){swap(arr, ++i, p++);}else if(arr[p] === arr[R]){p++;}else if(arr[p] > arr[R]){swap(arr, p, --j);}}//最后将>区的左边界和标杆换位置。swap(arr, j, R);//返回=区的左右边界。return Array.of(++i, j);
}//优化:样本数<60使用插入排序;其余使用快排。

(6)堆排序

  • 时间复杂度O(NlogN)
  • 空间复杂度O(1)
  • 不稳定
  • 优先级队列结构就是堆结构。
//完全二叉树中,(结点下标为i),i结点的父结点为(i-1)/2,左孩子2i+1,右孩子2i+2。//给了个大根堆,从index位置开始往上调整成大根堆。
//不断跟父节点比较,比父节点大上浮。
//时间复杂度O(logN)
function heapInsert(arr, index){//比父亲小 或 我的父亲就是我自己 时跳出循环。while(arr[index] > arr[(index-1)/2]){swap(arr, index, (index-1)/2);index = (index - 1) / 2;}
}//给了个大根堆,从index位置开始往下调整成大根堆。
//不断跟子节点较大的比较,比子节点小下沉。
//时间复杂度O(logN)
function heapify(arr, index, heapSize){let lchild = index*2+1;while(lchild < heapSize){//左右孩子进行比较let bigOne = lchild+1 < heapSize && arr[lchild] < arr[lchild+1]? lchild+1 : lchild;//左右孩子较大的和父节点进行比较bigOne = arr[bigOne] > arr[index]? bigOne : index;if(bigOne === index) break;swap(arr, bigOne, index);index = bigOne;lchild = index*2+1;}
}//给了个大根堆,把index位置改成某个数num,要求调整成大根堆。
//num和arr[index]进行比较,如果num<arr[index]往下调整;如果num>arr[index]往上调整。//堆排序
function heapSort(arr){if(arr === null || arr.length < 2) return;//从0位置开始往上调整大根堆。//for(let i = 0; i < arr.length; i++){//   heapInsert(arr, i);//}//倒序遍历,向下调整大根堆。for(let i = arr.length - 1; i >= 0; i--){heapify(arr, i, arr.length);}//不断把最大的摘下来,放在最后。let heapSize = arr.length;swap(arr, 0, --heapSize);while(heapSize > 0){heapify(arr, 0, heapSize);swap(arr, 0, --heapSize);}
}
//已知一个几乎有序的数组,选择合适的算法对数组进行排序,要求数组排好顺序后,每个元素移动的距离可以不超过k,并且k相对于数组来说比较小。
function sortedArrDistanceLessK(arr, k){let heap = new Array();let index = 0;//把数组前k个数放进堆里,调整堆。//求出数组长度和k中的最小值(防止k>>数组长度)for(; index < Math.min(arr.length, k); index++){heap.push(arr[index]);heapInsert(heap, index);}let i = 0;//不断把数组中k+1个数放进堆里,调整堆,弹出堆顶。for(; index < arr.length; i++, index++){heap[index] = arr[index];heapInsert(heap, index);//弹出堆顶,放在i位置上。arr[i] = heap[0];heap[0] = heap[heap.length-1];heap.pop();heapify(heap, 0, heap.length);}//堆里还有剩余while(heap.length>0){arr[i++] = heap[0];heap[0] = heap[heap.length-1];heap.pop();heapify(heap, 0, heap.length);}
}
//数据流,实时找出中位数
//建一个大根堆和小根堆。
//对于每个数字cur:(1)cur<=大根堆堆顶,加入大根堆;否则加入小根堆。(2)比较大小根堆的大小,如果相差>=2,摘下较多的堆的对顶加入另一个堆。

非基于比较的排序

(1)计数排序

  • 建立数组,下标即为数字,统计数字出现的频率。

(2)桶排序(基数排序)

function radixSortMain(arr){if(arr === null || arr.length < 2)return;radixSort(arr, 0, arr.length - 1, maxbits(arr));
}
//求最多有几位十进制数。
function maxbits(arr){let max = Number.MAX_VALUE;for(let i = 0; i < arr.length; i++){max = Math.max(max, arr[i]);}let res = 0;while(max !== 0){res++;max /= 10;}return res;
}
function radixSort(arr, L, R, digit){const radix = 10;let i = 0, j = 0;let bucket = new Array(R-L+1);for(let d = 1; d <= digit; d++){//原count数组,count[i]表示d位上,i出现的次数。//现count数组,count[i]等于count[i]前面的数字累加,表示d位上<=i的有几个(每个数字的片加起来);每个数该放在bucket数组下标=count[i]-1。let count = new Array(radix);//统计出现的次数for(i = L; i <= R; i++){j = getDigit(arr[i], d);count[j]++;}//累加for(i = 1; i < radix; i++){count[i] = count[i] + count[i-1];}//从右往左遍历数组(保证先入桶的先出桶)for(i = R; i >= L; i--){j = getDigit(arr[i], d);bucket[count[j]-1] = arr[i];count[j]--;}for(i = L, j = 0; i <= R; i++, j++){arr[i] = bucket[j];}}
}
function getDigit(x, d){return ((x / (Math.pow(10, d-1)))%10);
}

2、二分查找

有序数组,找某个数是否存在。

  • 时间复杂度O( logN)

有序数组,找>=某个数最左侧的位置。

  • 时间复杂度O(logN)

局部最小值
无序数组,相邻数一定不相等,求局部最小值。

  • 时间复杂度O(N)

3、哈希表

哈希表

  • 增删改查操作的时间复杂度可以认为是O(1),但是常数时间比较大。
  • 放入哈希表的东西,如果是基础类型,内部按值传递,内存占用的是这个东西的大小;如果不是基础类型,内部按引用传递,内存占用的是这个东西内存地址的大小。
  • JS中的Object

有序表

  • 有序表和哈希表的区别是,有序表把key按照顺序组织起来,而哈希表完全不组织。
  • 放入有序表的东西,如果是基础类型,内部按值传递,内存占用的是这个东西的大小;如果不是基础类型, 必须提供比较器,内部按引用传递,内存占用的是这个东西内存地址的大小。
  • 红黑树、AVL树、size-balance-tree和跳表都属于有序表。
  • JS中的Map、Set

4、链表

//反转单链表
//leetcode-206
//指针法
var reserveList = function(head){let prev = null;let curr = head;let next = head;while(curr !== null){next = curr.next;curr.next = prev;prev = curr;curr = next;}return prev;
};
//指针法-简写(利用解构赋值)
var reverseList = function(head){let prev = null;let curr = head;while(curr !== null){[curr.next, prev, curr] = [prev, curr, curr.next];}return prev;
};
//递归法
var reverseList = function(head){if(head === null || head.next === null){return head;}//last用来指向反转后的头节点const last = reserveList(head.next);head.next.next = head;head.next = null;return last;
};//反转双链表
var reserve = function(head){let prev = null;let curr = head;while(curr!==null){curr.prev = curr.next;curr.next = prev;prev = curr;curr = curr.prev;//[curr.prev, curr.next, prev, curr] = [curr.next, prev, curr, curr.prev];}return prev;
};
//打印两个有序链表的公共部分
//牛客网-程序员bla-CD48(os:牛客网真难用)
//谁小谁移动,相等打印并移动,有一个越界停。
var print = function(head1, head2){let p1 = head1, p2 = head2;while(p1!==null && p2!==null){if(p1.data < p2.data){p1 = p1.next;}else if(p1.data > p2.data){p2 = p2.next;}else{console.log(p1.data);p1 = p1.next;p2 = p2.next;}}
}
//分割链表
//分成<,=,>区版本
//定义六个指针变量sH/sT/eH/eT/bH/bT
var partition = function(head, x){let sH = null;let sT = null;let eH = null;let eT = null;let bH = null;let bT = null;let next = null;while(head !== null){//next记录下一个节点。next = head.next;//摘下头节点。head.next = null;if(head.val < x){if(sH === null){sH = head;sT = head;}else{sT.next = head;sT = head;}}else if(head.val === x){if(eH === null){eH = head;eT = head;}else{eT.next = head;eT = head;}}else{if(bH === null){bH = head;bT = head;}else{bT.next = head;bT = head;}}head = next;}//s区非空,连接s区和e区if(sH !== null){sT.next = eH;//判断e区是否为空eT = eT === null? sT : eT;}//e区非空,连接e区和b区if(eH !== null){eT.next = bH;}return sH !== null ? sH : (eH !== null ? eH : bH);
};//分成<,>=区版本
//leetcode-86
var partition = function(head, x){let sH = null;let sT = null;let eH = null;let eT = null;let next = null;while(head !== null){//next记录下一个节点。next = head.next;//摘下头节点。head.next = null;if(head.val < x){if(sH === null){sH = head;sT = head;}else{sT.next = head;sT = head;}}else{if(eH === null){eH = head;eT = head;}else{eT.next = head;eT = head;}}head = next;}//s区非空,连接s区和e区if(sH !== null){sT.next = eH;}return sH !== null ? sH : eH;
};
//判断回文链表
//leetcode-234
var isPalindrome = function(head){let slow = head;let fast = head;while(fast !== null && fast.next !== null){//慢指针一个一个走;快指针走双倍。//如果链表长度是奇数,最后slow走到中间,fast走到end。//如果链表长度是偶数,最后slow走到中间后一个,fast走到end+1(null)。slow = slow.next;fast = fast.next.next;}//链表长度是奇数,slow还需要往后走一个。if(fast !== null){slow = slow.next;}//分成左右两块开始遍历let left = head;//将slow后面的链表反转let right = reverse(slow);while(right !== null){if(left.val !== right.val)return false;left = left.next;right = right.next;}return true;
};
var reverse = function(head){let prev = null;let curr = head;while(curr !== null){[curr.next, prev, curr] = [prev, curr, curr.next];}return prev;
};
//复制带随机指针的链表
//leetcode-138
//用哈希表
var copyRandomList = function(head){if(!head) return head;let map = new Map();let cur = head;while(cur !== null){//key为老节点,value为新节点map.set(cur, new Node(cur.val));cur = cur.next;}cur = head;while(cur !== null){//根据map.get来查map,从而设置新节点。map.get(cur).next = map.get(cur.next) || null;map.get(cur).random = map.get(cur.random) || null;cur = cur.next;}return map.get(head);
}//骚操作
var copyRandomList = function(head){if(!head) return head;let cur = head;let next = null;while(cur !== null){next = cur.next;//把每个节点的克隆节点放在每个节点的后面。cur.next = new Node(cur.val);//克隆节点串上原来的nextcur.next.next = next;cur = next;}cur = head;//拷贝random指向//遍历新节点let curCopy = null;while(cur !== null){//遍历老节点next = cur.next.next;curCopy = cur.next;curCopy.random = cur.random !== null ? cur.random.next : null;cur = next;}const res = head.next;cur = head;//拷贝nextwhile(cur !== null){next = cur.next.next;curCopy = cur.next;//修改新旧节点的next指向cur.next = next;curCopy.next = next !== null ? next.next : null;cur = next;}return res;
}
//无环或有环链表相交节点
function getIntersectNode(head1, head2){if(head1 === null || head2 === null){return null;}let loop1 = getLoopNode(head1);let loop2 = getLoopNode(head2);if(loop1 === null && loop2 === null){return noLoop(head1, head2);}if(loop1 !== null && loop2 !== null){return bothLoop(head1, loop1, head2, loop2);}  return null;
}function getLoopNode(head){if(head === null || head.next === null || head.next.next === null){return null;}//如果链表有环,快慢指针会相遇;且快慢指针在环中转的圈数不会大于两圈,因为快指针比慢指针快了一倍。let slow = head.next;let fast = head.next.next;while(slow !== fast){if(fast.next === null || fast.next.next === null){return null;}fast = fast.next.next;slow = slow.next;}//当快慢指针相遇,快指针回到头,快慢指针一起一步一步走,相遇时则为环的入节点。fast = head;while(fast !== slow){fast = fast.next;slow = slow.next;}return fast;
}
//求两个无环链表相交节点
function noLoop(head1, head2){if(head1 === null || head2 === null){return null;}let cur1 = head1;let cur2 = head2;let n = 0;while(cur1.next !== null){n++;cur1 = cur1.next;}while(cur2.next !== null){n--;cur2 = cur2.next;}if(cur1 !== cur2){return null;}//求出差值n,让cur1指向长链表cur1 = n > 0? head1:head2;cur2 = cur1 === head1? head2:head1;n = Math.abs(n);//长链表往前走差值步数while(n !== 0){n--;cur1 = cur1.next;}while(cur1 !== cur2){cur1 = cur1.next;cur2 = cur2.next;}return cur1;
}
//一个有环,一个无环是不可能相交的。
//求两个有环链表相交节点
function bothLoop(head1, loop1, head2, loop2){let cur1 = null;let cur2 = null;//首环节点相同if(loop1 === loop2){cur1 = head1;cur2 = head2;let n = 0;while(cur1 !== loop1){n++;cur1 = cur1.next;}while(cur2 !== loop2){n--;cur2 = cur2.next;}cur1 = n > 0? head1:head2;cur2 = cur1 === head1? head2: head1;n = Math.abs(n);while(n !== 0){n--;cur1 = cur1.next;}while(cur1 !== cur2){cur1 = cur1.next;cur2 = cur2.next;}return cur1;}else{//首环节点不相同cur1 = loop1.next;while(cur1 !== loop1){if(cur1 === cur2){return loop1;}cur1 = cur1.next;}return null;}
}

5、二叉树

//递归遍历(每个节点遍历3次)
function recursion(head){//1if(head === null){return;}//1recursion(head.left);//2//2recursion(head.right);//3//3
}
//先序遍历(在1的时候打印)
function preOrderRecur(head){if(head === null){return;}console.log(head.value);preOrderRecur(head.left);preOrderRecur(head.right);
}
//中序遍历(在2的时候打印)
function inOrderRecur(head){if(head === null){return;}inOrderRecur(head.left);console.log(head.value);inOrderRecur(head.right);
}
//后序遍历(在3的时候打印)
function posOrderRecur(head){if(head === null){return;}posOrderRecur(head.left);posOrderRecur(head.right);console.log(head.value);
}//非递归版本
//先序遍历:根左右
function preOrderUnRecur(head){if(head !== null){let stack = new Array();stack.push(head);while(stack.length > 0){//弹出节点,处理head = stack.pop();console.log(head.value);//右节点压入if(head.right !== null){stack.push(head.right);}//左节点压入if(head.left !== null){stack.push(head.left);}}}
}
//中序遍历:左根右
function inOrderUnRecur(head){if(head !== null){let stack = new Array();while(stack.length > 0 || head !== null){//左边界进栈if(head !== null){stack.push(head);head = head.left;}else{//弹出节点,走右树head = stack.pop();console.log(head.value);head = head.right;}}}
}
//后序遍历:左右根(利用收集栈)
function posOrderUnRecur(head){if(head !== null){let stack1 = new Array();let stack2 = new Array();stack1.push(head);while(stack1.length > 0){//弹出节点,压入收集栈head = stack1.pop();stack2.push(head);//左节点压入if(head.left !== null){stack1.push(head.left);}//右节点压入if(head.right !== null){stack1.push(head.right);}}while(stack2.length > 0){console.log(stack2.pop().value);}}
}
//层序遍历(广度优先)
//用队列
function floorTraversing(head){if(head !== null){let queue = new Array();queue.push(head);while(queue.length > 0){//队头出队let cur = queue.shift();console.log(cur.value);//左孩子进队if(cur.left !== null){queue.push(cur.left);}//右孩子进队if(cur.right !== null){queue.push(cur.right);}}}
}
//求二叉树的最大宽度
//哈希表
function maxWidth(head){if(head !== null){let queue = new Array();let levelMap = new Map();queue.push(head);levelMap.set(head, 1);let curLevel = 1;//当前层数let curLevelNodes = 0;//当层节点个数let max = 0;//最大层数while(queue.length > 0){let cur = queue.shift();let curNodeLevel = levelMap.get(cur);if(curNodeLevel === curLevel){curLevelNodes++;}else{//该更新了max = Math.max(max, curLevelNodes);curLevel++;curLevelNodes = 1;}if(cur.left !== null){queue.push(cur.left);levelMap.set(cur.left, curNodeLevel+1);}if(cur.right !== null){queue.push(cur.right);levelMap.set(cur.right, curNodeLevel+1);}}//更新最后一层max = Math.max(max, curLevelNodes);return max;}
}
//判断搜索二叉树
//中序遍历依次升序就是搜索二叉树
//1
var preValue = Number.MIN_VALUE;
function isBST(head){if(head === null){return true;}if(!isBST(head.left)){return false;}if(head.value <= preValue){return false;}else{preValue = head.value;}return isBST(head.right);
}
//2
function checkBST(head){let list = new Array();process(head, list);for(let i = 0; i < list.length - 1; i++){if(list[i] > list[i+1]){return false;}}return true;
}
function process(head, list){if(head === null){return;}process(head.left, list);list.add(head);process(head.right, list);
}
//3
function isBST(head){if(head !== null){let preValue = Number.MIN_VALUE;let stack = new Array();while(stack.length > 0 || head !== null){//左边界进栈if(head !== null){stack.push(head);head = head.left;}else{//弹出节点,走右树head = stack.pop();if(head.value <= preValue){return false;}else{preValue = head.value;}head = head.right;}}}
}//判断完全二叉树
function isCBT(head){if(head === null){return true;}let queue = new Array();let leaf = false;//是否遇到左右两个孩子不双全的节点let l = null;let r = null;queque.push(head);while(queue.length > 0){head = queue.shift();l = head.left;r = head.right;//左孩子为空,右孩子不为空 or leaf开启后遇到的不是叶节点if(l === null && r !== null|| leaf && (l!==null || r!==null)){return false;}if(l !== null){queue.push(l);}if(r !== null){queue.push(r);}//遇到左右两个孩子不双全时(要么l有r空,要么l有r有)if(l === null || r === null){leaf = true;}}return true;
}//判断满二叉树
//可以用以下套路//判断平衡二叉树
//树型dp套路
function isBT(head){//>=0返回高度是平衡二叉树;<0不是平衡二叉树。return process(head) >= 0;
}
function process(head){if(head === null){return 0;}let lh = process(head.left);let rh = process(head.right);//如果这棵子树是平衡二叉树if(lh >= 0&& rh >= 0 && Math.abs(lh - rh) < 2){return Math.max(lh, rh) + 1;}return -1;
}
//给定两个二叉树的节点,找到他们的最低公共祖先节点。
//leetcode-236
//1,超时了好吧
function lca(head, n1, n2){//构造fatherMaplet fatherMap = new Map();fatherMap.set(head, head);process(head, fatherMap);let set = new Set();//n1往上遍历let cur = n1;//当等于自己(根节点),跳出while(cur !== fatherMap.get(cur)){set.add(cur);cur = fatherMap.get(cur);}set.add(head);//n2往上遍历cur = n2;while(!set.has(fatherMap.get(cur))){cur = fatherMap.get(cur);}return cur;
}
function process(head, map){if(head === null){return;}map.set(head.left, head);map.set(head.right, head);process(head.left, map);process(head.right, map);
}
//2
function lca(head, n1, n2){if(head === null || head === n1 || head === n2){return head;}let left = lca(head.left, n1, n2);let right = lca(head.right, n1, n2);//左右都不为空if(left!==null && right!==null){return head;}//左右一个为空,返回不空的return left !== null ? left : right;
}
//求中序遍历后继节点
function getSuccessorNode(node){if(node === null){return node;}//有右子树,后继节点为右子树中最左边的节点if(node.right !== null){return getLeftMost(node.right);}else{let parent = node.parent;//找到第一个为父亲的左子树的节点,该父亲就是后继节点while(parent!==null && parent.left!==node){node = parent;parent = node.parent;}return parent;}
}
function getLeftMost(node){if(node === null){return node;}while(node.left !== null){node = node.left;}return node;
}
//二叉树的序列化和反序列化
//leetcode-297
//序列化,按先序
function serialByPre(head){if(head === null){return "#_";}let res = head.value + '_';res += serialByPre(head.left);res += serialByPre(head.right);return res;
}
//反序列化,按先序
function reconByPre(str){let values = str.split('_');return process(values);
}
function process(queue){let value = queue.shift();if(value === '#'){return null;}let head = new Node(Number(value));head.left = process(queue);head.right = process(queue);return head;
}
//纸条的折痕
//二叉树中序遍历就是纸条从左到右的折痕顺序,根节点是凹,左孩子是凹,右孩子是凸。
function printAllFolds(n){//节点层数;总层数;是否为凹printProcess(1, n, true);
}
function printProcess(i, n, down){if(i > n){return;}printProcess(i+1, n, true);console.log(down?"凹":"凸");printProcess(i+1, n, false);
}

6、图

(略了略了,面试少遇到)

7、前缀树

class TrieNode{constructor(){this.past = 0;//经过几次this.end = 0;//以该节点为叶节点的字符有几个this.nexts = new Array(26);//走向26个字母的路//字符种类很多的时候用map}
}
function main(words){const root = new TrieNode();words.forEach(w=>{insert(w, root);});search('ab', root);prefixNumber('a', root);
}
//建立前缀树
function insert(word, root){if(word === null){return;}let node = root;node.pass++;let index = 0;for(let i = 0; i < word.length; i++){index = word[i] - 'a';if(node.nexts[index] === null){node.nexts[index] = new TrieNode();}node = node.nexts[index];node.pass++;}node.end++;
}
//搜索字符串出现的次数
function search(word, root){if(word === null){return 0;}let node = root;let index = 0;for(let i = 0; i < word.length; i++){index = word[i] - 'a';if(node.nexts[index] === null){return 0;}node = node.nexts[index];}return node.end;
}
//搜索以pre为前缀的字符串出现的次数
function prefixNumber(word, root){if(word === null){return 0;}let node = root;let index = 0;for(let i = 0; i < word.length; i++){index = word[i] - 'a';if(node.nexts[index] === null){return 0;}node = node.nexts[index];}return node.pass;
}
//删除字符串
function delete(word, root){//查询到有这个字符串才能进行删除操作if(search(word)){let node = root;let index = 0;for(let i = 0; i < word.length; i ++){index = word[i] - 'a';//下级的pass=0,删除整条下级if(--node.nexts[index].pass === 0){node.nexts[index] = null;return;}node = node.nexts[index];}node.end--;}
}

8、贪心算法

局部最优解=>答案

  • 根据某标准建立一个比较器来排序
  • 根据某标准建立一个比较器来组成堆
//合理地安排最多的会议
//类似leetcode-435
function cmp(a, b){return a.end - b.end;
}
function bestArrange(programs, timePoint){//按结束时间升序排列programs.sort(cmp);let res = 0;//先安排结束时间最早的for(let i = 0; i < programs.length; i++){//如果开始时间比时间线晚,即可安排if(timePoint <= programs[i].start){res ++;timePoint = programs[i].end;}}return res;
}
//n皇后问题:在n*n的棋盘上摆n个皇后,要求任何2个皇后不同行、不同列,也不在同一条斜线上。
//给定一个整数n,返回n皇后的摆法有多少种。
function nqueen(n){if(n === 0) return 0;//record[0] = 3表示第0行的第3列放着一个皇后。let record = new Array(n);return process(0, record, n);
}
function process(i, record, n){//来到终止行,返回1表示有一种方案if(i === n) return 1;let res = 0;//处理第i行该放在哪一列for(let j = 0; j < n; j++){if(isValid(record, i, j)){record[i] = j;res += process(i+1, record, n);}}return res;
}
function isValid(record, i , j){//看第i行之前的皇后放在哪for(let k = 0; k < i; k++){//判断是否同一列or在同一斜线(列差=行差)//不必判断是否同一行(因为处理是一行一行处理的,自然不会在同一行。if(j === record[k] || Math.abs(record[k] - j) === Math.abs(k - i)){return false;}}return true;
}//请不要超过32皇后问题(用位运算加速)
//bbq,不做了,再见。

【记录】算法 - JavaScript版相关推荐

  1. 连连看连接算法Javascript版

    继连连看连接算法Python版后,我将该算法移植到了Javascript上,为在浏览器版连连看做准备. 功能及使用方法参照另外一篇:连连看连接算法Python版. 值得一提的是由于Javascript ...

  2. 算法笔记(JavaScript版)——排序

    算法笔记(JavaScript版)--排序 本文内容根据Rebert Sedgewick和Kevin Wayne的<算法(第四版)>整理,原代码为java语言,自己修改为JavaScrip ...

  3. 地图着色问题javaScript版-- 《算法分析与设计》课程设计题目

    @地图着色问题javaScript版-- <算法分析与设计>课程设计题目 地图着色问题js版 已知中国地图,对各省进行着色,要求相邻省所使用的颜色不同,并保证使用的颜色 ,总数最少 地图着 ...

  4. mooc数据结构与算法python版期末测验_中国大学MOOC(慕课)_数据结构与算法Python版_测试题及答案...

    中国大学MOOC(慕课)_数据结构与算法Python版_测试题及答案 更多相关问题 采用fopen()函数打开文件,支持文件读取的参数有: [简答题]简单阐述高分子材料热-机械特征及成型加工的关系,并 ...

  5. 资料分享:送你一本《数据结构与算法JavaScript描述》电子书!

    数据结构 是掌握计算机编程必须具备的技能.通常情况下,我想掌握一门编程语言所用的方法就是利用这门语言把数据结构中线性表.栈.队列.字符串.动态数字.整数集合.树.图.搜索.排序等涉及的算法全部写一遍. ...

  6. 数据结构与算法JavaScript (一) 栈

    序 数据结构与算法JavaScript这本书算是讲解得比较浅显的,优点就是用javascript语言把常用的数据结构给描述了下,书中很多例子来源于常见的一些面试题目,算是与时俱进,业余看了下就顺便记录 ...

  7. mooc数据结构与算法python版期末考试_数据结构与算法Python版-中国大学mooc-试题题目及答案...

    数据结构与算法Python版-中国大学mooc-试题题目及答案 更多相关问题 婴儿出生一两天后就有笑的反应,这种笑的反应属于(). [判断题]填制原始凭证,汉字大写金额数字一律用正楷或草书书写,汉字大 ...

  8. 微信公众号推送课表及天气(事无巨细+JavaScript版+python版)

    微信公众号推送课表及天气 22.10.16更新 我已经做出来更优秀的直接在聊天列表中推送的了! 请移步 企业微信推送 超强版! 基础 简介 作者:申也 个人网站:申也个人网站(www.dongshen ...

  9. java 随机金额_java_微信随机生成红包金额算法java版,最近几年玩得最疯狂的应该是 - phpStudy...

    微信随机生成红包金额算法java版 最近几年玩得最疯狂的应该是发红包了,尤其是过年的时候特别受欢迎,下面写了红包的随机算法,其实挺简单的,仅是提供一种思路,希望可以给大家一些启发. public cl ...

最新文章

  1. applet打包的MANIFEST.MF配置
  2. Poj-1088-滑雪
  3. Nexenta和ParaScale发布开源存储产品
  4. varchar保存带格式的数据_软件测试必备之数据库知识(一)
  5. 迅速掌握Unix编程
  6. 为div添加滚动效果:
  7. mac xampp连接mysql数据库_在mac上如何使用终端打开XAMPP自带的MySQL
  8. iOS开发之解析XML格式数据
  9. Spring Cloud 是什么
  10. PHP踩坑:对象的引用
  11. Unity WebGL Input输入中文
  12. centos7安装telnet命令
  13. OSGeolive 开源GIS 使用
  14. 基于WxPython的批量图片缩放工具
  15. C语言编辑时绝对值怎么输,互联网常识:c语言绝对值怎么打
  16. Unity3d:一个简单的画圈圈手势判断
  17. 深度解析:电商直播基地运营及盈利模式
  18. python打开pdf文档
  19. python中jieba库的安装
  20. Gartner:2018年十大安全项目详解

热门文章

  1. c++11多线程编程同步——使用条件变量condition variable
  2. CSS3动画、定位小结
  3. qdialog 返回值_PyQt QDialog - 返回一个值并从对话框中关闭
  4. 游戏平台运营核心是什么?
  5. 我在企业中是怎么简单使用Git的参与团队开发的?Git使用教程
  6. CentOs7 + Stable Diffusion + Novel AI实现AI绘画
  7. JDK1.8 ConcurrentHashMap 源码解析
  8. 富文本处理(QTextEdit)
  9. 为什么说寒湿是万病之源?【瑶一堂】三味真火养生馆
  10. python 常用标准库