LeetCode之专题整理

55 阅读4分钟

链表

链表题目的解决, 通常要结合画图来理解

876 链表的中间结点

入门级别, 快慢指针即可解决

var middleNode = function(head) {
    let quick = head,
        slow = head
    while (quick!==null && quick.next!==null) {
        quick = quick.next.next
        slow = slow.next
    }
    return slow
};

160 相交链表

让我用一道很浪漫的题开启大家的链表之旅吧!

关键词: 重走一遍你的路, 我们终究会相遇

function getIntersectionNode (headA, headB) {
    let pA = headA
    let pB = headB
    while (pA!==pB) {
        pA = (pA===null) ? headB : pA.next
        pB = (pB===null) ? headA : pB.next
    }
    return pA
}

206 反转链表

pre, cur, next三指针

本题目不使用dummy节点, 使用pre和cur双节点的方式, 由于单链表的限制, 额外使用一个next指针记录cur节点的下一个节点的位置, 每次遍历时, 让cur指向pre, 并让pre前移, cur前移(指向next)

/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */
/**
 * @param {ListNode} head
 * @return {ListNode}
 */
var reverseList = function(head) {
    // pre始终落后cur和next一个身位, cur和next指向同一个节点
    let pre = null,
        cur = head,
        next = head
    while (cur!==null) {
        // 使用next指针记录cur后一位的地址
        next = cur.next
        cur.next = pre
        // pre前移一位
        pre = cur
        // cur前移一位
        cur = next
    }  
    return pre
};

利用es6的解构赋值, 我们有更为精巧的解法

var reverseList = function(head) {
    // pre始终落后cur一个身位, cur指向当前节点
    let pre = null,
        cur = head
    while (cur!==null) {
        // 利用es6的解构赋值, 你甚至不需要额外的next指针
        [cur.next, pre, cur] = [pre, cur, cur.next]
        // pre, cur移动的顺序也可以改变
        // [cur.next, cur, pre] = [pre, cur.next, cur]
    }  
    return pre
};

92 反转链表 II

pre, cur, next三指针 + 额外存储

想法:

  • 反转位置m到位置n之间的链表
  • 将原链表与反转后的链表拼接

步骤:

  • 初始化变量: pre指向前方, curr遍历当前链表, 二者依次增加, pre始终落后curr一个身位
  • 将pre, curr递增到位置m之前, 使用pre2和curr2分别存储当前pre和curr的位置
  • 反转位置m到位置n之间的链表
  • 利用pre2和curr2, 将反转后的部分链表与原链表拼接 image.png
var reverseBetween = function (head, m, n) {
  let prev = null;
  let curr = head;
  for (let i = 1; i < m; i++) {
    prev = curr;
    curr = curr.next;
  }

  let prev2 = prev;
  let curr2 = curr;

  for (let i = m; i <= n; i++) {
    [curr.next, prev, curr] = [prev, curr, curr.next];
  }
  if (m > 1) {
    prev2.next = prev;
  } else {
    head = prev;
  }
  curr2.next = curr;
  return head;
};

141 环形链表

快慢指针

  • 创建两个指针都指向头部, 快指针走两步, 慢指针走一步
  • 如果二者能相遇, 则为环形链表
/**
 * Definition for singly-linked list.
 * function ListNode(val) {
 *     this.val = val;
 *     this.next = null;
 * }
 */

/**
 * @param {ListNode} head
 * @return {boolean}
 */
var hasCycle = function(head) {
    if (head===null) return false
    let fast = head,
        slow = head
    while (fast.next!==null && fast.next.next!==null) {
        fast = fast.next.next
        slow = slow.next
        if (fast === slow) {
            return true
        }
    }
    return false
};

142 环形链表 II

原理图解: image.png 代码:

var detectCycle = function(head) {
    if (head===null) return null
    let fast = head,
        slow = head,
        isCircle = false
    while (fast.next!==null && fast.next.next!==null) {
        fast = fast.next.next
        slow = slow.next
        if (fast===slow) {
            // 此时说明有环, 退出循环
            fast = head
            isCircle = true
            break
        }
    }
    if (!isCircle) return null
    while (fast!==slow) {
        fast = fast.next
        slow = slow.next
    }
    return fast
};

328 奇偶链表

奇偶双指针

/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */
/**
 * @param {ListNode} head
 * @return {ListNode}
 */
