做题总结

递归是由大到小的层层递进
dp是由小到大的逐渐积累
穷举是不断遍历获取所有结果

leetcode题目回顾&思路总结

undone:5
toReview:

经典题目

236.[树]Lowest Common Ancestor of a Binary Tree

image.png

解法1:

image.png

1663.[经典] Smallest String With A Given Numeric Value

image.png

解法1:

  1. /**
  2. * @param {number} n
  3. * @param {number} k
  4. * @return {string}
  5. */
  6. var getSmallestString = function(n, k) {
  7. var arr=new Array(n).fill(1)
  8. var all=k-n
  9. for(var i=n-1;i>=0;i--){
  10. if(all>25){
  11. arr[i]=26
  12. all=all-25
  13. }else{
  14. arr[i]=all+1
  15. break
  16. }
  17. }
  18. return arr.map(it=>String.fromCharCode(it+96)).join('')
  19. };

解法2[待更快速]:

78.[回溯]Subsets

image.png

解法1:

回溯算法

  1. var subsets = function(nums) {
  2. var res=[]
  3. function pan(tem=[],index=0,n){
  4. res.push(tem.slice(0))
  5. for(var i=index;i<n.length;i++){
  6. tem.push(n[i])
  7. pan(tem,i+1,n)
  8. tem.pop()
  9. }
  10. }
  11. pan([],0,nums)
  12. return res
  13. };

解法2:

类似70题方法3

264.Ugly Number II

image.png

解法1:

  1. var nthUglyNumber = function(n) {
  2. let multiplicationList = [2,3,5]
  3. let Answer = [1]
  4. let i2=0
  5. let i3=0
  6. let i5 = 0
  7. while (Answer.length<n){
  8. let m2 = Answer[i2]*2
  9. let m3 = Answer[i3]*3
  10. let m5 = Answer[i5]*5
  11. let newAnswer = Math.min(m2,m3,m5)
  12. if(newAnswer===m2){
  13. i2++
  14. }
  15. if(newAnswer===m3){
  16. i3++
  17. }
  18. if(newAnswer===m5){
  19. i5++
  20. }
  21. Answer.push(newAnswer)
  22. }
  23. return Answer[n-1]
  24. };

202.Happy Number

image.png

解法1:

我们可以用 map来记录所有出现过的数字,然后每出现一个新数字,在 map中查找看是否存在,若不存在则加入表中,若存在则跳出循环,并且判断此数是否为1,若为1返回true,不为1返回false,

22.[经典]Generate Parentheses

image.png

解法1:

枚举法,根据递归列举所有情况

  1. function generateParenthesis(n) {
  2. const res = [];
  3. function go(l, r, s) { // l: left remaining, r: right remaining
  4. if (l > r) return; // The number of '(' should be always >= ')'
  5. if (l === 0 && r === 0) {
  6. res.push(s);
  7. return;
  8. }
  9. if (l > 0) go(l - 1, r, s + '(');
  10. if (r > 0) go(l, r - 1, s + ')');
  11. }
  12. go(n, n, '');
  13. return res;
  14. }

5??.[经典]Longest Palindromic Substring

image.png

解法1:

回文字符串的中心要不是一个字符‘aba’,要不是两个字符‘aabb’,所以根据给定字符串的单个字符和双字符进行递归判断即可

  1. var longestPalindrome = function (s) {
  2. if (s.length < 2) { return s[0] || '' }
  3. var max = 0
  4. var res = ''
  5. function pan(x, y) {
  6. if (x < 0 || y > s.length - 1) { return }
  7. if (x === y) {
  8. if (max < 1) {
  9. max = 1
  10. res = s[x]
  11. }
  12. pan(x, x + 1)
  13. pan(x + 1, x + 1)
  14. }
  15. if (x === y - 1) {
  16. if (max < 2 && s[x] === s[y]) {
  17. max = 2
  18. res = s.slice(x, y + 1)
  19. }
  20. if (s[x] !== s[y]) { return }
  21. }
  22. if (x - 1 >= 0 && y + 1 <= s.length - 1 && s[x - 1] === s[y + 1]) {
  23. pan(x - 1, y + 1)
  24. } else {
  25. res = y - x + 1 > max ? s.slice(x, y + 1) : res
  26. max = y - x + 1 > max ? y - x + 1 : max
  27. }
  28. }
  29. pan(0, 0)
  30. return res
  31. };

322[DP]. Coin Change

image.png

解法1:

动态规划+缓存
image.png

解法2(时间更快的解):

  1. var coinChange = function(coins, amount) {
  2. var dp = Array(amount + 1).fill(Infinity);
  3. dp[0] = 0;
  4. for (let idx = 1; idx <= amount; idx++) {
  5. for (let i = 0; i < coins.length; i++) {
  6. var coin = coins[i];
  7. if (idx - coin < 0) {
  8. continue
  9. }
  10. dp[idx] = Math.min(dp[idx], dp[idx - coin] + 1)
  11. }
  12. }
  13. return dp[amount] === Infinity ? -1 : dp[amount]
  14. };

518[DP]. Coin Change 2

image.png

解法1:

相比于322,这题求的是组合数,基准应该是coin而不是amount,这点是最大的差异
现算出只用一个coin的情况下0-amount的结果,在算出coin增加时的dp结果

  1. var change = function(amount, coins) {
  2. var dp = new Array(amount + 1).fill(0)
  3. dp[0] = 1
  4. for (let i = 0; i < coins.length; i++) {
  5. const coin = coins[i];
  6. for (let idx = 0; idx <=amount; idx++) {
  7. if (idx - coin < 0) { continue }
  8. dp[idx] = dp[idx] + dp[idx - coin]
  9. }
  10. }
  11. return dp[amount]
  12. };

64[DP]. Minimum Path Sum
image.png

解法1(时间不够快):

动态规划+缓存

  1. var minPathSum = function (grid) {
  2. var map = new Array(grid.length)
  3. map[0] =[grid[0][0]]
  4. function pan(x, y) {
  5. if(!map[x]){map[x]=[]}
  6. if (map[x][y]) { return map[x][y] }
  7. if (x === 0 && y === 0) {
  8. console.log(x, y, map[x][y])
  9. return grid[x][y]
  10. }
  11. if (x === 0 && y !== 0) {
  12. map[x][y] = grid[x][y] + pan(x, y - 1)
  13. console.log(x, y, map[x][y])
  14. return map[x][y]
  15. }
  16. if (y === 0 && x !== 0) {
  17. map[x][y] = grid[x][y] + pan(x - 1, y)
  18. console.log(x, y, map[x][y])
  19. return map[x][y]
  20. }
  21. map[x][y] = grid[x][y] +
  22. Math.min(pan(x, y - 1), pan(x - 1, y))
  23. console.log(x, y, map[x][y])
  24. return map[x][y]
  25. }
  26. var res = pan(grid.length-1, grid[0].length-1)
  27. return res
  28. };
  29. console.log(minPathSum(
  30. [[1, 3, 1], [1, 5, 1], [4, 2, 1]]
  31. ));

