跟着瓶子刷算法Leetcode-hot100-ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 function twoSum (nums: number[], target: number ): number[] { let helperMap = new Map (); let resArr = []; let resIndex = undefined ; for (let i = 0 ; i< nums.length ; i++){ if (helperMap.has (target - nums[i])){ resIndex = helperMap.get (target - nums[i]) if (resIndex !== undefined ){ resArr = [resIndex, i] break ; } } helpMap.set (nums[i], i) } return resArr; }
跟着瓶子刷算法
1 2 3 4 function intersection (num1: number[], num2: number[] ): number[] { return [...new Set (num1.filter (item => num2.includes (item)))] }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 function threeSum (nums: number[] ): number[][] { const len = nums.length ; const res = []; if (len < 3 ) { return res } nums.sort ((a, b ) => a - b) for (let i = 0 ; i < len -2 ; i++){ if (nums[i] > 0 ){ break ; } if (i && nums[i] === nums[i-1 ]){ continue ; } let left = i + 1 ; let right = len -1 while (left < right){ const sum = nums[i] + nums[left] + nums[right] if (sum === 0 ){ res.push ([nums[i], nums[left], nums[right]]) while (left < right && nums[left] === nums[left + 1 ]){ left++ } while (left < right && nums[right] === nums[right -1 ]){ right-- } left++ right-- } else if (sum > 0 ){ right -- } else { left++ } } } return res }
1 2 3 4 5 6 7 8 9 10 11 12 13 function groupAnagrams (strs: string [] ): string [][] { let map : Map <string , Array <string >> = new Map (); for (let i=0 ; i<strs.length ; i++){ let key = strs[i].split ("" ).sort ().join ("" ) if (!map.has (key)){ map.set (key, []) } map.get (key)!.push (strs[i]) } return [...map.values ()] }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 function longestConsecutive (nums: number[] ): number { const numSet : Set <number> = new Set <number>(nums); let longestSequence : number = 0 ; for (const currentNum of numSet){ const isStartOfSequence : boolean = !numSet.has (currentNum - 1 ) if (isStartOfSequence){ let sequenceLength : number = 1 ; let nextNum : number = currentNum + 1 ; while (numSet.has (nextNum)){ sequenceLength++; nextNum++ } longestSequence = Math .max (longestSequence, sequenceLength); } } return longestSequence; }
1 2 3 4 5 6 7 8 9 10 11 function moveZeroes (nums: number[] ): void { let left = 0 ; for (let right=0 ; right<nums.length ; right++){ if (nums[right]!==0 ){ [nums[left], nums[right]] = [nums[right], nums[left]]; left++ } } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 function maxArea (height: number[] ): number{ let left = 0 ; let right = height.length - 1 ; let maxArea = 0 while (left<right){ const width = right - left; const currentHeight = Math .min (height[left], height[right]); const area = width * currentHeight; maxArea = Math .max (maxArea, area); if (height[left]<height[right]){ left++ } else { right-- } } return maxArea; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 function threeSum (nums: number[] ): number[][]{ const result : number[][] = []; nums.sort ((a, b ) => a - b) for (let i=0 ; i<nums.length -2 ;i++){ if (i>0 && nums[i]===nums[i-1 ]) continue ; let left=i+1 ; let right=nums.length -1 ; while (left<right){ const sum=nums[i]+nums[left]+nums[right]; if (sum===0 ){ result.push ([nums[i],nums[left],nums[right]]) while (left<right && nums[left]===nums[left+1 ]) left++; while (left<right && nums[right] === nums[right-1 ]) right--; left++; right-- } else if (sum<0 ){ left++; } else { right--; } } } return result; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 function trap (height: number[] ): number{ let left=0 ; let right=height.length -1 ; let leftMax=0 ; let rightMax=0 ; let result=0 ; while (left<right){ if (height[left]<height[right]){ if (height[left]>=leftMax){ leftMax=height[left]; }else { result+=leftMax-height[left] } left++ } else { if (height[right] >= rightMax){ rightMax=height[right] }else { result+=rightMax-height[right] } right-- } } return result; }
链表 1 2 3 4 5 理解改指针。插入X,A → B X.next = A.next A.next = X A → X → B head 是 入口,丢了 head,整个链表就找不到了
跟着瓶子刷算法Leetcode-hot100-ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 function mergeTwoLists (list1: ListNode | null , list2: ListNode | null ): ListNode | null { if (list1 === null ){ return list2 } if (list2 === null ){ return list1 } if (list1.val <= list2.val ){ list1.next = mergeTwoLists (list1.next , list2) return list1 } else { list2.next = mergeTwoLists (list2.next , list1) return list2 } };
从尾到头打印链表 牛客网剑指offer
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 function printListFromTailToHead (head ){ let arr = []; while (head){ arr.push (head.val ) head = head.next } return arr.reverse () } function printListFromTailToHead1 (head ){ if (!head) return [] let result = printListFromTailToHead (head.next ) result.push (head.val ) return result }
牛客网剑指offerLeetcode-hot100-ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 function reverseList (head ){ if (!head){ return null ; } let prev = null , cur = head, next while (cur){ next = cur.next ; cur.next = prev; prev = cur; cur = next; } return prev }
牛客网剑指offer
1 2 3 4 5 6 7 8 9 10 function FindFirstCommonNode (pHead1, pHead2 ){ let p1 = pHead1, p2 = pHead2; while (p1 !== p2){ p1 = p1 ? p1.next : pHead2; p2 = p2 ? p2.next : pHead1; } return p1 }
LCR 022. 环形链表 II,链表中环的入口结点 牛客网剑指offer
1 2 3 4 5 6 7 8 9 10 11 12 function entryNodeOfLoop (pHead ){ let arr = []; while (pHead){ if (arr.indexOf (pHead) !== -1 ){ return pHead; } else { arr.push (pHead) pHead = pHead.next } } return null ; }
运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制。它应该支持以下操作:获取数据 get 和写入数据 put。
获取数据 get(key) - 如果密钥存在于缓存中,则获取密钥的值(总是正数),否则返回 -1
写入数据 put(key, value) - 如果密钥不存在,则写入数据。当缓存容量达到上限时,它应该在写入新数据之前删除最久未使用的数据,从而为新数据留出空间
进阶 :你是否可以在 O(1) 时间复杂度内完成这两种操作?
示例 :
1 2 3 4 5 6 7 8 9 10 11 LRUCache cache = new LRUCache (2 ); cache.put (1 , 1 ); cache.put (2 , 2 ); cache.get (1 ); cache.put (3 , 3 ); cache.get (2 ); cache.put (4 , 4 ); cache.get (1 ); cache.get (3 ); cache.get (4 );
实现 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 class LRUCache { max : number = 0 ; cache : Map <number , number >; constructor (capacity: number ) { this .max = capacity; this .cache = new Map (); } get (key : number ): number { const { cache } = this ; let value = cache.get (key); if (value !== undefined ) { cache.delete (key); cache.set (key, value); return value; } else { return -1 ; } } put (key : number , value : number ): void { const { cache } = this ; if (cache.has (key)) { cache.delete (key); } else if (cache.size >= this .max ) { const firstKey = cache.keys ().next ().value ; cache.delete (firstKey); } cache.set (key, value); } }
树 TODO
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 const kthLargest = function (root, k ){ if (!root) return 0 ; const stack = []; while (stack.length || root){ while (root){ stack.push (root); root = root.right ; } const cur = stack.pop (); k--; if (k===0 ) return cur.val ; root = cur.left ; } return 0 }
1 2 3 4 5 6 7 function TreeDepth (pRoot ){ if (!pRoot) { return 0 ; } else { return Math .max (TreeDepth (pRoot.left ), TreeDepth (pRoot.right )) + 1 ; } }
十大排序算法 我记不住怎么办,每次从冒泡开始,就像是学习英语,每次只背了个abandon。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 排序算法 ├─ O(n²) 简单排序 │ ├─ 冒泡 Bubble │ ├─ 选择 Selection │ └─ 插入 Insertion │ ├─ O(n log n) 高级排序(基于分治) │ ├─ 归并 Merge —— 非原地 + 稳定 │ └─ 快排 Quick —— 原地 + 不稳定 │ ├─ O(n log n) 不基于分治 │ └─ 堆排序 Heap — 原地 + 不稳定 │ └─ O(n) 非比较排序(限定数据范围) ├─ 计数排序 Counting ├─ 桶排序 Bucket └─ 基数排序 Radix
冒泡排序 两两比较,大的往后沉,双重循环,交换!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 function bubbleSort (arr ){ for (let i = 0 ; i< arr.length ; i++){ for (let j = 0 ; j< arr.length - i - 1 ; j++){ if (arr[j] > arr[j+1 ]){ const temp = arr[j] arr[j] = arr[j+1 ] arr[j+1 ] = temp } } } return arr } function bubbleSort1 (arr ){ for (let i = 0 ; i< arr.length ; i++){ for (let j = 0 ; j< arr.length - i - 1 ; j++){ let flag = false if (arr[j] > arr[j+1 ]){ const temp = arr[j] arr[j] = arr[j+1 ] arr[j+1 ] = temp flag = true } if (!flag) break } } return arr }
选择排序 每次选最小,放到前面,固定位置,找最小index
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 function selectionSort (arr ) { let length = arr.length ; let indexMin; for (let i = 0 ; i < length - 1 ; i++) { indexMin = i; for (let j = i + 1 ; j < length; j++) { if (arr[indexMin] > arr[j]) { indexMin = j; } } if (i !== indexMin) { let temp = arr[i]; arr[i] = arr[indexMin]; arr[indexMin] = temp; } } return arr; }
插入排序 前面是有序区,把当前数插进去,适合几乎有序数组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 ((arr ) => { for (let i=1 ; i<arr.length ; i++){ let current = arr[i]; let = j = i-1 ; while (j>=0 && arr[j] > current){ arr[j+1 ] = arr[j] j-- } arr[j+1 ] = current } return arr })([1 ,5 ,4 ,2 ,9 ,7 ,8 ])
*** 快速排序(Top1 高频) 快速排序其实是分治法,将待排序数组里的项和基准数对比,比基准数大的放在一边,小的放另一边,然后再对左右两边的子数组重复使用这个思路,直到整个数组排序完毕。 选基准,比它小的放左边,比它大的放右边。里面比较的是合法下标哦,是合法下标的区间哦!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 function quickSort (arr, left, right ) { if (left >= right) return ; let i = left; let j = right; let pivot = arr[left]; while (i < j) { while (arr[j] >= pivot && i < j) j--; while (arr[i] <= pivot && i < j) i++; if (i < j) { [arr[i], arr[j]] = [arr[j], arr[i]]; } } arr[left] = arr[i]; arr[i] = pivot; quickSort (arr, left, i - 1 ); quickSort (arr, i + 1 , right); return arr; } console .log (quickSort ([1 ,5 ,4 ,2 ,9 ,7 ,8 ], 0 , 6 ));
*** 堆排序(面试经常问“Top K”) 建立大顶堆,堆顶最大,不断弹出再重建。 React Fiber = 一棵被不断“重新排序”的最小堆结构 → 面试官特别爱问堆。 React Fiber 的本体结构是一串 单向链表 + 树: React Fiber 的调度器(Scheduler)用的是“最小堆(min-heap)”结构; React Fiber 的任务执行顺序(Fiber 树构建 / Reconciliation)本身是用“可中断的链表(队列)结构”。 所以: Fiber = 链表结构(队列式遍历) Scheduler = 用最小堆来决定“下一件事做谁” 这两者完全不冲突,只是层级不一样。
React Fiber 本身不是堆,它是一套链表结构的 Fiber 树,用来支持可中断更新。 但 React 的调度器 Scheduler 为了按优先级选择下一个任务,使用了“最小堆”作为优先级队列。 所以:Fiber = 链表;调度 = 堆。
堆的特性: 必须是完全二叉树; 任一结点的值是其子树所有结点的最大值或最小值。 最大值时,称为”最大堆”,也称大顶堆; 最小值时,称为”最小堆”,也称小顶堆。 堆排序主要用到最大堆/最小堆的删除操作,也即根节点已经是最大/小的值,排序的话,只需要把根结点拿(删)掉,放入有序的新数组里,然后用下沉算法处理剩余的结点以便组成新的最大堆/最小堆……如此循环。 所谓下沉算法,拿最小堆来举例说(最大堆同理),就是把完全二叉树根结点R和该树第二层左右子结点的值比较,如果大,结点就互换位置(”下沉”),以此逐层递归,直到处理完整棵树,此时,根节点值最小。
堆的本质是完全二叉树,而完全二叉树最典型的表示方式就是“连续数组”。 只要数组里没有空洞(每个 index 都能访问到),它天然可以作为堆结构。 只要保证数组连续,堆排序一定能用。
1 2 3 4 5 6 function isContinuousArray (arr ) { for (let i = 0 ; i < arr.length ; i++) { if (!(i in arr)) return false ; } return true ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 ((arr ) => { let result = [] buildMinHeap (arr) for (let i = 0 , length = arr.length ; i< length; i++){ result.push (arr[0 ]) swap (0 , length-result.length ) sink (arr, 0 , length-result.length ) } function buildMinHeap (arr ){ let length = arr.length ; let currentIndex; for (currentIndex = Math .floor ((length - 2 ) / 2 ); currentIndex >= 0 ; currentIndex--){ console .log ('正在处理的下沉结点索引' + currentIndex) sink (arr, currentIndex, length) } } function sink (arr, currentIndex, length ){ let minIndex = currentIndex let leftChildIndex = 2 *currentIndex + 1 let rightChildIndex = 2 *currentIndex + 2 if (leftChildIndex < length && arr[leftChildIndex] < arr[minIndex]){ minIndex = leftChildIndex } if (rightChildIndex < length && arr[rightChildIndex] < arr[minIndex]){ minIndex = rightChildIndex } if (minIndex !== currentIndex){ swap (minIndex, currentIndex) sink (arr, minIndex, length) } } function swap (x, y ){ let temp = arr[x] arr[x] = arr[y] arr[y] = temp } return result })([1 ,5 ,4 ,2 ,9 ,7 ,8 ])
计数排序(不太会) 统计次数,再按次数依次输出,只适合整数小范围。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 ((arr ) => { function countingSort (arr ) { const n = arr.length ; let max = arr[0 ]; let min = arr[0 ]; for (let i = 1 ; i < n; i++) { if (arr[i] > max) max = arr[i]; if (arr[i] < min) min = arr[i]; } const range = max - min + 1 ; const count = new Array (range).fill (0 ); const output = new Array (n); for (let i = 0 ; i < n; i++) { count[arr[i] - min]++; } for (let i = 1 ; i < range; i++) { count[i] += count[i - 1 ]; } for (let i = n - 1 ; i >= 0 ; i--) { output[count[arr[i] - min] - 1 ] = arr[i]; count[arr[i] - min]--; } for (let i = 0 ; i < n; i++) { arr[i] = output[i]; } return arr; } const sortedArr = countingSort (arr); console .log (sortedArr); })([1 , 5 , 4 , 2 , 9 , 7 , 8 ]);
桶排序 分桶 → 各桶排序 → 合并 适合均匀分布的数据。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 ((arr ) => { function bucketSort (arr, bucketSize = 5 ) { if (arr.length === 0 ) return arr; let min = arr[0 ]; let max = arr[0 ]; for (let i = 1 ; i < arr.length ; i++) { if (arr[i] < min) min = arr[i]; if (arr[i] > max) max = arr[i]; } const bucketCount = Math .floor ((max - min) / bucketSize) + 1 ; const buckets = new Array (bucketCount); for (let i = 0 ; i < bucketCount; i++) { buckets[i] = []; } for (let i = 0 ; i < arr.length ; i++) { const bucketIndex = Math .floor ((arr[i] - min) / bucketSize); buckets[bucketIndex].push (arr[i]); } for (let i = 0 ; i < buckets.length ; i++) { buckets[i].sort ((a, b ) => a - b); } let index = 0 ; for (let i = 0 ; i < buckets.length ; i++) { for (let j = 0 ; j < buckets[i].length ; j++) { arr[index++] = buckets[i][j]; } } return arr; } const sortedArr = bucketSort (arr); console .log (sortedArr); })([1 , 5 , 4 , 2 , 9 , 7 , 8 ]);
基数排序 按个位、十位、百位依次排 用桶 + 多次扫描。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 ((arr ) => { function radixSort (arr ) { let max = arr[0 ]; for (let i = 1 ; i < arr.length ; i++) { if (arr[i] > max) max = arr[i]; } let maxDigit = 1 ; while (Math .floor (max / 10 ) > 0 ) { maxDigit++; max = Math .floor (max / 10 ); } for (let digit = 0 ; digit < maxDigit; digit++) { const buckets = Array .from ({length : 10 }, () => []); for (let i = 0 ; i < arr.length ; i++) { const bucketIndex = Math .floor (arr[i] / Math .pow (10 , digit)) % 10 ; buckets[bucketIndex].push (arr[i]); } let index = 0 ; for (let i = 0 ; i < 10 ; i++) { for (let j = 0 ; j < buckets[i].length ; j++) { arr[index++] = buckets[i][j]; } } } return arr; } const sortedArr = radixSort (arr); console .log (sortedArr); })([1 , 5 , 4 , 2 , 9 , 7 , 8 ]);
*** 归并排序(分治 + 面试能问很深) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 function mergeSort (arr ){ let array = mergeSortRec (arr) return array } function mergeSortRec (arr ){ let length = arr.length ; if (length === 1 ){ return arr; } let mid = Math .floor (length/2 ), left = arr.slice (0 , mid), right = arr.slice (mid, length) return merge (mergeSortRec (left), mergeSortRec (right)) } function merge (left, right ){ let result = [], ileft = 0 , iright=0 while (ileft<left.length && iright<right.length ){ if (left[ileft] < right[iright]){ result.push (left[ileft++]) } else { result.push (right[iright++]) } } while (ileft<left.length ){ result.push (left[ileft++]) } while (iright<right.length ){ result.push (right[iright++]) } return result }
希尔排序 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 ((arr ) => { function shellSort (arr ) { const n = arr.length ; for (let gap = Math .floor (n / 2 ); gap > 0 ; gap = Math .floor (gap / 2 )) { for (let i = gap; i < n; i++) { let temp = arr[i]; let j; for (j = i; j >= gap && arr[j - gap] > temp; j -= gap) { arr[j] = arr[j - gap]; } arr[j] = temp; } } return arr; } const sortedArr = shellSort (arr); console .log (sortedArr); })([1 , 5 , 4 , 2 , 9 , 7 , 8 ]); ### 找出字符串中不含重复字符的最长子串的长度 `` `js var lengthOfLongestSubstring = function(s){ let arr = []; let max = 0; for(let i = 0; len = s.length; ++i){ const sameIndex = arr.findIndex(item => item === s[i]) arr.push(s[i]) if(sameIndex > -1){ arr = arr.splice(sameIndex+1) } max = Math.max(arr.length, max) } return max; }
给定一个字符串,判定其能否排列成回文串 1 2 3 4 5 6 7 8 9 10 11 var canPermutePalindrome = function (s ){ const set = new Set () s.split ('' ).forEach (key => { if (set.has (key)){ set.delete (key) } else { set.add (key) } }) return set.size <= 1 }
反转链表 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 const node = { val : next : } var reverseList = function (head ){ if (!head){ return head; } let pre = null ; let cur = head; while (cur){ const {next} = cur; cur.next = pre; pre = cur; cur = next; } return pre }
二叉树的遍历 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 const node = { value : left : right : } var preorderTraversal = function (root ){ if (!root){ return []; } const result = []; result.push (root.val ) if (root.left ){ result.push (...preorderTraversal (root.left ))} if (root.right ){result.push (...preorderTraversal (root.right ))} return result } var inorderTraversal = function (root ){ if (!root){ return [] } let result = [] result = result.concat (inorderTraversal (root.left )) result.push (root.val ) result = result.concat (inorderTraversal (root.right )) return result; } var postorderTraversal = function (root ){ if (!root){ return [] } const result = [] result.push (...postorderTraversal (root.left )) result.push (...postorderTraversal (root.right )) result.push (root.val ) return result; }
实现一个全排列 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 function permute (arr ){ const result = []; function backtrack (subarr, remaining ){ if (remaining.length === 0 ){ result.push (subarr.slice ()) } else { for (let i=0 ; i<remaining.length ; i++){ subarr.push (remaining[i]); const newRemaining = [...remaining.slice (0 ,1 ), ...remaining.slice (i+1 )] backtrack (subarr, newRemaining) subarr.pop () } } } backtrack ([], arr); return result; } const inputArray = [1 ,2 ,3 ]const permutations = permute (inputArray)console .log (permutations)
快速找到链表的中间节点 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class ListNode { constructor (val, next = null ){ this .val = val; this .next = next; } } function findMiddleNode (head ){ let slow = head; let fast = head; while (fast !== null && fast.next !== null ){ slow = slow.next ; fast = fast.next .next } return slow }