var oddEvenList = function(head) {
    if (head===null) return null
    if (head.next===null) return head
    let odd = head
    let even = head.next,
        evenHead = even
    while (even!==null && even.next!==null) {
        odd.next = odd.next.next
        odd = odd.next
        even.next = even.next.next
        even = even.next
    }
    odd.next = evenHead
    return head
};

沉没效应

419 甲板上的战舰

沉没, 类似计算岛屿数量

通解

var countBattleships = function(board) {
    let result = 0
    function dfs(row, col) {
        if (row<0 || row>=board.length || col<0 || col>=board[0].length || board[row][col]!=='X') {
            return
        }
        board[row][col] = '.'
        dfs(row-1, col)
        dfs(row+1, col)
        dfs(row, col-1)
        dfs(row, col+1)
    }
    for (let row=0; row<board.length; row++) {
        for (let col=0; col<board[0].length; col++) {
            if (board[row][col]==='X') {
                result++
                // 从当前点出发, 进行深度优先搜索
                dfs(row, col)
            }
        }
    }
    return result
};

特解: 结合这个题目的特点, 还有一个特殊解法, 大家了解即可

var countBattleships = function(board) {
    let result = 0
    for (let row=0; row<board.length; row++) {
        for (let col=0; col<board[0].length; col++) {
            if (board[row][col]==='X') {
                if (row>0 && board[row-1][col]==='X') {
                    continue
                }
                if (col>0 && board[row][col-1]==='X') {
                    continue
                }
                result++
            }
        }
    }
    return result
};

695 岛屿的最大面积

/**
 * @param {number[][]} grid
 * @return {number}
 */
var maxAreaOfIsland = function(grid) {
    let maxArea = 0
    for (let row=0; row<grid.length;row++) {
        for (let col=0; col<grid[0].length;col++) {
            if (grid[row][col]===1) {
                let area = dfs(row, col)
                maxArea = Math.max(area, maxArea)
            }
        }
    }
    function dfs(row, col) {
        // 边界判断
        if (row<0 || row>=grid.length || col<0 || col>=grid[0].length || grid[row][col]===0) {
            return 0
        }
        // 先沉没当前点, 并将面积+1
        grid[row][col] = 0
        let area = 1
        // 递归调用, 并加上四周点的返回值
        area += dfs(row-1, col)
        area += dfs(row+1, col)
        area += dfs(row, col-1)
        area += dfs(row, col+1)
        return area
    }
    return maxArea
};

733 图像渲染

var floodFill = function(image, sr, sc, color) {
    if (image[sr][sc]===color) return image
    const oldColor = image[sr][sc]
    function dfs(sr, sc) {
        if (sr<0 || sc<0 || sr>=image.length || sc>=image[0].length || oldColor!==image[sr][sc]) {
            return
        }
        image[sr][sc] = color
        dfs(sr-1, sc)
        dfs(sr+1, sc)
        dfs(sr, sc-1)
        dfs(sr, sc+1)
    }
    dfs(sr, sc)
    return image
};

动态规划

动态规划的本质就是: 递归(recursion) + 记忆化(memoization)

509 斐波那契数列 - 梦开始的地方

botom-up

var fib = function(n) {
    // bottom-up + 空间复杂度优化
    if (n<=1) return n
    let cur1 = 0,
        cur2 = 1,
        result = 0
    for (let i=2; i<=n; i++) {
        result = cur1 + cur2
        cur1 = cur2
        cur2 = result
    }
    return result
};

up-bottom 比较难理解

var fib = function(n) {
    // bottom-up + 空间复杂度优化
    if (n<=1) return n
    let cache = []
    cache[0] = 0
    cache[1] = 1
    function memoize(num) {
        if (cache[num]!==undefined) return cache[num]
        cache[num] = memoize(num-1) + memoize(num-2)
        return cache[num]
    }
    let result = memoize(n)
    return result
};

55

62

198

二分搜索

704 二分查找

  • 循环while left<=right
    • let mid = left和right的中间位置
    • if (nums[mid]<target) left = mid+1 砍掉左半边
    • if (nums[mid]<target) right = mid-1 砍掉右半边
  • 循环结束都没有找到, 就返回-1
/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number}
 */
var search = function(nums, target) {
    let left = 0
    let right = nums.length-1
    while (left<=right) {
        let mid = left + Math.floor((right-left)/2)
        if (nums[mid]<target) {
            // 砍掉左半边
            left = mid + 1
        }else if (nums[mid]>target) {
            right = mid-1
        }else {
            return mid
        }
    }
    return -1
};