解法2:

动态规划+类似缓存
想比于方法一,减少了重复计算,提高算力

  1. var minPathSum = function(grid) {
  2. if(grid.length === 0){
  3. return 0;
  4. }
  5. let m=grid.length, n = grid[0].length;
  6. for(let i = 1; i < n; i++){
  7. grid[0][i] += grid[0][i-1];
  8. }
  9. for(let i = 1; i < m; i++){
  10. grid[i][0] += grid[i-1][0];
  11. }
  12. for(let i = 1; i < m; i++){
  13. for(let j = 1; j < n; j++){
  14. grid[i][j]+= Math.min(grid[i-1][j], grid[i][j-1]);
  15. }
  16. }
  17. return grid[m-1][n-1];
  18. };

70.[经典]Climbing Stairs

image.png
image.png

解法1:递归

爬楼梯直接思路递归法,登顶的最后一步有两种走法,也就是1步,后者2步。
所以可以得出公式f(n) = f(n-1) + f(n-2).

  1. var ClimbingStairs {
  2. var climbStairsWithRecursion(n) {
  3. if (n == 1) {
  4. return 1;
  5. } else if (n == 2) {
  6. return 2;
  7. } else {
  8. return climbStairsWithRecursion(n - 1) + climbStairsWithRecursion(n - 2);
  9. }
  10. }
  11. }

解法2:动态规划

  1. var climbStairs = function(n) {
  2. if(n==0 || n==1 || n==2) return n;
  3. var arr=[];
  4. arr[0]=1;
  5. arr[1]=2;
  6. for(var i=2 ; i< n;i++){
  7. arr[i]=arr[i-1]+arr[i-2];
  8. }
  9. return arr[n-1];
  10. };

解法3

列举

  1. let climbStairs = function (n) {
  2. const a = [1, 2]
  3. let res = 0
  4. // for (i = 1; i <= n; i++) {
  5. // a.push(i)
  6. // }
  7. function b(arr, index) {
  8. for (let id = 0; id < 2; id++) {
  9. let attTemp = [...arr]
  10. attTemp.push(a[id])
  11. let sum = attTemp.reduce((t, num) => t + num)
  12. sum === n && console.log(attTemp);
  13. if (sum === n) {
  14. res++
  15. return
  16. }
  17. else if (sum > n) { return }
  18. b(attTemp, id)
  19. }
  20. }
  21. b([], 0)
  22. console.log(res);
  23. return res
  24. };
  25. climbStairs(5)

46. [回溯]Permutations

image.png

解法1:

image.png

其他题目

1:Two Sum

解法1:

双重循环,找到两数之和等于目标数

解法2:

空间换时间,创建map,key为数字,value为index,每次只需判断map[目标数-当前数]的值是否存在即可

2[链表]Add Two Numbers

image.png

解法1:

常规链表遍历

3.Longest Substring Without Repeating Characters

image.png

解法1:

创建map,记录每一个字母的index,当map已有记录时,说明有重复字母,记录此时的长度,并与此前记录的最大值作比较。若一直无重复,则返回整个字符串长度

7.Reverse Integer

image.png

解法1:

变成数组,反转去零。负数单独处理。

9.Palindrome Number

image.png

解法1:

变成字符串,split,反转,join,对比差异

11.Container With Most Water

image.png

解法1:

双重循环,逐一计算面积,取最大值。

解法2:

  1. var maxArea = function(height) {
  2. let i = 0;
  3. let j = height.length - 1;
  4. let max = 0
  5. while(i<j) {
  6. let min = Math.min(height[i], height[j])
  7. max = Math.max(((j-i) * min), max)
  8. if (height[i] > height[j]) {
  9. j--
  10. } else {
  11. i++
  12. }
  13. }
  14. return max
  15. };

双指针,先让index之差最大,计算面积,再取两者之中较高的,移动另一个(如果取低的,那么下次计算出的面积会在这次面积之内),相当于双指针同时变化,时间复杂度较低。

13.Roman to Integer

14.Longest Common Prefix

image.png

解法1:

应该想到:先比较数组中每个元素的第一位,如果都相同,则开始比较每个元素的第二位,依次类推;直到出现一个元素的某一位不在其他元素中都出现,此时返回这个元素之前的值

(非题目解法):

创建map,映射数组首元素每个字母,过滤出数量是数组长度的元素,根据数组首元素遍历选出连续字符串

  1. var temp = (arr) => {
  2. var a = {}
  3. for (var i = 0; i < arr[0].length; i++) {
  4. a[arr[0][i]] = 1
  5. }
  6. arr.slice(1, Infinity).forEach(it => {
  7. for (var i = 0; i < it.length; i++) {
  8. a[it[i]]++
  9. }
  10. });
  11. Object.keys(a).forEach(it => {
  12. if (a[it] !== arr.length) {
  13. delete a[it]
  14. }
  15. })
  16. var l = 0, max = 0, targetIndex = 0, targetMax = 0, isStart = true
  17. for (var i = 0; i < arr[0].length; i++) {
  18. if (a[arr[0][i]] === arr.length) {
  19. if (isStart) {
  20. l = i
  21. max++
  22. isStart = false
  23. } else {
  24. max++
  25. }
  26. } else {
  27. targetIndex = max > targetMax ? l : targetIndex
  28. targetMax = max > targetMax ? max : targetMax
  29. isStart = true
  30. max = 0
  31. }
  32. }
  33. return arr.length !== 1 ? arr[0].slice(targetIndex, targetIndex + targetMax) : arr[0]
  34. }
  35. console.log(temp(['a']))

19.[链表]Remove Nth Node From End of List

解法1:

根据n来遍历找到对应节点,做删除处理

20.Valid Parentheses

image.png

解法1:

创建栈,遇到左符号就push,遇到匹配的右符号就pop,完成后看栈是否为空

21.[链表经典递归]Merge Two Sorted Lists

解法1:

  1. var mergeTwoLists = function(l1, l2) {
  2. if(!l1){
  3. return l2
  4. }
  5. else if(!l2){
  6. return l1
  7. }
  8. if(l1.val<l2.val){
  9. l1.next=mergeTwoLists(l1.next,l2)
  10. return l1
  11. }
  12. else{
  13. l2.next=mergeTwoLists(l2.next,l1)
  14. return l2
  15. }
  16. };

23.[链表]Merge k Sorted Lists

解法1:

遍历列表,取出全部数值排序,创建新的链表进行填充

