目录

题目 难度 求解
03.数组中重复的数字 简单
- [ ] 索引法
- [ ] 建立新索引
- [ ] 原地交换
- [x] 排序法(自己写的)
- [x] 集合去重
04.二维数组中的查找 中等
- [x] 线性查找(注意if判断的边界值条件)
05.替换空格 简单
- [x] 调用系统函数
06.从尾到头打印链表 简单
- [x] 递归
- [ ] 栈
- [x] 映射法
07.重建二叉树 中等
- [ ]

| | 09.栈实现队列 | 简单 |
- [x]

| | 10-I.斐波那契数列 | 简单 |
- [x] 迭代法(注意细节)
| | 10-II.跳台阶 | 简单 |
- [x] 动态规划(注意%1e7的处理)
- [x] 迭代法
| | 11.旋转数组的最小元素 | 简单 |
- [x] 排序法
- [x] 变量数组,找到第一个刚好最小的
- [ ] 二分法(特殊的二分,注意细节)
| | 12.矩阵中的路径 | 中等 |
- [x] DFS+剪枝(值得复习)
| | 13.机器人的运动范围 | 中等 |
- [ ] DFS
- [ ] BFS
| | 14-II.剪绳子 | 中等 |
- [ ] 动态规划+大数取余
- [ ] 循环取余
- [ ] 快速幂
| | 15.二进制1的个数 | 简单 |
- [x] 转成字符串
- [ ] 位运算
| | 16.数值的整数次方 | 中等 |
- [x] 快速幂
| | 17.打印从1到最大的n位数 | 简单 |
- [x]

| | 18.删除链表节点 | 简单 |
- [x]

| | 19.正则表达式的匹配 | 困难 |
- [ ]

| | 20.表示数值的字符串 | 中等 |
- [ ] 有穷自动机
| | 21.调整数组顺序 | 简单 |
- [x]

| | 22.链表倒数第k个节点 | 简单 |
- [x] 快慢指针
| | 24.链表翻转 | 简单 |
- [x] 递归法
- [x] 双指针
| | 25.合并两个排序的链表 | 简单 |
- [x]

| | 26.树的子结构 | 中等 |
- [ ] 双重递归(递归的典例)
| | 27.二叉树的镜像 | 简单 |
- [x] 递归
| | 28.对称的二叉树 | 简单 |
- [ ] 递归
| | 29.顺时针打印矩阵 | 简单 |
- [ ] DFS(层数k放置的细节)
| | 30.包含min函数的栈 | 简单 |
- [x] 模拟
| | 31.栈的压入、弹出 | 中等 |
- [x] 模拟
| | 32-I.从上到下打印二叉树 | 简单 |
- [x] 队列
| | 32-II.从上到下打印二叉树 | 简单 |
- [x] 队列
| | 32-III.从上到下打印二叉树 | 中等 |
- [ ] 队列,需要循环单调装入队列中
| | 33.二叉树的后序遍历序列 | 中等 | | | 34.二叉树中和为某一值的路径 | 中等 |
- [x] 递归
| | | | |

03

索引法

  1. var findRepeatNumber = function(nums) {
  2. let m = [];
  3. for(let num of nums){
  4. if(m[num]){
  5. return num;
  6. }
  7. m[num]=1;
  8. }
  9. return -1;
  10. };

第二种方法的关键点:num === nums[num],让数字回归到它原来的下标位置处

  1. // 原地交换
  2. var findRepeatNumber = function(nums) {
  3. for (let i = 0; i < nums.length; i++) {
  4. let num = nums[i];
  5. while(num !== i) {
  6. if (num === nums[num]) return num;
  7. [nums[i], nums[num]] = [nums[num], nums[i]];
  8. }
  9. }
  10. return -1;
  11. };

集合去重

  1. var findRepeatNumber = function(nums) {
  2. let set = new Set();
  3. for(let num of nums){
  4. if(set.has(num)){
  5. return num;
  6. }
  7. set.add(num);
  8. }
  9. return -1;
  10. };