解法2:

  1. function mergeLists(a, b) {
  2. const dummy = new ListNode(0);
  3. let temp = dummy;
  4. while (a !== null && b !== null) {
  5. if (a.val < b.val) {
  6. temp.next = a;
  7. a = a.next;
  8. } else {
  9. temp.next = b;
  10. b = b.next;
  11. }
  12. temp = temp.next;
  13. }
  14. if (a !== null) {
  15. temp.next = a;
  16. }
  17. if (b !== null) {
  18. temp.next = b;
  19. }
  20. return dummy.next;
  21. }
  22. var mergeKLists = function(lists) {
  23. if (lists.length === 0 ) {
  24. return null;
  25. }
  26. while(lists.length > 1) {
  27. let a= lists.shift()
  28. let b = lists.shift()
  29. let m = mergeLists(a,b)
  30. lists.push(m)
  31. }
  32. return lists[0];
  33. }

24.[链表]Swap Nodes in Pairs

image.png

解法1:

递归思路值得研究
image.png

26.Remove Duplicates from Sorted Array

image.png

解法1:

按照索引依次去重

27.Remove Element

image.png

解法1:

按照索引依次去重

解法2:

创建map取keys

28.Implement strStr()

easy

33.Search in Rotated Sorted Array

easy

34.Find First and Last Position of Element in Sorted Array

easy

35.Search Insert Position

image.png

解法1:

遍历一次,判断左侧数值、自身数值、右侧数值的关系

36?.Valid Sudoku

39.Combination Sum

image.png

解法1:

给定index,产生tempArr,递归,当sum(tempArr)小于目标时,继续递归,大于目标时,结束。
image.png

42?.Trapping Rain Water

image.png

43.Multiply Strings

解法1:

大数相乘,转化为字符串,reverse,诸位相乘,创建res[],计算结果

48.Rotate Image

image.png

解法1:

根据index的关系变化创建数组进行操作。
image.pngimage.png

49.Group Anagrams

image.png

解法1:

创建map,遍历元素,记录每个元素sort后的字符串。遍历map,

50.Pow(x, n)

easy

53.Maximum Subarray

image.png

解法1:

遍历一遍,每一步的sum值如果是正数,就与max比较。如果是负数就会重置(因为如果是负数,则意味着上次的sum更大,或者数组项都是负数)
然后再遍历一遍,看单个项中谁最大,与max比较。

58.Length of Last Word

easy

61.[链表]Rotate List

image.png

解法1:

根据k值找到最终的头,根据头部位置重新组合链表

62.Unique Paths

image.png

解法1:

只要是走到边缘,那么一定只有一种解法。基于这个事实,递归,利用[剩余x,剩余y]数组递归,直至到x=1或者y=1.image.png

63.Unique Paths II

与62相似,遇到障碍物就return

66.Plus One

image.png

解法1:

从末尾加1,查看是否大于10,是的话诸位向上一位进1.

67.Add Binary

二进制诸位相加

69.Sqrt(x)

二分法寻找 n*n===x 的最接近值

71?.Simplify Path

  1. var simplifyPath = function(path) {
  2. let stack = [];
  3. path = path.split('/');
  4. for (let i=0;i<path.length;i++) {
  5. if (path[i]=='.' || path[i]=='') continue;
  6. if (path[i]=='..') stack.pop();
  7. else stack.push(path[i]);
  8. }
  9. return '/'+stack.join('/');
  10. };

75.Sort Colors

image.pngimage.png

解法1:

因为只有这三种颜色(三个元素),所以只需要记录三种元素出现的次数,再根据次数重置数组即可

80.Remove Duplicates from Sorted Array II

删除排序数组中的重复项,重复次数最多5次

解法1:

index从2开始,如果arr[index]===arr[index+2],原地删除数组中的这一项

解法2:

类似75题解法1

82?.[链表]Remove Duplicates from Sorted List II

83.[链表]Remove Duplicates from Sorted List

easy

88.Merge Sorted Array

image.png

解法1:

从两数组对应的结束位置向前遍历,谁大谁填入新数组的尾端

92.[链表]Reverse Linked List II

按部就班

94.[树中序遍历]Binary Tree Inorder Traversal

image.png

98.[树]Validate Binary Search Tree

左侧所有节点都比本节点小的,xxx,是bst

解法1:

  1. var inorderTraversal = function (root) {
  2. var res = true
  3. function pan(node, leftMin = -Infinity, rightMax = Infinity) {
  4. if (!node) { return }
  5. if (node.left) {
  6. if (node.val > node.left.val && node.left.val > leftMin) {
  7. pan(node.left, leftMin || -Infinity, node.val)
  8. } else {
  9. res = false
  10. }
  11. }
  12. if (node.right) {
  13. if (node.val < node.right.val && node.right.val < rightMax) {
  14. pan(node.right, node.val, rightMax || Infinity)
  15. } else {
  16. res = false
  17. }
  18. }
  19. }
  20. pan(root)
  21. return res
  22. };

100.[树]Same Tree

image.png

101?.[树]Symmetric Tree

100题的转换
image.png

102.[树]Binary Tree Level Order Traversal

image.png

解法1:

根据树的深度对应数组对应的index,这样可以用深度遍历
image.png

解法2:

广度遍历
image.png

103.[树]Binary Tree Zigzag Level Order Traversal

image.png

解法1:

深度遍历的时候根据树的深度做数组的push或unShift处理

104.[树]Maximum Depth of Binary Tree

image.png

105.[树]Construct Binary Tree from Preorder and Inorder Traversal

根据先中后序遍历在数组中的位置,进行处理

106.[树]Construct Binary Tree from Inorder and Postorder Traversal

类似105

107.[树]Binary Tree Level Order Traversal II

类似102

108.[树]Convert Sorted Array to Binary Search Tree

image.png

解法1:

找到数组中间元素,作为子树的root,递归左右两部分

109.[树]Convert Sorted List to Binary Search Tree

类似108

110?.[树]Balanced Binary Tree

没搞懂题目意思

111.[树]Minimum Depth of Binary Tree

easy,遍历即可

112.[树]Path Sum

easy

118.Pascal’s Triangle

image.png
image.png

解法1:

初始值为[1],下一级的length为上一级+1,当下一级index=0、length-1时为1,其他元素根据上一级的对应关系得出

120.[动态规划解法]Triangle

image.png

解法1:

递归列出所有可能,选出最短路径

解法2:

动态规划+缓存
image.png

125.Valid Palindrome

image.png

解法1:

split,toLowerCase,reverse,join后对比

129.[树]Sum Root to Leaf Numbers

相对easy

136.Single Number

easy

137.Single Number II

easy

141.[链表]Linked List Cycle

image.png

解法1:

  1. var hasCycle = function(head) {
  2. let slow = head, fast = head
  3. while (fast && fast.next) {
  4. slow = slow.next
  5. fast = fast.next.next
  6. if (slow === fast) { return true }
  7. }
  8. return false
  9. };

142.[链表]Linked List Cycle II

image.png

解法1:

  1. var detectCycle = function(head) {
  2. if(!head||!head.next){return null}
  3. var slow = head, fast = head
  4. while (fast && fast.next) {
  5. slow = slow.next
  6. fast = fast.next.next
  7. if (slow == fast) {
  8. if(head!=slow){
  9. var dummy=head;
  10. while(dummy!=slow){
  11. dummy=dummy.next;
  12. slow=slow.next;
  13. }
  14. return dummy
  15. }
  16. return slow}
  17. }
  18. return null
  19. };

image.png
image.png

143.[链表]Reorder List

解法1:

这道链表重排序问题可以拆分为以下三个小问题:
1. 使用快慢指针来找到链表的中点,并将链表从中点处断开,形成两个独立的链表。
2. 将第二个链翻转。
3. 将第二个链表的元素间隔地插入第一个链表中。

144.[树]Binary Tree Preorder Traversal

先序遍历

145?.[树]Binary Tree Postorder Traversal

后序遍历

148.[链表]Sort List

解法1:

遍历链表,取出数值,重新构建

151.Reverse Words in a String

easy

153.Find Minimum in Rotated Sorted Array

easy

154.Find Minimum in Rotated Sorted Array II

easy

155?.[实现栈]Min Stack

160.[链表]Intersection of Two Linked Lists

image.png

解法1:

先算出两链表长度之差,再对其数组同时进行遍历

167.Two Sum II - Input array is sorted

解法1:

二分法寻找匹配组合

168.Excel Sheet Column Title

image.png

解法1:

26进制对应26字母(十进制转26进制),依次相加(利用String.fromCharCode)

169.Majority Element

image.png

解法1:

利用map

171.Excel Sheet Column Number

168反操作,26进制转10进制

189.Rotate Array

easy

191.Number of 1 Bits

image.png
image.png

解法1:

转二进制,过程中判断每一位的数值

203.[链表]Remove Linked List Elements

image.png

解法1:

用递归清晰一点
image.png

205.Isomorphic Strings

image.png

解法1:

创建2个map,相同index时判断map里重复的字母index是否相同

  1. var isIsomorphic = function(s, t) {
  2. if (s.length !== t.length) {
  3. return false;
  4. }
  5. if (s === t) {
  6. return true;
  7. }
  8. const obj1 = {};
  9. const obj2 = {};
  10. for(let i = 0; i < s.length; i++) {
  11. const letter = s[i];
  12. const tLetter = t[i];
  13. if (!obj2[tLetter]) {
  14. obj2[tLetter] = letter;
  15. }
  16. if (!obj1[letter]) {
  17. obj1[letter] = tLetter;
  18. }
  19. if (obj1[letter] !== tLetter || obj2[tLetter] !== letter) {
  20. return false;
  21. }
  22. }
  23. return true;
  24. };

206.[链表]Reverse Linked List

image.png

215.Kth Largest Element in an Array

解法1:

数组排序

217.Contains Duplicate

easy

219.Contains Duplicate II

image.png

222.[树]Count Complete Tree Nodes

image.png

225?.[实现队列]Implement Stack using Queues

226.[树]Invert Binary Tree

image.png

231.Power of Two

image.png

解法1:

数字不断除2,除到小于等于1,看是否等于1

232.Implement Queue using Stacks

实现队列

234.[链表]Palindrome Linked List

easy

235.[树]Lowest Common Ancestor of a Binary Search Tree

image.png

解法1:

根据BST找位置关系
image.png

237.[链表]Delete Node in a Linked List

image.png

257.[树]Binary Tree Paths

easy

258.Add Digits

easy

260.Single Number III

easy

263.Ugly Number

image.png

解法1:

  1. /**
  2. * @param {number} num
  3. * @return {boolean}
  4. */
  5. var isUgly = function(num) {
  6. if(num==0){
  7. return false;
  8. }
  9. n=num;
  10. while(n%2==0){
  11. n=n/2;
  12. }
  13. while(n%3==0){
  14. n=n/3;
  15. }
  16. while(n%5==0){
  17. n=n/5;
  18. }
  19. if(n==1){
  20. return true;
  21. }
  22. else{
  23. return false;
  24. }
  25. };


268.Missing Number

image.png

解法1:

  1. var missingNumber = function(nums) {
  2. // construct array of size n+1, to leave a spot for the missing element.
  3. // Assign each val to -1 so we can see which position was not filled
  4. // O(n)
  5. const res = new Array(nums.length+1).fill(-1);
  6. // "sort" the elements by assigning to the array based on val
  7. // O(n)
  8. for(const num of nums) {
  9. res[num] = num;
  10. }
  11. // the remaining -1 index is the missing value
  12. // O(n-1)
  13. return res.indexOf(-1);
  14. };

283.Move Zeroes

image.png

解法1:

  1. /**
  2. * @param {number[]} nums
  3. * @return {void} Do not return anything, modify nums in-place instead.
  4. */
  5. var moveZeroes = function(nums) {
  6. var count=0;
  7. for(var i=0;i<nums.length;i++){
  8. if(nums[i]!=0){
  9. nums[count]=nums[i]
  10. count++
  11. }
  12. }
  13. for(var i=count;i<nums.length;i++){
  14. nums.splice(i,1,0)
  15. }
  16. return nums
  17. };

287.Find the Duplicate Number

image.png

  1. /**
  2. * @param {number[]} nums
  3. * @return {number}
  4. */
  5. var findDuplicate = function(nums) {
  6. var arr=new Array(nums.length-1);
  7. for(var i=0;i<nums.length;i++){
  8. if(arr[nums[i]]==1){
  9. return nums[i]
  10. }
  11. else {arr[nums[i]]=1;
  12. }
  13. }
  14. };

326.Power of Three

image.png

  1. /**
  2. * @param {number} n
  3. * @return {boolean}
  4. */
  5. var isPowerOfThree = function(n) {
  6. if(n === 0) return false
  7. while(n % 3 === 0){
  8. n = n / 3
  9. }
  10. return n === 1 ? true : false
  11. };

328.[链表]Odd Even Linked List

按部就班

338.[看不懂] Counting Bits

image.png

解法1:

342. Power of Four

类似326

344. Reverse String

easy

345. Reverse Vowels of a String

image.png

解法1:

一遍扫描所有字符,记录所有元音字母和它们出现的位置到一个数组中;再扫描这个数组,将元音字母逆序填回原来的字符串相应位置。

解法2:

维护两个指针分别从字符串头和尾扫描,每次交换两个指针扫到的元音字母,于是只需遍历一遍字符串就可以完成元音字母逆序。

349. Intersection of Two Arrays

image.png

解法1:

分别遍历,第一次遍历创建map记录每个元素,设map[x]=1,二次遍历查map

350. Intersection of Two Arrays II

image.png
类似349