排序法

/**
 * @param {number[]} nums
 * @return {number}
 */
var findRepeatNumber = function(nums){
    nums.sort((a,b)=>(a-b));
    for(let i=0;i<nums.length;i++){
        if(nums[i]===nums[i+1]){
            return nums[i];
        }
    }
    return -1;
};

04

线性查找

注意开始if的判定条件

var findNumberIn2DArray = function(matrix, target) {
    if(matrix===null || matrix.length===0){
        return false;
    }
    let row = matrix.length;
    let col = matrix[0].length;
    let i=0,j=col-1;
    while(i<row && j>=0){
        if(matrix[i][j]<target){
            i++;
        }else if(matrix[i][j]>target){
            j--;
        }else{
            return true;
        }
    }
    return false;
};

05

调用系统函数

最推荐第二种,比较熟悉

var replaceSpace = function(s) {
    // return s.replace(/\s/g,"%20");
    return s.split(" ").join("%20");
    // return s.replaceAll(" ","%20");
};

06

递归法

var reversePrint = function(head) {
    let res = [];
    calc(head);
    return res;

    function calc(head){
        if(head===null){
            return;
        }
        calc(head.next);
        res.push(head.val);
    }
};

映射法

var reversePrint = function(head) {
    let p = head;
    let n = 0;
    while(p!==null){
        n++;
        p=p.next;
    }
    let res = [];
    p = head;
    while(p!=null){
        res[n-1]=p.val;
        n--;
        p=p.next;
    }
    return res;
};

07

09

var CQueue = function() {
    this.data = [];
    this.helper = [];
};

/** 
 * @param {number} value
 * @return {void}
 */
CQueue.prototype.appendTail = function(value) {
    this.data.push(value);
};

/**
 * @return {number}
 */
CQueue.prototype.deleteHead = function() {
    if (this.data.length!==0) {
        while (this.data.length > 1) {
            this.helper.push(this.data.pop());
        }
        let res = this.data.pop();
        while (this.helper.length) {
            this.data.push(this.helper.pop());
        }
        return res;
    } else {
        return -1;
    }
};

/**
 * Your CQueue object will be instantiated and called as such:
 * var obj = new CQueue()
 * obj.appendTail(value)
 * var param_2 = obj.deleteHead()
 */

10-I

迭代法

var fib = function (n) {
    let a = 0,
        b = 1;
    for (let i = 0; i < n; ++i) {
        const c = (a + b) % (1e9 + 7);
        a = b;
        b = c;
    }
    return a;
};

10-II

动态规划

var numWays = function(n) {
    if(n==0){
        return 1;
    }
    let arr= new Array(n+1);
    arr[0] = 0;
    arr[1] = 1;
    arr[2] = 2;
    for(let i=3; i<=n; i++){
        arr[i] = arr[i-1]%1000000007 + arr[i-2]%1000000007;
    }
    return arr[n]%1000000007;
};

迭代法

var numWays = function(n) {
    let a = 1, b = 1, sum;
    for(let i = 0; i < n; i++){
        sum = (a + b) % 1000000007;
            a = b;
            b = sum;
    }
    return a;
};

11

遍历数组

class Solution {    public int minArray(int[] numbers) {        for(int i=0;i<numbers.length-1;i++){            if(numbers[i]>numbers[i+1]){                return numbers[i+1];            }        }        return numbers[0];    }}

二分

var minArray = function(numbers) {    let left=0,right=numbers.length-1;    while(left<right){        let mid = Math.floor(left + (right-left)/2);        if(numbers[mid]<numbers[right]){            right=mid;        }else if(numbers[mid]>numbers[right]){            left=mid+1;        }else{            right--;        }    }    return numbers[left];};

12