367. Valid Perfect Square

image.png

  1. var isPerfectSquare = function(num) {
  2. for(i=1;!((i*i<num)&&((i+1)*(i+1)>num));i++){
  3. if(i*i==num){
  4. return true
  5. }
  6. else if(i*i>num){
  7. i=Math.floor(i*0.5);
  8. }
  9. else if(i*i<num){
  10. i=i*2;
  11. }
  12. }
  13. return false
  14. };

383. Ransom Note

image.png
image.pngtrue

  1. var canConstruct = function(ransomNote, magazine) {
  2. let arr = [];
  3. for(let i = 0; i< ransomNote.length; i++){
  4. let index = magazine.indexOf(ransomNote[i]);
  5. if(index === -1) return false;
  6. magazine = magazine.substring(0, index) + magazine.substring(index + 1);
  7. }
  8. return true;
  9. };

387. First Unique Character in a String

image.png

解法1:

遍历map,再遍历看哪个元素的map值是1

389. Find the Difference

image.png

解法1:

t做元素map,遍历s减去map里的值,剩下的就是结果

392. Is Subsequence

image.png

解法1:

双指针,遍历一次

404.[树] Sum of Left Leaves

easy

409. [经典]Longest Palindrome

image.png

解法1:

412. Fizz Buzz
easy

414?. Third Maximum Number

415. Add Strings

大数相加


429.[树]N-ary Tree Level Order Traversal

n叉树的遍历,可分为深度遍历(按照树的深度),也可以广度遍历


441. Arranging Coins

image.png
i*(i+1)/2

  1. /**
  2. * @param {number} n
  3. * @return {number}
  4. */
  5. var arrangeCoins = function(n) {
  6. if(n==1){return 1}
  7. if(n==0){return 0}
  8. for(i=2;;i++){
  9. if((1+i)*i*0.5>n){
  10. break;
  11. }
  12. }
  13. return i-1
  14. };


442?. Find All Duplicates in an Array

image.png
445. Add Two Numbers II

类似415


448. Find All Numbers Disappeared in an Array

image.png
创建一个数组,元素作为index push,然后看那些们没有元素,就是缺少的

451?. Sort Characters By Frequency


453. Minimum Moves to Equal Array Elements

image.png
每次将n-1个元素+1,最少几次所有元素相同

解法1:

数组排序,计算每个元素与min值的差距,累计差距

461?. Hamming Distance

462. Minimum Moves to Equal Array Elements II

image.png

解法1:

类似453,取中位数

  1. /**
  2. * @param {number[]} nums
  3. * @return {number}
  4. */
  5. var minMoves2 = function(nums) {
  6. nums.sort((a, b) => a-b);
  7. const med = nums[Math.floor(nums.length / 2)];
  8. let moves = 0;
  9. for (let i = 0; i < nums.length; i++) {
  10. const diff = Math.abs(nums[i] - med);
  11. moves += diff;
  12. }
  13. return moves;
  14. };

476. Number Complement

easy

485. Max Consecutive Ones

easy


500. Keyboard Row

easy

504. Base 7

返回七进制数
image.png
image.png

110(10)-> 215(7)

507. Perfect Number

easy

509. Fibonacci Number

image.png

解法1:

  1. /**
  2. * @param {number} n
  3. * @return {number}
  4. */
  5. let results = {}
  6. var fib = function(n) {
  7. if(results[n]) {
  8. return results[n]
  9. }
  10. if(n < 2) {
  11. return n
  12. }
  13. let res = fib(n - 1) + fib(n - 2)
  14. results[n] = res
  15. return res
  16. };

513.[树] Find Bottom Left Tree Value

image.png

解法1:

遍历,根据分支向左偏移的count寻找节点
也可中序遍历,根据deep,找到最深deep的第一个遍历节点

image.png


520. Detect Capital

easy

540. Single Element in a Sorted Array

easy

557. Reverse Words in a String III

easy


559. [树]Maximum Depth of N-ary Tree

easy

561?.[经典] Array Partition I

575. Distribute Candies

easy

589. [树] N-ary Tree Preorder Traversal

类似429

590. [树] N-ary Tree Postorder Traversal

类似429

599. Minimum Index Sum of Two Lists

easy


606. [树] Construct String from Binary Tree

基于遍历

617. [树] Merge Two Binary Trees

image.png

622?.[实现循环队列] Design Circular Queue


633. [较为经典]Sum of Square Numbers

image.png

解法1:(可以用二分法改进)

  1. /**
  2. * @param {number} c
  3. * @return {boolean}
  4. */
  5. var judgeSquareSum = function(c) {
  6. var c1=Math.sqrt(c*0.5);
  7. var c2=Math.floor(Math.sqrt(c));
  8. for(var i=0;i<=c1;i++){
  9. for(var s=c2;s>=c1;s--){
  10. if(i*i+s*s==c){
  11. return true;
  12. }
  13. else if(i*i+s*s>c){
  14. }
  15. else{
  16. break;
  17. }
  18. }
  19. }
  20. return false
  21. };

解法2:

  1. /**
  2. * @param {number} c
  3. * @return {boolean}
  4. */
  5. var judgeSquareSum = function (c) {
  6. if (c < 0) return false;
  7. let i = 0;
  8. let j = Math.trunc(Math.sqrt(c));
  9. while (i <= j) {
  10. let powSum = i * i + j * j;
  11. if (powSum === c) {
  12. return true
  13. } else if (powSum > c) {
  14. j--
  15. } else {
  16. i++;
  17. }
  18. }
  19. return false;
  20. };


637.[树] Average of Levels in Binary Tree

image.png

解法1:

遍历完树的深度,再计算

692. [经典]Top K Frequent Words

image.png
解法1:

  1. /**
  2. * @param {string[]} words
  3. * @param {number} k
  4. * @return {string[]}
  5. */
  6. var topKFrequent = function(words, k) {
  7. const freq = words.reduce((acc,curr)=> ( (acc.hasOwnProperty(curr))? acc[curr] += 1 : acc[curr] = 1 ,acc),{});
  8. const sortedWords = Object.keys(freq).sort((a, b) => {
  9. if (freq[a] > freq[b]) {
  10. return -1
  11. } else if (freq[a] < freq[b]) {
  12. return 1
  13. }
  14. return a < b ? -1 : 1
  15. // const n = freq[b] - freq[a];
  16. // if (n !== 0) return n;
  17. // return a > b ? 1 : -1
  18. })
  19. return sortedWords.slice(0,k);
  20. /*
  21. create a hashTable where we can store each
  22. word and how many times it repeats,
  23. iterate through the hashTable and find k greatest
  24. numbers and sort them in alphabetic order
  25. */
  26. // const map = new Map();
  27. // for (let i = 0; i < words.length; i ++) {
  28. // if (!map.has(words[i])) map.set(words[i], 0);
  29. // map.set(words[i], map.get(words[i]) + 1);
  30. // }
  31. // const mostFrequent = [...map.keys()].sort((a, b) => {
  32. // let n = map.get(b) - map.get(a);
  33. // if (n !== 0) return n;
  34. // return a > b ? 1 : -1
  35. // });
  36. // console.log( mostFrequent )
  37. // return mostFrequent.slice(0, k);
  38. };

693. Binary Number with Alternating Bits

easy

700.[树]Search in a Binary Search Tree

easy

701.[树] Insert into a Binary Search Tree

easy

703?. [实现什么东东]Kth Largest Element in a Stream

704. Binary Search

easy

707?.[实现链表] Design Linked List

709. To Lower Case

easy

724. Find Pivot Index

image.png

错误解法(但可以参考做法)

  1. var pivotIndex = function (nums) {
  2. var l = -1
  3. var lSum = 0
  4. var r = nums.length
  5. var rSum = 0
  6. if (r === 0) { return -1 }
  7. while (l != r) {
  8. if (lSum >= rSum) {
  9. r--
  10. rSum = rSum + nums[r]
  11. } else {
  12. l++
  13. lSum = lSum + nums[l]
  14. }
  15. }
  16. if (lSum == rSum) {
  17. console.log(l);
  18. return l
  19. }
  20. console.log(-1);
  21. return -1
  22. };

正确解法

  1. var pivotIndex = function(nums) {
  2. let left = 0
  3. let right = nums.reduce((a, b) => a + b, 0)
  4. for(let i=0; i<nums.length; i++){
  5. right -= nums[i]
  6. if(left === right) return i
  7. left += nums[i]
  8. }
  9. return -1
  10. };

728. Self Dividing Numbers

image.png

解法核心片段

  1. while(num1>=1){
  2. if(num%(num1%10)!=0){
  3. return false;
  4. }
  5. num1=(num1-num1%10)/10;
  6. }
  7. return true;

747. Largest Number At Least Twice of Others

easy

771. Jewels and Stones

easy

783.[树] Minimum Distance Between BST Nodes

image.png

解法1:

image.png


796. Rotate String

easy

830. Positions of Large Groups

image.png

解法1:

  1. /**
  2. * @param {string} s
  3. * @return {number[][]}
  4. */
  5. var largeGroupPositions = function(s) {
  6. var start=true
  7. var res=[]
  8. var item=[]
  9. for(var i=0;i<s.length;i++){
  10. if(start){
  11. if (s[i] == s[i + 1] && s[i] == s[i + 2]) {
  12. item.push(i)
  13. // if(s.length==3){
  14. // item.push(i+2)
  15. // return [item]
  16. // }
  17. start=false
  18. i=i+1
  19. }
  20. }else{
  21. if(s[i]!=s[i+1]){
  22. item.push(i)
  23. res.push(item)
  24. item=[]
  25. start=true
  26. }
  27. }
  28. }
  29. return res
  30. };

867. Transpose Matrix

image.png

解法1:

  1. /**
  2. * @param {number[][]} A
  3. * @return {number[][]}
  4. */
  5. var transpose = function(a) {
  6. return a[0].map(function(item1,index1){
  7. return a.map(function(item){
  8. return item[index1]
  9. })
  10. })
  11. };

868. Binary Gap

解法1:

双重循环计算每个起始位的gap,算出最大值

解法2(有待更好的解法):

872.[树] Leaf-Similar Trees

基于遍历

876.[链表] Middle of the Linked List

image.png
image.png

889.[树] Construct Binary Tree from Preorder and Postorder Traversal

类似105

905. Sort Array By Parity

easy

908. [经典]Smallest Range I

image.png

解法1:

  1. const smallestRangeI = (A, K) => {
  2. let leftMax = Number.MIN_SAFE_INTEGER, // 左区间最大值
  3. rightMax = Number.MAX_SAFE_INTEGER; // 右区间最小值
  4. for (let i = 0; i < A.length; i++) {
  5. leftMax = Math.max(leftMax, A[i] - K);
  6. rightMax = Math.min(rightMax, A[i] + K);
  7. }
  8. return rightMax > leftMax ? 0 : leftMax - rightMax;
  9. };

922. Sort Array By Parity II

easy

938.[树] Range Sum of BST

根据BST的特性进行递归
image.png
image.png

961. N-Repeated Element in Size 2N Array

easy

965.[树]Univalued Binary Tree

image.png

解法1:

判断自身和左右节点是否相等,是的话返回量子节点的递归结果,不等返回false
image.png

977. Squares of a Sorted Array

image.png