/** * @param {character[][]} board * @param {string} word * @return {boolean} */ var exist = function (board, word) {    let words = word.split('');    for (let i = 0; i < board.length; i++) {        for (let j = 0; j < board[0].length; j++) {            if (dfs(board, words, i, j, 0) === true) {                return true;            }        }    }    return false;    // 1.边界外返回false    // 2.board[i][j]!==words[k],返回false    // 3.已经访问过的坐标    function dfs(board, words, i, j, k) {        if (i < 0 || j < 0 || i >= board.length || j >= board[0].length) {            return false;        }        if(board[i][j]!==words[k]){            return false;        }        if (k === words.length-1) {            return true;        }        board[i][j] = '';        let res = dfs(board, words, i - 1, j, k + 1) || dfs(board, words, i, j - 1, k + 1) || dfs(board, words, i + 1, j, k + 1) || dfs(board, words, i, j + 1, k + 1);        board[i][j] = words[k];        return res;    }}

13

数位和增量:
image-20210513194226581.png
图例展示了 n,m = 20n,m=20 , k \in [6, 19]k∈[6,19] 的可达解、不可达解、非解,以及连通性的变化。
13.gif
根据可达解的结构和连通性,易推出机器人可 仅通过向右和向下移动,访问所有可达解 。

  • 三角形内部: 全部连通,易证;
  • 两三角形连通处: 若某三角形内的解为可达解,则必与其左边或上边的三角形连通(即相交),即机器人必可从左边或上边走进此三角形。

image-20210513194033594.png
DFS:

/**
 * @param {number} m
 * @param {number} n
 * @param {number} k
 * @return {number}
 */
var movingCount = function (m, n, k) {
    let visited = new Array(m).fill('').map((item, index) => { return new Array(n).fill(false) });
    return dfs(0, 0, 0, 0);

    function dfs(i, j, si, sj) {
        if (i >= m || j >= n || k < si + sj || visited[i][j]) {
            return 0;
        }
        visited[i][j] = true;
        return 1 + dfs(i + 1, j, (i + 1) % 10 !== 0 ? si + 1 : si - 8, sj) + dfs(i, j + 1, si, (j + 1) % 10 !== 0 ? sj + 1 : sj - 8);
    }
};

BFS:

/**
 * @param {number} m
 * @param {number} n
 * @param {number} k
 * @return {number}
 */
var movingCount = function (m, n, k) {
    let visited = new Array(m).fill('').map((item, index) => { return new Array(n).fill(false) });
    let res = 0;
    let queue = [];
    queue.push([0, 0, 0, 0]);
    while (queue.length > 0) {
        let x = queue.shift();
        let i = x[0], j = x[1], si = x[2], sj = x[3];
        if (i >= m || j >= n || k < si + sj || visited[i][j]) {
            continue;
        }
        visited[i][j] = true;
        res++;
        queue.push([i + 1, j, (i + 1) % 10 !== 0 ? si + 1 : si - 8, sj]);
        queue.push([i, j + 1, si, (j + 1) % 10 !== 0 ? sj + 1 : sj - 8]);
    }
    return res;
};

通过leetcode题解有另外一位答主的回答,也是DSF和BFS,另一种写法可以参考

DFS:

/**
 * @param {number} m
 * @param {number} n
 * @param {number} k
 * @return {number}
 */
var movingCount = function (m, n, k) {
    // 位数和
    function getSum(num) {
        let answer = 0;

        while (num) {
            answer += num % 10;
            num = Math.floor(num / 10);
        }

        return answer;
    }
    // 方向数组
    const directionAry = [
        [-1, 0], // 上
        [0, 1], // 右
        [1, 0], // 下
        [0, -1] // 左
    ];
    let set = new Set(['0,0']);
    dfs(0, 0, k);

    function dfs(x, y, k) {
        for (let i = 0; i < 4; i++) {
            let offsetX = x + directionAry[i][0];
            let offsetY = y + directionAry[i][1];
            if (offsetX < 0 || offsetY < 0 || offsetX > m - 1 || offsetY > n - 1 || getSum(offsetY) + getSum(offsetX) > k || set.has(`${offsetX},${offsetY}`)) {
                continue;
            }
            set.add(`${offsetX},${offsetY}`);
            dfs(offsetX, offsetY, k);
        }
    }
    return set.size;
};

BFS:

/** * @param {number} m * @param {number} n * @param {number} k * @return {number} */var movingCount = function (m, n, k) {    // 位数和    function getSum(num) {        let answer = 0;        while (num) {            answer += num % 10;            num = Math.floor(num / 10);        }        return answer;    }    // 方向数组    const directionAry = [        [-1, 0], // 上        [0, 1], // 右        [1, 0], // 下        [0, -1] // 左    ];    // 已经走过的坐标    let set = new Set(['0,0']);    // 将遍历的坐标队列,题意要求从[0,0]开始走    let queue = [[0, 0]];    // 遍历队列中的坐标    while (queue.length) {        // 移除队首坐标        let [x, y] = queue.shift();        // 遍历方向        for (let i = 0; i < 4; i++) {            let offsetX = x + directionAry[i][0];            let offsetY = y + directionAry[i][1];            // 临界值判断            if (offsetX < 0 || offsetX >= m || offsetY < 0 || offsetY >= n || getSum(offsetX) + getSum(offsetY) > k || set.has(`${offsetX},${offsetY}`)) {                continue;            }            // 走过的格子就不再纳入统计            set.add(`${offsetX},${offsetY}`);            // 将该坐标加入队列(因为这个坐标的四周没有走过,需要纳入下次的遍历)            queue.push([offsetX, offsetY]);        }    }    // 走过坐标的个数就是可以到达的格子数    return set.size;};

14-II

动态规划+大数取余

import java.math.BigInteger;
class Solution {
    public int cuttingRope(int n) {
        BigInteger[] dp = new BigInteger[n + 1];
        Arrays.fill(dp, BigInteger.valueOf(1));
        // dp[1] = BigInteger.valueOf(1);
        for(int i = 3; i < n + 1; i++){
            for(int j = 1; j < i; j++){
                dp[i] = dp[i].max(BigInteger.valueOf(j * (i - j))).max(dp[i - j].multiply(BigInteger.valueOf(j)));
            }
        }
        return dp[n].mod(BigInteger.valueOf(1000000007)).intValue();
    }
}

这种方式效率太低,而且在JS代码中运行结果为NaN

循环取余

/**
 * @param {number} n
 * @return {number}
 */
var cuttingRope = function (n) {
    if (n < 4) {
        return n - 1;
    } else if (n == 4) {
        return n;
    }
    let res = 1;
    while (n > 4) {
        res *= 3;
        res %= 1000000007;
        n -= 3;
    }
    // 最终剩下来的肯定是2,3,4
    return res * n % 1000000007;
};

快速幂

JS中对大数转换支持不好,在JS代码中运行失败

这个大数处理之后再弄,这里标记上

class Solution {    int mod = 1000000007;    public int cuttingRope(int n) {        if(n < 4) return n - 1;        int a = n / 3;        int b = n % 3;        if(b == 0) return (int) (myPow(3, a) % mod);        else if(b == 1) return (int) (myPow(3, a - 1) * 4 % mod);        else return (int) (myPow(3, a) * 2 % mod);    }    public long myPow(long base, int num){        long res = 1;        while(num > 0){            if((num & 1) == 1){                res *= base;                res %= mod;            }            base *= base;            base %= mod;            num >>= 1;        }        return res;    }}

15

转成字符串

/**
 * @param {number} n - a positive integer
 * @return {number}
 */
var hammingWeight = function(n) {
    let str = n.toString(2);
    let len = str.length;
    let count = 0;
    for(let i=0;i<len;i++){
        if(str[i]==='1'){
            count++;
        }
    }
    return count;
};

位运算

var hammingWeight = function(n) {
    let cnt = 0;
    while (n) {
        cnt += n & 1;
        n >>>= 1;
    }
    return cnt;
};

16

/**
 * @param {number} x
 * @param {number} n
 * @return {number}
 */
var myPow = function(x, n) {
    let r = 1;
    let tmp = x;
    let tag = 0;
    if (n < 0) {
        tag = 1;
        n = -n;
    }
    while (n) {
        if (n & 1) {
            r *= tmp;
        }
        tmp *= tmp;
        n >>>= 1;
    }
    return tag ? 1 / r : r;
};

17

/**
 * @param {number} n
 * @return {number[]}
 */
var printNumbers = function(n) {
    let end = Math.pow(10,n)-1;
    let res = [];
    for(let i=0;i<end;i++){
        res[i]=i+1;
    }
    return res;
};

18

/**
 * Definition for singly-linked list.
 * function ListNode(val) {
 *     this.val = val;
 *     this.next = null;
 * }
 */
/**
 * @param {ListNode} head
 * @param {number} val
 * @return {ListNode}
 */
var deleteNode = function(head, val) {
    if(head.val===val){
        head=head.next;
        return head;
    }
    let p = head;
    while(p!==null){
        if(p.next.val===val){
            p.next=p.next.next;
            break;
        }
        p=p.next;
    }
    return head;
};

20

剑指offer系列 - 图4

/**
 * @param {string} s
 * @return {boolean}
 */
var isNumber = function(s) {

    // 状态枚举
    const   STATE_INITIAL = 1           //1. 起始的空格
    const   STATE_INT_SIGN = 2          //2. 符号位
    const   STATE_INTEGER = 3           //3. 整数部分
    const   STATE_POINT = 4             //4. 左侧有整数的小数点
    const   STATE_POINT_WITHOUT_INT = 5 //5. 左侧无整数的小数点
    const   STATE_FRACTION = 6          //6. 小数部分
    const   STATE_EXP = 7               //7. 字符 E e
    const   STATE_EXP_SIGN = 8          //8. 指数部分的符号位
    const   STATE_EXP_NUMBER = 9        //9. 指数部分的整数部分
    const   STATE_END = 10              //10. 末尾的空格

    // 字符枚举
    const   CHAR_NUMBER = 1         // 0-9
    const   CHAR_EXP = 2            // E e
    const   CHAR_POINT = 3          // .
    const   CHAR_SIGN = 4           // + -
    const   CHAR_SPACE = 5          // ' '
    const   CHAR_ILLEGAL = 6        // 除上面字符外都是非法字符


    const toCharType = char => {
        switch(char) {
            case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':case '8':case '9':  return CHAR_NUMBER
            case 'E': case 'e': return CHAR_EXP
            case '.': return CHAR_POINT
            case '+': case '-': return CHAR_SIGN
            case ' ': return CHAR_SPACE
            default: return CHAR_ILLEGAL
        }
    }    

    // 状态转移表
    const tranfer = {
        [STATE_INITIAL]: {
            [CHAR_NUMBER]: STATE_INTEGER,
            [CHAR_POINT]: STATE_POINT_WITHOUT_INT,
            [CHAR_SIGN]: STATE_INT_SIGN,
            [CHAR_SPACE]: STATE_INITIAL,
        },
        [STATE_INT_SIGN]: {
            [CHAR_NUMBER]: STATE_INTEGER,
            [CHAR_POINT]: STATE_POINT_WITHOUT_INT,
        },
        [STATE_INTEGER]: {
            [CHAR_NUMBER]: STATE_INTEGER,
            [CHAR_EXP]: STATE_EXP,
            [CHAR_POINT]: STATE_POINT,
            [CHAR_SPACE]: STATE_END,
        },
        [STATE_POINT]: {
            [CHAR_NUMBER]: STATE_FRACTION,
            [CHAR_EXP]: STATE_EXP,
            [CHAR_SPACE]: STATE_END,
        },
        [STATE_POINT_WITHOUT_INT]: {
            [CHAR_NUMBER]: STATE_FRACTION
        },
        [STATE_FRACTION]: {
            [CHAR_NUMBER]: STATE_FRACTION,
            [CHAR_EXP]: STATE_EXP,
            [CHAR_SPACE]: STATE_END,
        },
        [STATE_EXP]: {
            [CHAR_NUMBER]: STATE_EXP_NUMBER,
            [CHAR_SIGN]: STATE_EXP_SIGN,
        },
        [STATE_EXP_SIGN]: {
            [CHAR_NUMBER]: STATE_EXP_NUMBER,
        },
        [STATE_EXP_NUMBER]: {
            [CHAR_NUMBER]: STATE_EXP_NUMBER,
            [CHAR_SPACE]: STATE_END,
        },
        [STATE_END]: {
            [CHAR_SPACE]: STATE_END,
        }
    }

    const len = s.length
    let state = STATE_INITIAL // 初始状态

    for (let i = 0; i < len; i++) {
        let ch = s[i]
        let chType = toCharType(ch)
        let nextState = tranfer[state][chType]
        if (nextState) {
            state = nextState
        } else {
            return false
        }
    }  

    // 注意遍历完字符串后最后可能的状态 
    return state === STATE_INTEGER
        || state === STATE_POINT
        || state === STATE_FRACTION
        || state === STATE_EXP_NUMBER
        || state === STATE_END
};

21

/**
 * @param {number[]} nums
 * @return {number[]}
 */
 var exchange = function(nums) {
    let x = [];
    let y = [];
    for(let i=0;i<nums.length;i++){
        if(nums[i]%2){
            x.push(nums[i]);
        }else{
            y.push(nums[i])
        }
    }
    return x.concat(y);
};

22

class Solution {
    public ListNode getKthFromEnd(ListNode head, int k) {
        ListNode fast=head;
        ListNode slow=head;
        while(k!=0){
            fast=fast.next;
            k--;
        }
        while(fast!=null){
            fast=fast.next;
            slow=slow.next;
        }
        return slow;
    }
}

24

递归法

/**
 * Definition for singly-linked list.
 * function ListNode(val) {
 *     this.val = val;
 *     this.next = null;
 * }
 */
/**
 * @param {ListNode} head
 * @return {ListNode}
 */
var reverseList = function(head) {
    if(head==null){
        return null;
    }
    if(head.next==null){
        return head;
    }
    let last = reverseList(head.next);
    head.next.next=head;
    head.next=null;
    return last;
};

双指针

/**
 * Definition for singly-linked list.
 * function ListNode(val) {
 *     this.val = val;
 *     this.next = null;
 * }
 */
/**
 * @param {ListNode} head
 * @return {ListNode}
 */
var reverseList = function(head) {
    if (head == null) { return null; }
    let cur = head;
    while (head.next != null) {
        let t = head.next.next;
        head.next.next = cur;
        cur = head.next;
        head.next = t;
    }
    return cur;
};

25

/**
 * @param {ListNode} l1
 * @param {ListNode} l2
 * @return {ListNode}
 */
var mergeTwoLists = function(l1, l2) {
    let dum = new ListNode(0), cur = dum;
    while(l1 != null && l2 != null) {
        if(l1.val < l2.val) {
            cur.next = l1;
            l1 = l1.next;
        }
        else {
            cur.next = l2;
            l2 = l2.next;
        }
        cur = cur.next;
    }
    cur.next = l1 != null ? l1 : l2;
    return dum.next;
};

26

image.png
recur(A, B)函数:
1.终止条件:

  • 当B为空,说明已经匹配完成了,返回true
  • 当A为空,说明已经越过A的叶子节点,返回false
  • 值不同:匹配失败,返回false

2.返回值:

  • 判断A和B的左子节点是否相等,即recur(A.left, B.left)
  • 判断A和B的右子节点是否相等,即recur(A.right, B.right)

isSubStructure(A,B)函数:
1.特例处理:当树A为空或B为空,直接返回false
2.返回值:若B是A的子结构,则:

  • 以节点A为根节点的子树包含B,对应recur(A,B)
  • B是A的左子树,isSubStructure(A.left,B)
  • B是A的左子树,isSubStructure(A.right,B)

剑指offer系列 - 图6
剑指offer系列 - 图7

/**
 * 判断A的子树和B的子树是否完全相同
 * @param {TreeNode} A
 * @param {TreeNode} B
 * @return {boolean}
 */
var isSubStructure = function (A, B) {
    // 递归终止条件,某个树为空时必然不想等,返回false
    if (A === null || B === null) {
        return false;
    }
    // 运用 || 运算符,左边的为真就不需要往右边执行了
    return recur(A, B) || isSubStructure(A.left, B) || isSubStructure(A.right, B);

    /**
     * 判断当A和B都不为空时,子树是否完全相同
     * @param {TreeNode} A 
     * @param {TreeNode} B 
     * @returns {boolean}
     */
    function recur(A, B) {
        // B为空,表示子树比较完成了
        if (B === null) {
            return true;
        }
        // A为空,表示到达了叶子节点,此时B还没到,因此比如不相等
        // A节点的值和B节点的值不相等时候返回false
        if (A === null || A.val !== B.val) {
            return false;
        }
        // 左子树和右子树同时相等时才返回true
        return recur(A.left, B.left) && recur(A.right, B.right);
    }
};

27

image.png

/**
 * @param {TreeNode} root
 * @return {TreeNode}
 */
var mirrorTree = function (root) {
    if (root === null || (root.left === null && root.right === null)) {
        return root;
    }
    let temp;
    temp = mirrorTree(root.right);
    root.right = mirrorTree(root.left);
    root.left = temp;
    return root;
};

28

image.png

/**
 * @param {TreeNode} root
 * @return {boolean}
 */
var isSymmetric = function (root) {
    if (root === null) {
        return true;
    }
    return recur(root.left, root.right);

    function recur(L, R) {
        if (L === null && R === null) {
            return true;
        }
        if (L === null || R === null || L.val !== R.val) {
            return false;
        }
        return recur(L.left, R.right) && recur(L.right, R.left);
    }
};

29

image.png
剑指offer系列 - 图11
这里有一个细节需要注意,就是为啥是在left—->up状态下k++呢?

/**
 * 顺序打印矩阵
 * @param {number[][]} matrix
 * @return {number[]}
 */
var spiralOrder = function (matrix) {
    if (!matrix || !matrix.length) {
        return [];
    }
    let row = matrix.length;
    let col = matrix[0].length;
    // res保存移动的路径
    let res = [];
    // 获取移动的方向,保存在moves对象中,相当于一个map映射
    let moves = {
        right: [0, 1],
        down: [1, 0],
        left: [0, -1],
        up: [-1, 0]
    };
    // 向内部折回的此时,就是绕了第几次圈
    let k = 0;
    // 从坐标(0,0)处开始,向右执行
    dfs(0, 0, "right");
    return res;

    function dfs(i, j, dir) {
        // 越界检查
        if (i < 0 || j < 0 || i >= row || j >= col || res.length === row * col) {
            return;
        }
        // 访问过的点加入到路径中
        res.push(matrix[i][j]);
        switch (dir) {
            case "right":
                if (j === col - 1 - k) {
                    dir = "down";
                }
                break;
            case "down":
                if (i === row - 1 - k) {
                    dir = "left";
                }
                break;
            case "left":
                if (j === k) {
                    dir = "up";
                    k++;
                }
                break;
            case "up":
                if (i === k) {
                    dir = "right";
                }
                break;
        }
        // 坐标的移动
        let x = i + moves[dir][0];
        let y = j + moves[dir][1];
        // 递归到下一步
        dfs(x, y, dir);
    }
};

30

image.png

var MinStack = function() {
    this.x_stack = [];
    this.min_stack = [Infinity];
};

MinStack.prototype.push = function(x) {
    this.x_stack.push(x);
    this.min_stack.push(Math.min(this.min_stack[this.min_stack.length - 1], x));
};

MinStack.prototype.pop = function() {
    this.x_stack.pop();
    this.min_stack.pop();
};

MinStack.prototype.top = function() {
    return this.x_stack[this.x_stack.length - 1];
};

MinStack.prototype.min = function() {
    return this.min_stack[this.min_stack.length - 1];
};

31

image.png

/**
 * @param {number[]} pushed
 * @param {number[]} popped
 * @return {boolean}
 */
Array.prototype.peek = function () {
    return this[this.length - 1];
}

Array.prototype.isEmpty = function () {
    return this.length === 0 ? true : false;
}
var validateStackSequences = function (pushed, popped) {
    let i = j = 0;
    let len = pushed.length;
    let stack = [];
    while (i < len && j < len) {
        if (i < len) {
            stack.push(pushed[i]);
            i++;
        }
        while (!stack.isEmpty() && stack.peek() === popped[j]) {
            stack.pop();
            j++;
        }
    }
    if (stack.isEmpty()) {
        return true;
    } else {
        return false;
    }
};

32-I

image.png

/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
Array.prototype.isEmpty = function () {
    let len = this.length;
    return len === 0 ? true : false;
}
/**
 * @param {TreeNode} root
 * @return {number[]}
 */
var levelOrder = function (root) {
    if (root === null) {
        return [];
    }
    let queue = [];
    let res = [];
    queue.push(root);
    while (!queue.isEmpty()) {
        let node = queue.shift();
        if (node.left !== null) {
            queue.push(node.left);
        }
        if (node.right !== null) {
            queue.push(node.right);
        }
        res.push(node.val);
    }
    return res;
};

32-II

image.png

Array.prototype.isEmpty = function () {
    let len = this.length;
    return len === 0 ? true : false;
}
/**
 * @param {TreeNode} root
 * @return {number[]}
 */
var levelOrder = function (root) {
    if (root === null) {
        return [];
    }
    let queue = [];
    let res = [];
    queue.push(root);
    while (!queue.isEmpty()) {
        let tmp = [];
        for (let i = queue.length; i > 0; i--) {
            let node = queue.shift();
            if (node.left !== null) {
                queue.push(node.left);
            }
            if (node.right !== null) {
                queue.push(node.right);
            }
            tmp.push(node.val);
        }
        res.push(tmp);
    }
    return res;
};

32-III

QQ截图20210626214631.png
在II中修改奇数层和偶数层的添加方向就行,这个层级是奇数还是偶数可以由结果res数组的长度来判断

Array.prototype.isEmpty = function () {
    let len = this.length;
    return len === 0 ? true : false;
}
/**
 * @param {TreeNode} root
 * @return {number[]}
 */
var levelOrder = function (root) {
    if (root === null) {
        return [];
    }
    let queue = [];
    let res = [];
    queue.push(root);
    while (!queue.isEmpty()) {
        let tmp = [];
        for (let i = queue.length; i > 0; i--) {
            let node = queue.shift();
            if (node.left !== null) {
                queue.push(node.left);
            }
            if (node.right !== null) {
                queue.push(node.right);
            }
            if (res.length % 2 === 0) {
                // 偶数层
                tmp.push(node.val);
            } else {
                // 奇数层
                tmp.unshift(node.val);
            }
        }
        res.push(tmp);
    }
    return res;
};

34

image.png

/**
 * @param {TreeNode} root
 * @param {number} target
 * @return {number[][]}
 */
var pathSum = function (root, target) {
    let res = [];
    let track = [];
    recur(root, target);
    return res;

    function recur(root, target) {
        if (root === null) return;
        track.push(root.val);
        target = target - root.val;
        if (target === 0 && root.left === null && root.right === null) {
            let t = JSON.parse(JSON.stringify(track));
            res.push(t);
        }
        recur(root.left, target);
        recur(root.right, target);
        track.pop();
    }
};