解法1:

  1. /**
  2. * @param {number[]} nums
  3. * @return {number[]}
  4. */
  5. var sortedSquares = function(nums) {
  6. const result = new Array(nums.length).fill(0);
  7. let start = 0, end = nums.length - 1;
  8. let index = nums.length - 1;
  9. while(start <= end) {
  10. const startNum = nums[start], endNum = nums[end];
  11. if(Math.abs(startNum) > Math.abs(endNum)) {
  12. result[index] = Math.pow(startNum, 2);
  13. start++;
  14. }
  15. else {
  16. result[index] = Math.pow(endNum, 2);
  17. end--;
  18. }
  19. index--;
  20. }

989. Add to Array-Form of Integer

image.png

解法1:

  1. var addToArrayForm = function(num, k) {
  2. return (BigInt(num.join(''))+BigInt(k)).toString().split('')
  3. };

993.[树] Cousins in Binary Tree

image.png

解法1:

当两节点深度相同且父节点不是同一节点时,为true
遍历每个节点,当左右节点为x、y时,返回true
否则查看遍历结果,看x所在深度内有无y

1002. Find Common Characters

image.png

解法1:

  1. var commonChars = function(A) {
  2. let res = A[0].split('')
  3. for (string of A) {
  4. let localString = string.split('')
  5. res = res.filter(s => {
  6. let idx = localString.indexOf(s)
  7. if (idx === -1) return false
  8. localString[idx] = NaN
  9. return true
  10. })
  11. }
  12. return res
  13. };


1008.[树] Construct Binary Search Tree from Preorder Traversal

image.png
image.png

1022.[树]Sum of Root To Leaf Binary Numbers

基于遍历

1047. Remove All Adjacent Duplicates In String

image.png
easy

1089. Duplicate Zeros

easy

1108. Defanging an IP Address

easy

1122.[较经典] Relative Sort Array

image.png

————————————————————————-

位运算

左移 <<

  1. 10 << 1 // -> 20

左移就是将二进制全部往左移动,10 在二进制中表示为 1010 ,左移一位后变成 10100 ,转换为十进制也就是 20

右移 >>

  1. 10 >> 1 // -> 5

算数右移就是将二进制全部往右移动并去除多余的右边,10 在二进制中表示为 1010 ,右移一位后变成 101 ,转换为十进制也就是 5

右移很好用,比如可以用在二分算法中取中间值

  1. 13 >> 1 // -> 6

按位操作

按位与 &

每一位都为 1,结果才为 1

  1. 8 & 7 // -> 0
  2. // 1000 & 0111 -> 0000 -> 0

按位或 |

其中一位为 1,结果就是 1

  1. 8 | 7 // -> 15
  2. // 1000 | 0111 -> 1111 -> 15

按位非 ~

其中一位为 1,结果就是 1

  1. ~1的计算步骤:
  2. 1(这里叫:原码)转二进制 00000001
  3. 按位取反 11111110
  4. 发现符号位(即最高位)为1(表示负数),将除符号位之外的其他数字取反 10000001
  5. 末位加1取其补码 10000010
  6. 转换回十进制 -2

负数:补码(x) = -x - 1,正数:补码(x) = x

按位异或 ^

每一位都不同,结果才为 1

  1. 8 ^ 7 // -> 15
  2. 8 ^ 8 // -> 0
  3. // 1000 ^ 0111 -> 1111 -> 15
  4. // 1000 ^ 1000 -> 0000 -> 0

从以上代码中可以发现按位异或就是不进位加法

面试题:两个数不使用四则运算得出和

这道题中可以按位异或,因为按位异或就是不进位加法,8 ^ 8 = 0 如果进位了,就是 16 了,所以我们只需要将两个数进行异或操作,然后进位。那么也就是说两个二进制都是 1 的位置,左边应该有一个进位 1,所以可以得出以下公式 a + b = (a ^ b) + ((a & b) << 1) ,然后通过迭代的方式模拟加法

  1. function sum(a, b) {
  2. if (a == 0) return b
  3. if (b == 0) return a
  4. let newA = a ^ b
  5. let newB = (a & b) << 1
  6. return sum(newA, newB)
  7. }

搜索

二分搜索

  1. const binarySearch = function (array, item) {
  2. var low = 0, //{2}
  3. high = array.length - 1, //{3}
  4. mid, element;
  5. while (low <= high) { //{4}
  6. mid = Math.floor((low + high) / 2); //{5}
  7. element = array[mid]; //{6}
  8. if (element < item) { //{7}
  9. low = mid + 1; //{8}
  10. } else if (element > item) { //{9}
  11. high = mid - 1; //{10}
  12. } else {
  13. return mid; //{11}
  14. }
  15. }
  16. return -1; //{12}
  17. };
  18. var arr = [0, 1, 3, 4, 5, 6, 8, 9].sort()
  19. console.log(arr, binarySearch(arr, 3));

排序

冒泡排序

每个循环逐渐把大的放后面

  1. const array = [1, 3, 2, 6, 4, 5, 9, 8, 7];
  2. const sort = (arr) => {
  3. let result = [...arr];
  4. let temp;
  5. for(let i = 0; i < result.length - 1; i++){
  6. for(let j = 0; j < result.length - 1 -i; j++){
  7. if(result[j] > result[j + 1]){
  8. temp = result[j];
  9. result[j] = result[j + 1];
  10. result[j + 1] = temp;
  11. }
  12. }
  13. }
  14. return result;
  15. }
  16. const newArr = sort(array);
  17. console.log(newArr); // [1, 2, 3, 4, 5, 6, 7, 8, 9]

该算法的操作次数是一个等差数列 n + (n - 1) + (n - 2) + 1 ,去掉常数项以后得出时间复杂度是 O(n * n)

插入排序

每个循环逐渐把小的放前面
插入排序的原理如下。第一个元素默认是已排序元素,取出下一个元素和当前元素比较,如果当前元素大就交换位置。那么此时第一个元素就是当前的最小数,所以下次取出操作从第三个元素开始,向前对比,重复之前的操作。
leetcode%26算法 - 图143
以下是实现该算法的代码

  1. const array = [1, 3, 2, 6, 4, 5, 9, 8, 7];
  2. const sort = (arr) => {
  3. let result = [...arr];
  4. let temp;
  5. for(let i = 0; i < result.length; i++){
  6. let j = i;
  7. while(result[j - 1] > result[j] && j>=0){
  8. temp = result[j - 1];
  9. result[j - 1] = result[j];
  10. result[j] = temp;
  11. j--;
  12. }
  13. }
  14. return result;
  15. }
  16. const newArr = sort(array);
  17. console.log(newArr); // [1, 2, 3, 4, 5, 6, 7, 8, 9]

该算法的操作次数是一个等差数列 n + (n - 1) + (n - 2) + 1 ,去掉常数项以后得出时间复杂度是 O(n * n)

选择排序

也是每个循环逐渐把小的数放前面,与插排不同的是,插排每个循环都可能调换目标数位置(逐渐前移),选排是每个循环确定一个循环内最小数,循环结束后前移。

选择排序的原理如下。遍历数组,设置最小值的索引为 0,如果取出的值比当前最小值小,就替换最小值索引,遍历完成后,将第一个元素和最小值索引上的值交换。如上操作后,第一个元素就是数组中的最小值,下次遍历就可以从索引 1 开始重复上述操作。
leetcode%26算法 - 图144
以下是实现该算法的代码

  1. function selection(array) {
  2. if (!checkArray(array)) return
  3. for (let i = 0; i < array.length - 1; i++) {
  4. let minIndex = i;
  5. for (let j = i + 1; j < array.length; j++) {
  6. minIndex = array[j] < array[minIndex] ? j : minIndex;
  7. }
  8. swap(array, i, minIndex);
  9. }
  10. return array;
  11. }
  12. var example=[8,94,15,88,55,76,21,39];
  13. function selectSort(arr){
  14. var len=arr.length;
  15. var minIndex,temp;
  16. console.time('选择排序耗时');
  17. for(i=0;i<len-1;i++){
  18. minIndex=i;
  19. for(j=i+1;j<len;j++){
  20. if(arr[j]<arr[minIndex]){
  21. minIndex=j;
  22. }
  23. }
  24. temp=arr[i];
  25. arr[i]=arr[minIndex];
  26. arr[minIndex]=temp;
  27. }
  28. console.timeEnd('选择排序耗时');
  29. return arr;
  30. }
  31. console.log(selectSort(example));

该算法的操作次数是一个等差数列 n + (n - 1) + (n - 2) + 1 ,去掉常数项以后得出时间复杂度是 O(n * n)

希尔排序

归并排序

我们现在写一个方法来得到a跟b组合后的有序数组,这个方法很简单,用两个指针i,j分别指向两个数组,然后开始遍历,比较a[i]和a[j]的大小,将小的那个放入新的有序数组。当任意一个数组遍历完,循环结束,将剩下的值全部放入新的有序数组:

  1. const merge = (arr1, arr2) => {
  2. const length1 = arr1.length;
  3. const length2 = arr2.length;
  4. const newArr = [];
  5. let i = 0;
  6. let j = 0;
  7. while(i < length1 && j < length2) {
  8. if(arr1[i] <= arr2[j]) {
  9. newArr.push(arr1[i]);
  10. i++;
  11. } else {
  12. newArr.push(arr2[j]);
  13. j++;
  14. }
  15. }
  16. // arr2还剩一些
  17. if(i === length1 && j < length2) {
  18. while(j < length2) {
  19. newArr.push(arr2[j]);
  20. j++;
  21. }
  22. }
  23. // arr1还剩一些
  24. if(j === length2 && i < length1) {
  25. while(i < length1) {
  26. newArr.push(arr1[i]);
  27. i++;
  28. }
  29. }
  30. return newArr;
  31. }
  32. // 测试一下
  33. const a = [1 ,2, 6, 8];
  34. const b = [3, 4, 9];
  35. const result = merge(a, b);
  36. console.log(result); // [1, 2, 3, 4, 6, 8, 9]

可以看出,如果a,b自身是有序数组,得到的结果就是有序数组。

然后我们递归的将待排序数组分成左右两个数组,一直分到数组只含有一个元素为止,因为数组只含有一个元素,我们就可以认为他是有序的。

  1. const mergeSort = (arr) => {
  2. const length = arr.length;
  3. if(length <= 1) {
  4. return arr;
  5. }
  6. const middle = Math.floor(length / 2);
  7. const left = arr.slice(0, middle);
  8. const right = arr.slice(middle);
  9. return merge(mergeSort(left), mergeSort(right));
  10. }
  11. // 测试一下
  12. const a = [2, 1, 3, 6, 4, 5, 9, 8, 7];
  13. // 测试下
  14. let result = mergeSort(a);
  15. console.log(result); // [1, 2, 3, 4, 5, 6, 7, 8, 9]

image.png
如[3,2,6,4,7,5,9]的数组,会像如图分组,直至每组只有一个数,每次每组后归并可以得到一个组内有序的小组,再向上层归并,最终得到有序数组。

快速排序

以某一基准值为基准(一般是第一个或者最后一个数,这里以最后一个数为例),在数组前后各建立一个指针(l,r),当l<r时,先进行l—操作,找到第一个大于基准的数,找到后再r—,找到第一个大于基准的数,两个数调换位置,这样,l的左侧和r的右侧都分别小于基准值和大于基准值。直到l=r,若l位置的值大于基准值,则与基准值换位置。此时,l=r这个位置及其左边都小于基准值,右边都大于基准值。再进行递归迭代。

  1. const quickSort = (array) => {
  2. const sort = (arr, left = 0, right = arr.length - 1) => {
  3. if (left >= right) {//如果左边的索引大于等于右边的索引说明整理完毕
  4. return
  5. }
  6. let i = left
  7. let j = right
  8. const baseVal = arr[j] // 取无序数组最后一个数为基准值
  9. while (i < j) { //把所有比基准值小的数放在左边大的数放在右边
  10. while (i < j && arr[i] <= baseVal) { //找到一个比基准值大的数交换
  11. i++
  12. }
  13. arr[j] = arr[i] // 将较大的值放在右边如果没有比基准值大的数就是将自己赋值给自己(i 等于 j)
  14. while (j > i && arr[j] >= baseVal) { //找到一个比基准值小的数交换
  15. j--
  16. }
  17. arr[i] = arr[j] // 将较小的值放在左边如果没有找到比基准值小的数就是将自己赋值给自己(i 等于 j)
  18. }
  19. arr[j] = baseVal // 将基准值放至中央位置完成一次循环(这时候 j 等于 i )
  20. sort(arr, left, j - 1) // 将左边的无序数组重复上面的操作
  21. sort(arr, j + 1, right) // 将右边的无序数组重复上面的操作
  22. }
  23. const newArr = array.concat() // 为了保证这个函数是纯函数拷贝一次数组
  24. sort(newArr)
  25. return newArr
  26. }

堆排序

排序时间复杂度与稳定性

时间复杂度

image.png

稳定性

假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳定的;否则称为不稳定的。
堆排序快速排序希尔排序直接选择排序是不稳定的排序算法,而基数排序冒泡排序直接插入排序折半插入排序归并排序是稳定的排序算法。

动态规划

用动态规划解决问题时,要遵循三个重要步骤:
(1) 定义子问题;
(2) 实现要反复执行而解决子问题的部分(这一步要参考前一节讨论的递归的步骤);
(3) 识别并求解出边界条件。
能用动态规划解决的一些著名的问题如下。

image.png

最少硬币找零问题

  1. function MinCoinChange(coins) {
  2. var coins = coins;
  3. var cache = {};
  4. this.makeChange = function (amount) {
  5. var me = this;
  6. if (!amount) {
  7. return [];
  8. }
  9. if (cache[amount]) {
  10. return cache[amount];
  11. }
  12. var min = [], newMin, newAmount;
  13. for (var i = 0; i < coins.length; i++) {
  14. var coin = coins[i];
  15. newAmount = amount - coin;
  16. if (newAmount >= 0) {
  17. newMin = me.makeChange(newAmount);
  18. }
  19. //如果余额大于等于零,或者newMin不是空数组
  20. if (
  21. newAmount >= 0 &&
  22. (newMin.length < min.length - 1 || !min.length)
  23. && (newMin.length || !newAmount)
  24. ) {
  25. min = [coin].concat(newMin);
  26. console.log('new Min ' + min + ' for ' + amount);
  27. }
  28. }
  29. return (cache[amount] = min);
  30. };
  31. }
  32. var minCoinChange = new MinCoinChange([1, 5, 10, 25]);
  33. console.log(minCoinChange.makeChange(36));

思路分析:给定面额x,将x减去不同面值,如果余数为负则非正常结果,为正则继续以此面额作为新的x,不断向下遍历,过程中不断对每个余数的最小解做缓存,有缓存直接利用。
eg[1,3,5],x=4
1、4-1
1.1、3—- 3-1
1.1.1、 2—-2-1
1.1.1.1、1
1.1、3—- 3-3
2、4-3
2.1、1
3、4-5 pass

贪心算法

【最少】硬币找零问题

  1. function MinCoinChange(coins) {
  2. var coins = coins; //{1}
  3. this.makeChange = function (amount) {
  4. var change = [], total = 0;
  5. for (var i = coins.length; i >= 0; i--) { //{2}
  6. var coin = coins[i];
  7. while (total + coin <= amount) { //{3}
  8. change.push(coin); //{4}
  9. total += coin; //{5}
  10. }
  11. }
  12. return change;
  13. };
  14. }

思路:将面额先用最大面值减到不能减,之后再换小一号面值
缺点是不一定是最优,也不一定有解,有点是快速