javascript 力扣leetcode hot100题解

1、两数之和

点评:最初的梦想

var twoSum = function (nums, target) {
    let map = new Map();
    let result = [];
    for (let i = 0; i < nums.length; i++) {
        let left = target - nums[i];
        if (map.has(left)) {
            return [map.get(left), i];
        }
        map.set(nums[i], i);
    }
};

49、字母异位词分组

点评:然后梦想就破灭了

var groupAnagrams = function (strs) {
    if (strs === null) return [['']];
    let map = new Map();
    let result = [];
    for (let i = 0; i < strs.length; i++) {
        let words = strs[i].split('').sort().join('');
        if (!map.has(words)) {
            map.set(words, [strs[i]]);
        } else {
            map.get(words).push(strs[i])
        }
    }
    for (let [key, value] of map) {
        result.push(value)
    }
    return result
};

128、最长连续序列

点评:指定一个动态的dp值随时记录

var longestConsecutive = function (nums) {
    if(nums.length === 0) return 0;
    nums.sort((a, b) => a - b);
    let len = 1;
    let max = 1;
    let dp = nums[0];
    for (let i = 1; i < nums.length; i++) {
        if (nums[i] === (dp + 1)) {
            len++;
            dp = nums[i];
        } else if (nums[i] === dp) {
            continue;
        } else {
            max = Math.max(max, len);
            len = 1;
            dp = nums[i];
        }
    }
    max = Math.max(max, len);
    return max
};

283、移动零

点评:其实单指针也可以,主要是记录0的索引。

var moveZeroes = function (nums) {
    let left;
    let flag = true;
    for (let i = 0; i < nums.length; i++) {
        if (nums[i] === 0 && flag) {
            left = i;
            flag = false
        } else {
            if (nums[i] === 0) continue
            if (left || left === 0) {
                [nums[left], nums[i]] = [nums[i], nums[left]];
                left++;
            }
        }
    }
    return nums
};

11、盛最多水的容器

点评:双指针yyds,记住维护最小值

var maxArea = function (height) {
    let left = 0, right = height.length - 1;
    let square = 0;
    while (left <= right) {
        let [min, max] = [Math.min(height[left], height[right]), Math.max(height[left], height[right])];
        square = Math.max(square, min * (right - left));
        if (height[left] <= height[right]) {
            left++;
        } else {
            right--
        }
    }
    return square
};

15、三数之和

点评:哈希表能免去一轮for循环,记得去重

var threeSum = function (nums) {
    nums.sort((a, b) => a - b);
    let result = [];
    for (let i = 0; i < nums.length; i++) {
        if (nums[i] === nums[i - 1]) continue;
        let map = new Map();
        let repeat = {};
        for (let j = i + 1; j < nums.length; j++) {
            let x = 0 - nums[i] - nums[j];
            if (map.has(x)) {
                let key = [nums[i], nums[j], x].join('');
                if (!repeat[key]) {
                    result.push([nums[i], nums[j], x]);
                    repeat[key] = (repeat[key] || 0) + 1;
                }
            } else {
                map.set(nums[j]);
            }
        }
    }
    return result
};

42、接雨水

点评:据说是某宇宙厂最爱出的题?应用单调栈

const len = height.length;
    if(len <= 2) return 0; // 可以不加
    const st = [];// 存着下标,计算的时候用下标对应的柱子高度
    st.push(0);
    let sum = 0;
    for(let i = 1; i < len; i++){
        if(height[i] < height[st[st.length - 1]]){ // 情况一
            st.push(i);
        }
        if (height[i] == height[st[st.length - 1]]) {  // 情况二
            st.pop(); // 其实这一句可以不加,效果是一样的,但处理相同的情况的思路却变了。
            st.push(i);
        } else { // 情况三
            while (st.length !== 0 && height[i] > height[st[st.length - 1]]) { // 注意这里是while
                let mid = st[st.length - 1];
                st.pop();
                if (st.length !== 0) {
                    let h = Math.min(height[st[st.length - 1]], height[i]) - height[mid];
                    let w = i - st[st.length - 1] - 1; // 注意减一,只求中间宽度
                    sum += h * w;
                }
            }
            st.push(i);
        }
    }
    return sum;

3、无重复字符的最长子串

点评:滑动窗不解释

var lengthOfLongestSubstring = function (s) {
    let ans = 0;
    let left = 0;
    const window = new Set();
    for(let right = 0;right<s.length;right++) {
        const c = s[right];
        while(window.has(c)) {
            window.delete(s[left]);
            left++;
        }
        window.add(c);
        ans = Math.max(ans,right-left+1);
    }
    return ans
};

438、找到字符串中所有字母异位词

点评:更复杂的滑动窗

var findAnagrams = function(s, p) {
    let pObj = {};
    for(let key of p) {
        pObj[key] = (pObj[key] || 0) + 1;
    }
    let left=0,right=0,valid=0;
    let window = {};
    let result = [];

    while(right<s.length) {
        let c = s[right];
        right++;
        if(pObj[c]) {
            window[c] = (window[c] || 0) + 1;
            if(window[c] === pObj[c]) {
                valid++;
            }
        }
        while(right-left>=p.length){
            if(valid === Object.keys(pObj).length) {
                result.push(left)
            }
            let d = s[left];
            left++;
            if(pObj[d]) {
                if(pObj[d] === window[d]) {
                    valid--
                }
                
            }    window[d]--
        }
    }
    return result
};

560、和为K的子数组

点评:额外空间换时间,创造前缀和额外数组

var subarraySum = function (nums, k) {
    const hashmap = {};
    let acc = 0;
    let count = 0;

    for (let i = 0; i < nums.length; i++) {
        acc += nums[i];

        if (acc === k) count++;

        if (hashmap[acc - k] !==  undefined) {
            count += hashmap[acc - k];
        }

        if (hashmap[acc] ===  undefined) {
            hashmap[acc] = 1;
        } else {
            hashmap[acc] += 1;
        }
    }

    return count;
}

239、滑动窗口最大值

点评:

53、最大子数组和

点评:维护前i项和,如果sum<0,将sum重置为0

var maxSubArray = function (nums) {
    let sum = 0;
    let max = -Infinity;
    for (let i = 0; i < nums.length; i++) {
       sum += nums[i];
       if(sum > max) {
        max= sum;
       } if(sum<0) {
        sum =0;
        continue;
       }
    }
    return max
};

56、合并区间

点评:对二维数组排序,维护左边界和右边界。

var merge = function (intervals) {
    intervals.sort((a, b) => {
        if (a[0] !== b[0]) {
            return a[0] - b[0]
        } else {
            return b[1] - a[1]
        }
    })//[[1,3],[2,6],[8,10],[15,18]]
    let left = intervals[0][0];
    let right = intervals[0][1];
    let result = [];
    for (let i = 1; i < intervals.length; i++) {
        if (intervals[i][0] <= right && intervals[i][0] >= left) {
            if (intervals[i][1] > right) {
                right = intervals[i][1];
            }
        } else {
            result.push([left, right]);
            left = intervals[i][0];
            right = intervals[i][1];
        }
    }
    result.push([left, right]);
    return result
};

189、轮转数组

点评:注意不允许有返回值,直接在原数组上修改。三部曲:先反转原数组,再翻转0 - k-1;最后翻转k - n-1

var rotate = function (nums, k) {
    function reverse(i,j) {
        while(i<j) {
            [nums[i],nums[j]] = [nums[j],nums[i]];
            i++;
            j--;
        }
    }
    const n = nums.length;
    k %= n;
    reverse(0,n-1);
    reverse(0,k-1);
    reverse(k,n-1);
};

238、除自身以外数组的乘积

点评:前遍历一次,后遍历一次,最后两个结果相乘就是结果

var productExceptSelf = function (nums) {
    let ans = [1]
    //前项积
    for (let i = 1; i < nums.length; i++) {
        ans[i] = ans[i - 1] * nums[i - 1]
    }
    // 当前项等于前项积乘以后项积
    let end = 1
    for (let j = nums.length - 1; j >= 0; j--) {
        ans[j] *= end
        end *= nums[j]
    }
    return ans
};

73、矩阵置零

点评:用两个额外的数组空间row和col,一个大小为n,一个为m,分别记录矩阵为0的行和列,共计遍历两次原矩阵。

var setZeroes = function(matrix) {
    let m = matrix.length;
    let n = matrix[0].length;
    let row = new Array(m).fill(0);
    let col = new Array(n).fill(0);

    for(let i = 0;i<m;i++) {
        for(let j = 0;j<n;j++) {
            if(matrix[i][j] === 0) {
                row[i] = col[j] = 1;
            }
        }
    }
    for(let i =0;i<m;i++) {
        for(let j = 0;j<n;j++) {
            if(row[i] === 1 || col[j] === 1) {
                matrix[i][j] = 0;
            }
        }
    }
    return matrix
};

54、螺旋矩阵

点评:四个边每个边遍历一次,再向内收缩

var spiralOrder = function(matrix) {
    let left = 0;
    let right = matrix[0].length-1;
    let top = 0;
    let bottom = matrix.length-1;
    let res = [];
    while(true){
        for(let i = left;i<=right;i++){
            res.push(matrix[top][i]);
        }
        top++;
        if(top>bottom) break;
        for(let i = top;i<=bottom;i++){
            res.push(matrix[i][right]);
        }
        right--;
        if(right<left) break;
        for(let i = right;i>=left;i--){
            res.push(matrix[bottom][i]);
        }
        bottom--;
        if(bottom<top) break;
        for(let i = bottom;i>=top;i--){
            res.push(matrix[i][left]);
        }
        left++;
        if(left>right) break;
    }
    return res;
};

48、旋转图像

点评:沿着水平对称轴翻转,再沿对角线翻转

var rotate = function(matrix) {
let n = matrix.length;

let start = Math.floor(n / 2);

for (let i = 0; i < start; i++) {
    for (let j = 0; j < n; j++) {
        [matrix[i][j], matrix[n - i - 1][j]] = [matrix[n - i - 1][j], matrix[i][j]];
    }
}
for (let i = 0; i < n; i++) {
    for (let j = 0; j < i; j++) {
        [matrix[i][j], matrix[j][i]] = [matrix[j][i], matrix[i][j]];
    }
}
return matrix
};

240、搜索二维矩阵II

点评:从右上角开始遍历,target比cur大,就向下,比cur小,就向左。

var searchMatrix = function(matrix, target) {
    if(matrix.length==0) return false // 判空
    let [left, up]=[matrix[0].length-1, 0]; // 初始化位置
    while(left>=0 && up<matrix.length){
        if(matrix[up][left]>target){
            left--;
        }else if(matrix[up][left]<target){
            up++;
        }else{
            return true;
        }
    }
    return false;
};

160、相交链表

点评:两个步幅相等的指针。相交链表总有相等的子节点

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

206、反转链表

点评:先保存住当前节点的下一个节点。让cur指向pre, 将pre的值赋为cur,然后再移动cur。

var reverseList = function (head) {
    let pre = null,
        tmp = null,
        cur = head;
    if (!head || !head.next)
        return head;
    while (cur) {
        tmp = cur.next;
        cur.next = pre;
        pre = cur;
        cur = tmp;
    }
    return pre
};

234、回文链表

点评:存成数组用双指针判断

var isPalindrome = function(head) {
    let arr = [];
    while(head){
        arr.push(head.val);
        head = head.next;
    }
    let start = 0,end = arr.length -1;
    while(start<end){
        if(arr[start] !== arr[end]){
            return false
        }
        start++;
        end--
    }
    return true;
};

141、环形链表

点评:快慢指针,有环必相交

var hasCycle = function(head) {
    let fast = head;
    let slow = head;
    while(fast) {
        if(fast.next == null) return false;
        slow = slow.next;
        fast = fast.next.next;
        if(slow === fast) return true;
    }
    return false
};

142、环形链表II

点评:快慢指针,相遇时只是证明有环。需要把slow指针重新放在头节点,再次相遇证明相交点。

var detectCycle = function (head) {
    if (!head || !head.next) return null;
    let slow = head.next, fast = head.next.next;
    while (fast && fast.next) {
        slow = slow.next;
        fast = fast.next.next;
        if (fast === slow) {
            slow = head;
            while (slow != fast) {
                slow = slow.next;
                fast = fast.next;
            }
            return slow;
        }
    }
    return null;
};

21、合并两个有序链表

点评:

var mergeTwoLists = function(list1, list2) {
    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(list1,list2.next);
        return list2
    }
};

2、两数相加

点评:

var addTwoNumbers = function(l1, l2) {
    let addOne = 0
    let sum = new ListNode('0')
    let head = sum
    while (addOne || l1 || l2) {
        let val1 = l1 !== null ? l1.val : 0
        let val2 = l2 !== null ? l2.val : 0
        let r1 = val1 + val2 + addOne
        addOne = r1 >= 10 ? 1 : 0
        sum.next = new ListNode(r1 % 10)
        sum = sum.next 
        if (l1) l1 = l1.next 
        if (l2) l2 = l2.next 
    }
    return head.next
};

19、删除链表的倒数第N个节点

点评:找两指针的差,让slow指针的next指向next.next,最后返回ret.next即可

var removeNthFromEnd = function(head, n) {
    const ret = new ListNode(0,head);
    let fast = ret, slow = ret;
    while(n--) {
        fast = fast.next;
    }
    while(fast.next != null) {
        fast = fast.next;
        slow = slow.next;
    }
    slow.next = slow.next.next;
    return ret.next;
};

24、两两交换链表中的节点

点评:利用虚拟头节点

var swapPairs = function (head) {
  let ret = new ListNode(0, head), temp = ret;
  while (temp.next && temp.next.next) {
    let cur = temp.next.next, pre = temp.next;
    pre.next = cur.next;
    cur.next = pre;
    temp.next = cur;
    temp = pre;
  }
  return ret.next;
};

138、随机链表的复制

点评: 第一次遍历生成具有val属性的链表,第二次遍历根据map映射关系,将random和next指针指向对应节点或者null;

var copyRandomList = function(head) {
    let cloneTarget = {};
    let cur = head;
    const map = new Map(); 
    while(cur){
        cur.set(cur,new Node(cur.val));
        cur = cur.next;
    }
    cur = head;
    while(cur) {
        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);
};

148、排序链表

点评: 转成数组再排序再生成链表

var sortList = function(head) {
    let arr = [];
    while(head) {
        arr.push(head.val)
        head = head.next;
    }
    arr.sort((a,b) => a-b);
    let dummpy = new ListNode(0);
    let prev = dummpy;
    for(let i = 0;i<arr.length;i++) {
       prev.next = new ListNode(arr[i]);
       prev = prev.next
    }
    return dummpy.next
};

146、LRU缓存

 点评:利用map的数据结构。三步:1、对LRU类添加limit,和以map为结构的cache变量。

2、在get方法中,首先检查cache中有没有key,如果有,调用get方法返回值,并且要把key-value删除重新添加进cache。这一步的作用主要是为了解决太久没用需要删除这一功能。如果没有,就直接返回-1 3、对于put方法,首先检查cache中有没有key,如果有也是删了重set。如果没有,检查size是否超过limit,,超过了就删除最早的键值对。

var LRUCache = function(capacity) {
    this.limit = capacity;
    this.cache = new Map();
};

LRUCache.prototype.get = function(key) {
    let tmp;
    if(this.cache.has(key)){
        tmp = this.cache.get(key);
        this.cache.delete(key);
        this.cache.set(key,tmp)
    }
    return tmp ?? -1;
};

LRUCache.prototype.put = function(key, value) {
    if(this.cache.has(key)) {
        this.cache.delete(key);
    }
    this.cache.set(key,value);
    if(this.cache.size > this.limit) {
        this.cache.delete(this.cache.keys().next().value);
    }
};

94、二叉树的中序遍历

点评:左中右

var inorderTraversal = function(root) {
    const res = [];
    inorder(root,res)
    return res
};

const inorder = (root,res) => {
    if(root === null) return;
    inorder(root.left,res);
    res.push(root.val);
    inorder(root.right,res);
}

 104、二叉树的最大深度

点评:层序遍历,遍历完一层count++

var maxDepth = function(root) {
    let queue = [root];
    let count = 0;
    while(queue.length && root !== null) {
        let length = queue.length;
        while(length--){
           let node = queue.shift();

           node.left && queue.push(node.left);
           node.right && queue.push(node.right);

        }
        count++;
    }
    return count;
};

226、翻转二叉树

点评: 层序遍历 + 交换左右值

var invertTree = function(root) {
    let queue = [root];
    const exchange = (root,left,right) => {
        [left,right] = [right,left];
        root.left = left;
        root.right = right;
    }
    while(queue.length && root !== null) {
        let n = queue.length;
        for(let i = 0; i<n; i++){
            let node = queue.shift();
            exchange(node,node.left,node.right);

            node.left && queue.push(node.left);
            node.right && queue.push(node.right);
        }
    }
    return root;
};

101、对称二叉树

点评:left.left和right.right比较,left.right和right.left比较 

var isSymmetric = function(root) {
    var compare = function(left,right){
        if(left !== null &&right === null || left === null && right!==null){
            return false;
        } else if (left === null && right === null) {
            return true;
        } else if (left.val !== right.val) {
            return false;
        }
        let incompare = compare(left.left,right.right);
        let outcompare = compare(left.right,right.left);

        return incompare && outcompare;
    }
    return compare(root.left,root.right)
};

543、二叉树的直径

点评:

var diameterOfBinaryTree = function (root) {
    let ans = 1;
    function depth(node){
        if(!node) return 0
        let L = depth(node.left);
        let R = depth(node.right);
        ans = Math.max(ans,L+R+1)
        return Math.max(L,R) + 1
    }
    depth(root)
    return ans - 1
};

 102、二叉树的层序遍历

点评:

var levelOrder = function(root) {
    let res = [],queue = [];
    queue.push(root);
    if(root === null) {
        return res;
    }
    while(queue.length !== 0) {
        let length = queue.length;
        let curLevel = [];
        for(i = 0;i<length;i++){
            let node = queue.shift();
            curLevel.push(node.val);

            node.left && queue.push(node.left);
            node.right && queue.push(node.right);
        }
        res.push(curLevel);
    }
    return res
};

108、将有序数组转化为二叉搜索树

点评: 从中间索引劈开,左边为左子树,右边为右子树,直接递归

var sortedArrayToBST = function (nums) {
    const dfs = (nums) => {
        if(nums.length === 0) return null;
        let mid = Math.floor(nums.length/2);
        const root = new TreeNode(nums[mid]);
        root.left = dfs(nums.slice(0,mid));
        root.right = dfs(nums.slice(mid+1));
        return root
    }
    return dfs(nums)
};

98、验证二叉搜索树

点评:中序遍历得到的结果应正好为递增数组

var isValidBST = function(root) {
    let arr = [];
    var addNumber = function(root) {
        if(root) {
            addNumber(root.left);
            arr.push(root.val);
            addNumber(root.right);
        }
    }
    addNumber(root);
    for(let i = 0; i<arr.length; i++) {
        if(arr[i] >= arr[i+1]) {
            return false;
        }
    }
    return true;
};

230、二叉搜索树中第K小的元素

点评:还是中序遍历

var kthSmallest = function(root, k) {
    let nums=[];
    function dfs(root){
      if(!root) return;
      dfs(root.left);
      nums.push(root.val);
      dfs(root.right);
    }
    dfs(root);
    return nums[k-1];
};

199、二叉树的右视图

点评: 层序遍历取每一层最后一个元素

var rightSideView = function(root) {
    let res = [], queue=[];
    queue.push(root);
    
    while(queue.length && root!==null) {
        let length = queue.length;
        while(length--) {

            let node = queue.shift()
            node.left && queue.push(node.left)
            node.right && queue.push(node.right)
            if(!length) {
                res.push(node.val)
            }
        }
    }
   return res
};

114、二叉树展开为链表

点评: 展开的链表节点顺序为二叉树前序遍历的结果 / 也可以将最左边的左子树接到右边,再将原右子树接到末尾。

var flatten = function (root) {
  /*
  函数的定义:给 flatten 函数输入一个节点 root,那么以 root 为根的二叉树就会被拉平为一条链表。
   */
  // base case
  if (root == null) return;

  flatten(root.left);
  flatten(root.right);

  // 1、左右子树已经被拉平成一条链表
  // 先用两个变量把原先的左右子树保存起来
  let left = root.left;
  let right = root.right;

  // 2、将左子树作为右子树
  root.left = null;
  root.right = left;

  // 3、将原先的右子树接到当前右子树的末端
  while (root.right != null) {
    root = root.right;
  }
  root.right = right;
};

105、从前序和中序构造二叉树

点评:分割

var buildTree = function(preorder, inorder) {
    if(preorder === -1 && inorder === -1) {
        return -1;
    }
    if(!preorder.length) {
        return null
    }
    let head = preorder.shift();
    let headIndex = inorder.indexOf(head);

    const root = new TreeNode(head);
    root.left = buildTree(preorder.slice(0,headIndex),inorder.slice(0,headIndex));
    root.right = buildTree(preorder.slice(headIndex),inorder.slice(headIndex+1));

    return root;
};

 437、路径总和III

点评:前缀和的思想,利用哈希表

var pathSum = function (root, targetSum) {
    let count = 0;
    const prefixSum = new Map();
    const dfs = (node, currSum) => {
        if (node === null) return;
        currSum += node.val;
        if (currSum === targetSum) {
            count++;
        }
        count += prefixSum.get(currSum - targetSum) || 0;
        prefixSum.set(currSum, (prefixSum.get(currSum) || 0) + 1);

        dfs(node.left, currSum);
        dfs(node.right, currSum);
        // 回溯:移除当前路径的和  
        prefixSum.set(currSum, prefixSum.get(currSum) - 1);
    };
    dfs(root, 0);
    return count;
};

236、二叉树的最近公共祖先

点评: 妙啊我只能说。如果根节点是pq中的一个,直接返回根节点。如若不然,先看左子树,再看右子树。如果不是根节点,则肯定在一边。看看left和right哪个是null,就返回另一个。都不是null,直接返回根节点

var lowestCommonAncestor = function(root, p, q) {
    if(root === null || root === p || root === q){
        return root;
    };
    let left = lowestCommonAncestor(root.left,p,q);
    let right = lowestCommonAncestor(root.right,p,q);
    if(left === null) {
        return right;
    }
    if(right === null) {
        return left;
    }
    return root;
};

200、岛屿数量

点评:图论第一题,梦想开始的地方 dfs和bfs都可

const numIslands = (grid) => {
  let count = 0
  for (let i = 0; i < grid.length; i++) {
    for (let j = 0; j < grid[0].length; j++) {
      if (grid[i][j] === '1') {
        count++
        turnZero(i, j, grid)
      }
    }
  }
  return count
}
function turnZero(i, j, grid) {
  if (i < 0 || i >= grid.length || j < 0 
       || j >= grid[0].length || grid[i][j] === '0') return
  grid[i][j] = '0'
  turnZero(i, j + 1, grid)
  turnZero(i, j - 1, grid)
  turnZero(i + 1, j, grid)
  turnZero(i - 1, j, grid)
}

994、烂橘子

点评: 先遍历一遍grid,统计新鲜橘子个数和初始的烂橘子。创建队列放初始的这些烂橘子。然后以队列的长度为判断条件,设置初始值为-1,对于每个烂橘子四个方向都让他变烂,再吧新的烂的放队列里,直到队列为空。

var orangesRotting = function(grid) {
    const dir = [[1,0],[-1,0],[0,1],[0,-1]];
    let n = grid.length;
    let m = grid[0].length;
    let fresh = 0;
    let q = [];
    for(let i = 0;i<n;i++){
        for(let j = 0;j<m;j++){
            if(grid[i][j] === 1){
                fresh++;
            } else if(grid[i][j] === 2) {
                q.push([i,j]);
            }
        }
    }
    let ans = -1;
    while(q.length) {
        ans++;
        const tmp = q;
        q = [];
        for(const [x,y] of tmp){
            for(const [i,j] of [[x-1,y],[x+1,y],[x,y-1],[x,y+1]]) {
                if(0<=i && i<m && 0<=j && j<n && grid[i][j] === 1){
                    fresh--;
                    grid[i][j] = 2;
                    q.push([i,j]);
                }
            } 
        }
    }
    return fresh ? -1 : Math.max(ans,0)
};

207、课程表

点评:拓扑排序

var canFinish = function(numCourses, prerequisites) {
    let n = prerequisites.length;
    if(n<=1) return true;
    let indegrees = new Array(numCourses).fill(0);
    let map = new Map();
    for(let i = 0;i<n;i++){
        let [x,y] = prerequisites[i];
        indegrees[y]++;
        if(!map.has(x)) {
            map.set(x,[y]);
        }else {
            map.get(x).push(y);
        }
    }
    const queue = [];
    for(let i = 0;i<indegrees.length;i++){
        if(indegrees[i] === 0) queue.push(i)
    }
    let count = 0;
    while(queue.length) {
        const selected = queue.shift();
        count++;
        const back = map.get(selected);
        if(back && back.length) {
            for(let i = 0;i<back.length;i++){
                indegrees[back[i]]--;
                if(indegrees[back[i]] === 0) {
                    queue.push(back[i])
                }
            }
        }
    } 
return count === numCourses

};

208、前缀树

点评:

function Node(val, isEnd){
    this.val = val
    this.child = {}
    this.isEnd = isEnd|| false
}

var Trie = function() {
    this.root = new Node()
};


Trie.prototype.insert = function(word) {
    let cur = this.root
    for (let c of word){
        if (cur.child[c] == null) cur.child[c] = new Node(c)
        cur = cur.child[c]
    }
    cur.isEnd = true;
};


Trie.prototype.search = function(word) {
    let cur = this.root
    for (let c of word){
        if (cur.child[c] == null) return false
        cur = cur.child[c]
    }
    return cur.isEnd
};


Trie.prototype.startsWith = function(prefix) {
    let cur = this.root
    for (let c of prefix){
        if (cur.child[c] == null) return false
        cur = cur.child[c]
    }
    return true
};

46、全排列 

点评:回溯经典

var permute = function(nums) {
    let path = [],res = [];
    const backtracking = function(arr) {
        if(path.length === nums.length) {
            res.push(Array.from(path));
            return;
        }
        for(let i = 0;i< nums.length; i++) {
            if(arr[i]) {
                continue;
            }
            path.push(nums[i]);
            arr[i] = 1;
            backtracking(arr);
            path.pop();
            arr[i] = 0;
        }
    }
    backtracking([]);
    return res
};

78、子集

点评: 

var subsets = function(nums) {
    const n = nums.length;
    const ans = [];
    const path = [];
    function dfs(i) {
        if(i === n) {
            ans.push(path.slice());
            return
        }
        dfs(i+1);
        path.push(nums[i]);
        dfs(i+1);
        path.pop()
    }
    dfs(0);
    return ans;
};

39、组合总和

点评: 

var combinationSum = function (candidates, target) {
    let count = 0;
    let path = [];
    let result = [];
    const backtracking = function (index, sum) {
        for (let i = index; i < candidates.length; i++) {
            if (sum === target) {
                result.push(Array.from(path));
                count++;
                return
            }
            if (!(target / candidates[i])) {
                count++;
                result.push([candidates[i]]);
                continue;
            }
            if (sum > target) {
                return;
            }
            sum += candidates[i];
            path.push(candidates[i]);
            backtracking(i, sum);
            sum -= candidates[i];
            path.pop(candidates[i]);
        }
    }
    backtracking(0, 0);
    return result;
};

 22、括号生成

点评:

var generateParenthesis = function(n) {
    const m = n * 2;
    const ans = [];
    const path = Array(m);
    // i=目前填了多少个括号
    // open=左括号个数,i-open=右括号个数
    function dfs(i, open) {
        if (i === n * 2) {
            ans.push(path.join(""));
            return;
        }
        if (open < n) { // 可以填左括号
            path[i] = '(';
            dfs(i + 1, open + 1);
        }
        if (i - open < open) { // 可以填右括号
            path[i] = ')';
            dfs(i + 1, open);
        }
    }
    dfs(0, 0);
    return ans;
};

79、单词搜索

点评:dfs+回溯

var exist = function (board, word) {
  //越界处理
  board[-1] = []; // 这里处理比较比较巧妙,利用了js的特性
  board.push([])

  //寻找首个字母
  for (let y = 0; y < board.length; y++) {
    for (let x = 0; x < board[0].length; x++) {
      if (word[0] === board[y][x] && dfs(word,board,y, x, 0)) return true
    }
  }
  return false
};
const dfs = function(word,board,y, x, i){
    if (i + 1 === word.length) return true
    var tmp = board[y][x];
    // 标记该元素已使用
    board[y][x] = false
    if (board[y][x + 1] === word[i + 1] && dfs(word,board,y, x + 1, i + 1)) return true
    if (board[y][x - 1] === word[i + 1] && dfs(word,board,y, x - 1, i + 1)) return true
    if (board[y + 1][x] === word[i + 1] && dfs(word,board,y + 1, x, i + 1)) return true
    if (board[y - 1][x] === word[i + 1] && dfs(word,board,y - 1, x, i + 1)) return true
    // 回溯
    board[y][x] = tmp
  }

131、分割回文串

点评: 和子集那道题类似,只不过增加一个回文串的判断。

var isPalindrome = function(s, left, right) {
    while (left < right) {
        if (s.charAt(left++) !== s.charAt(right--)) {
            return false;
        }
    }
    return true;
}

var partition = function(s) {
    const n = s.length;
    const ans = [];
    const path = [];

    function dfs(i) {
        if (i === n) {
            ans.push(path.slice()); // 复制 path
            return;
        }
        for (let j = i; j < n; j++) { // 枚举子串的结束位置
            if (isPalindrome(s, i, j)) {
                path.push(s.substring(i, j + 1));
                dfs(j + 1);
                path.pop(); // 恢复现场
            }
        }
    }

    dfs(0);
    return ans;
};

35、搜索插入位置

点评:二分法

var searchInsert = function(nums, target) {
   let left = 0, right = nums.length - 1, ans = nums.length;
   while (left <= right) {
    let mid = Math.floor((right - left) / 2) + left;
    const num = nums[mid];
    if (target === num){
        return mid;
    } else {
        if (target > num) {
            left = mid + 1;
        } else {
            ans = mid;
            right = mid - 1;
        }
    }
   }
   return ans;
};

74、搜索二维矩阵

点评:可以避开二分法,直接从右上角搜索 

var searchMatrix = function(matrix, target) {
    let n = matrix.length;
    let m = matrix[0].length;
    let [i,j] = [0,m-1];
    while(i<n && j>=0) {
        if(target < matrix[i][j]) {
            j--;
        }else if(target === matrix[i][j]) {
            return true;
        }else if(target > matrix[i][j]) {
            i++;
        }
    }
    return false
};

34、在排序数组中查找元素的第一个位置和最后一个位置

点评: 二分,遍历两次

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

33、搜索旋转排序数组

 点评:二分法,判断mid和left和right值

var search = function (nums, target) {
    let start = 0;
    let end = nums.length - 1;

    while (start <= end) {
        // >> 1 相当于除以2向下取整
        let mid = (start + end) >> 1;

        if (nums[mid] === target) {
            return mid;
        }

        // 如果中间数小于最右边数,则右半段是有序的
        // 如果中间数大于最右边数,则左半段是有序的
        if (nums[mid] < nums[end]) {
            // 判断target是否在(mid, end]之间
            if (nums[mid] < target && target <= nums[end]) {
                // 如果在,则中间数右移即start增大
                start = mid + 1;
            } else {
                // 如果不在,则中间数左移即end减小
                end = mid - 1;
            }
        } else {
            // [start, mid)
            if (nums[start] <= target && target < nums[mid]) {
                end = mid - 1;
            } else {
                start = mid + 1;
            }
        }
    }

    return -1;
};

153、寻找排序数组中的最小值

 点评:

var findMin = function(nums) {
    var left = 0;
    var right = nums.length - 1;
    while (left < right) {
        var mid = (left + right) >> 1;
        if (nums[mid] > nums[right]) {
            left = mid + 1;
        } else {
            right = mid;
        }
    }
    return nums[left];
};

17、电话号码随机组合

点评:创建对象

var letterCombinations = function (digits) {
    let obj = {
        '2': ['a', 'b', 'c'],
        '3': ['d', 'e', 'f'],
        '4': ['g', 'h', 'i'],
        '5': ['j', 'k', 'l'],
        '6': ['m', 'n', 'o'],
        '7': ['p', 'q', 'r', 's'],
        '8': ['t', 'u', 'v'],
        '9': ['w', 'x', 'y', 'z']
    };

    let res = [];
    let path = [];

    if (digits.length === 0) return res;

    let arrNum = [];
    for (let i = 0; i < digits.length; i++) {
        arrNum.push(obj[digits[i]]);
    }

    const backtracking = (index) => {
        if (path.length === digits.length) {
            res.push(path.join(''));
            return;
        }

        for (let j = 0; j < arrNum[index].length; j++) {
            path.push(arrNum[index][j]);
            backtracking(index + 1);
            path.pop();
        }
    };

    backtracking(0);
    return res;
};

20、有效括号

点评: 栈

var isValid = function(s) {
    if (s.length % 2) { // s 长度必须是偶数
        return false;
    }
    const mp = {')': '(', ']': '[', '}': '{'};
    const st = [];
    for (const c of s) {
        if (!mp.hasOwnProperty(c)) { // c 是左括号
            st.push(c); // 入栈
        } else if (st.length === 0 || st.pop() !== mp[c]) { // c 是右括号
            return false; // 没有左括号,或者左括号类型不对
        }
    }
    return st.length === 0; // 所有左括号必须匹配完毕
};

155、最小栈

点评: 


var MinStack = function () {
  // 主栈指针
  this.head = null
  // 辅助栈指针
  this.subHead = null
};

/** 
 * @param {number} x
 * @return {void}
 */
MinStack.prototype.push = function (x) {
  const newItem = {
    data: x,
    next: null
  }
  const subNewItem = {
    data: x,
    next: null
  }
  // 主栈判断逻辑
  if (!this.head) {
    this.head = newItem
  } else {
    newItem.next = this.head
    this.head = newItem
  }
  // 辅助栈判断逻辑
  if (!this.subHead) {
    this.subHead = subNewItem
  } else {
    if (this.subHead.data >= x) {
      subNewItem.next = this.subHead
      this.subHead = subNewItem
    }
  }
};

/**
 * @return {void}
 */
MinStack.prototype.pop = function () {
  if (!this.head) return false
  const result = this.head.data
  if (this.subHead && result === this.subHead.data) {
    this.subHead = this.subHead.next
  }
  this.head = this.head.next
  return result
};

/**
 * @return {number}
 */
MinStack.prototype.top = function () {
  if (!this.head) return false
  return this.head.data
};

/**
 * @return {number}
 */
MinStack.prototype.getMin = function () {
  if (!this.head || !this.subHead) return false
  return this.subHead.data
};

394、字符串解码

 点评:维护两个栈,一个数字栈,一个字符串栈。遇到数字先计算倍数,遇见字母先计算result。直到遇到[, 将倍数入数字栈,将result入字符串栈。并且都清空。遇到]时,意味着要清算了(cry)。取出数字栈顶,取出字符串栈顶元素 拼接 当前result * 倍数。

var decodeString = function (s) {
    let numStack = [];
    let strStack = [];
    let num = 0;
    let result = '';
    for (const c of s) {
        if (!isNaN(c)) {
            num = num * 10 + Number(c);
        } else if (c === '[') {
            strStack.push(result);
            result = '';
            numStack.push(num);
            num = 0;
        } else if (c === ']') {
            let repeatTimes = numStack.pop();
            result = strStack.pop() + result.repeat(repeatTimes);
        } else {
            result += c;
        }
    }
    return result
};

739、每日温度

点评:单调栈 

var dailyTemperatures = function (temperatures) {
    let arr = [];
    let result = new Array(temperatures.length).fill(0);
    let index = [0];
    arr.push(temperatures[0])
    for (let i = 1; i < temperatures.length; i++) {
        
        while(temperatures[i] > arr[arr.length - 1]) {
            result[index[index.length-1]] = i - index[index.length-1];
            arr.pop();
            index.pop()
        }
        arr.push(temperatures[i]);
        index.push(i);
        if (temperatures[i] < arr[arr.length- 1]) {
            arr.push(temperatures[i]);
            index.push(i);
        } 

    }
    return result
};

215、数组中的第K大元素

点评:

 121、买卖股票的最佳时机

点评:直接从后面遍历

var maxProfit = function(prices) {
    let result = 0;
    let max = prices[prices.length - 1];
    for(let i = prices.length - 2;i>=0;i--){
        if(prices[i] > max) {
            max = prices[i];
        }else {
            result = Math.max(max - prices[i],result);
        }
    }
    return result;
};

 

55、跳跃游戏

点评:技巧在于重复刷新cover的值。

var canJump = function(nums) {
    if(nums.length <= 1) {
        return true;
    } else if (nums[0] === 0) {
        return false;
    }
    let cover = 0;
    for(let i = 0;i<= cover;i++) {
        cover = Math.max(cover,nums[i]+i);
        if(cover>= nums.length-1) {
            return true;
        }
    }
    return false;
};

45、跳跃游戏II

点评:

var jump = function(nums) {
let count = 0, curIndex = 0,maxIndex = 0;
    for(let i = 0;i<nums.length-1;i++) {
        maxIndex = Math.max(maxIndex,nums[i] + i);
        if(i===curIndex) {
            curIndex = maxIndex;
            count++;
        }
    }
    return count;
};

 763、划分字母区间

点评:先利用哈希表整体遍历一遍,统计各个字符的出现位置。再遍历一次s,设置left,right为0;使right更新为遍历到的字母最远的位置。这样就可以防止一个字母在多个片段中出现。

var partitionLabels = function(s) {
    let hash = {}
    for(let i = 0; i < s.length; i++) {
        hash[s[i]] = i
    }
    let result = []
    let left = 0
    let right = 0
    for(let i = 0; i < s.length; i++) {
        right = Math.max(right, hash[s[i]])
        if(right === i) {
            result.push(right - left + 1)
            left = i + 1
        }
    }
    return result
};

70、爬楼梯

点评:就是斐波那契数列

var climbStairs = function (n) {
    let dp = [];
    dp[0] = 1;
    dp[1] = 2;
    if (n === 1) {
        return 1
    }
    const climbFun = (n) => {
        return dp[n - 1] + dp[n - 2]
    }

    for (let i = 2; i < n; i++) {
        dp.push(climbFun(i))
    }
    return dp[dp.length - 1]
};

118、杨辉三角

点评:

var generate = function (numRows) {
    if (numRows === 1) {
        return [[1]]
    } else if (numRows === 2) {
        return [[1], [1, 1]]
    } else {
        let i = 3;
        let result = [[1], [1, 1]];
        while (i <= numRows) {
            let arr = new Array(i);
            arr[0] = 1;
            arr[arr.length - 1] = 1;
            for (let j = 1; j < arr.length - 1; j++) {
                arr[j] = result[i - 2][j] + result[i - 2][j - 1]
            }
            result.push(arr);
            i++;
        }
        return result
    }
};

198、打家劫舍

点评 

var rob = function(nums) {
    let dp = new Array(nums.length + 1).fill(0);
    dp[1] = nums[0];
    for(let i = 1 ;i<nums.length;i++) {
        dp[i + 1] = Math.max(dp[i-1] +nums[i],dp[i])
    }
    return  dp[nums.length]
};

279、完全平方数

点评

var numSquares = function(n) {
       let dp = new Array(n + 1).fill(Infinity)
    dp[0] = 0

    for(let i = 1; i**2 <= n; i++) {
        let val = i**2
        for(let j = val; j <= n; j++) {
            dp[j] = Math.min(dp[j], dp[j - val] + 1)
        }
    }
    return dp[n]

};

322、零钱兑换

点评:

var coinChange = function(coins, amount) {
    
    let dp = new Array(amount+1).fill(Infinity);
    dp[0] = 0;
    for(let i = 0;i<coins.length;i++) {
        for(let j = 1;j<=amount;j++) {
            if(j >= coins[i] ) {
                dp[j] = Math.min(dp[j-coins[i]]+1,dp[j])
            }
        }
    }
    if(dp[amount] === Infinity) {
        return -1
    }
    return dp[amount]
};

139、单词拆分

点评:

var wordBreak = function(s, wordDict) {
    let dp = new Array(s.length+1).fill(0);
    dp[0] = 1;
    for(let i = 0;i<=s.length;i++) {
        for(let j = 0;j<wordDict.length;j++) {
            if(i>=wordDict[j].length) {
                if(s.slice(i-wordDict[j].length,i) === wordDict[j] && dp[i-wordDict[j].length]) {
                    dp[i] = 1;
                }
            }
        }
    }
   
    
    return dp[dp.length-1]
};

300、最长递增子序列

 点评:

var lengthOfLIS = function(nums) {
    let dp = new Array(nums.length).fill(1);
    let result = 1;
    for(let i = 1;i<nums.length;i++) {
        
        for(let j = 0;j<=i-1;j++) {
            if(nums[i] > nums[j]) {
                dp[i] = Math.max(dp[i],dp[j] + 1)
            } 
        }
        result = Math.max(result,dp[i])
    }
    return result

};

152、乘积最大子数组

 点评:

let max = min = nums[0], dp = [nums[0]]

    for (let i=1; i<nums.length; i++) {
        if (nums[i] < 0) {
           [max, min] = [min, max]
        }

        max = Math.max(max*nums[i], nums[i])
        min = Math.min(min*nums[i], nums[i])

        dp[i] = max
    }

    return Math.max(...dp)
};

416、分割等和子集

点评: 

var canPartition = function(nums) {
    const sum = (nums.reduce((p, v) => p + v));
    if (sum & 1) return false;
    const dp = Array(sum / 2 + 1).fill(0);
    for(let i = 0; i < nums.length; i++) {
        for(let j = sum / 2; j >= nums[i]; j--) {
            dp[j] = Math.max(dp[j], dp[j - nums[i]] + nums[i]);
            if (dp[j] === sum / 2) {
                return true;
            }
        }
    }
    return dp[sum / 2] === sum / 2;
};

62、不同路径

点评: 

var uniquePaths = function(m, n) {
    let dp = new Array(m).fill(1).map((item) => new Array(n).fill(1));
    for(let i = 1; i< m;i++) {
        for(let j = 1;j<n;j++) {
            dp[i][j] = dp[i-1][j] + dp[i][j-1];
            
        }
        
    }
    return dp[m-1][n-1]
    
};

64、最小路径和

点评:多维dp

var minPathSum = function(grid) {
    let n = grid.length;
    let m = grid[0].length;
    let dp = new Array(n).fill(0).map(()=>new Array(m).fill(0));
    dp[0][0] = grid[0][0];
    for(let i = 1;i<n;i++){
        dp[i][0] = grid[i][0] + dp[i-1][0];
    }
    for(let j = 1;j<m;j++){
        dp[0][j] = grid[0][j] + dp[0][j-1];
    }
    for(let i = 1;i<n;i++){
        for(let j = 1;j<m;j++){
            dp[i][j] = Math.min(dp[i-1][j],dp[i][j-1]) + grid[i][j];
        }
    }
    return dp[n-1][m-1]
};

5、最长回文子串 

点评:双指针

let max = '';
let s = 'abbac';
const helper = function (l, r) {
    while (l >= 0 && r < s.length && s[l] === s[r]) {
        l--;
        r++;
    }
    const maxStr = s.slice(l + 1, r + 1 - 1);
    if (maxStr.length > max.length) {
        max = maxStr;
    }
}
for (let i = 0; i < s.length; i++) {
    helper(i, i);
    helper(i, i + 1);
}

console.log(max)

1143、最长公共子序列

点评:二维dp用来表示text1的前i个字符与text2的前j个字符的最大公共子序列。明白这一点这题就做明白了

var longestCommonSubsequence = function (text1, text2) {
    let dp = Array.from(Array(text1.length + 1), () => Array(text2.length + 1).fill(0));
    let max = 0;
    for (let i = 1; i <= text1.length; i++) {
        for (let j = 1; j <= text2.length; j++) {
            if (text1[i - 1] === text2[j - 1]) {
                dp[i][j] = dp[i - 1][j - 1] + 1;
                max = Math.max(max, dp[i][j])
            } else {
                dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1])
            }
        }
    }
    return max;
};

 72、编辑距离

点评:初始化多维dp,要点就是取左上角,左边,上边,中的最小值+1

var minDistance = function(word1, word2) {
    const len1 = word1.length;
    const len2 = word2.length;
    const dp = Array.from(Array(len1 + 1), () => Array(len2 + 1))
    dp[0][0] = 0

    for (let i = 1; i <= len1; i++) dp[i][0] = dp[i - 1][0] + 1

    for (let i = 1; i <= len2; i++) dp[0][i] = dp[0][i - 1] + 1

    for (let i = 1; i <= len1; i++) {
        for (let j = 1; j <= len2; j++) {
            if (word1[i - 1] == word2[j - 1]) 
                dp[i][j] = dp[i - 1][j - 1]
            else
                dp[i][j] = Math.min(dp[i - 1][j - 1], dp[i - 1][j], dp[i][j-1]) + 1
        }
    }

    return dp[len1][len2]
};

136、只出现一次的数字

点评:异或大法好

var singleNumber = function(nums) {
    let ans = 0;
    for(const x of nums) {
        ans ^= x;
    }
    return ans
};

169、多数元素

点评:

var majorityElement = function(nums) {
    let count = 1;
    let majority = nums[0];
    for(let i = 1;i<nums.length;i++){
        if(count === 0){
            majority = nums[i]
        }
        if(nums[i] === majority) {
            count++
        }else {
            count--
        }
    }
    return majority
};

75、颜色分类

点评:双指针

var sortColors = function (nums) {
    let L = 0,
        ans = 0,
        R = nums.length - 1;
    for (i = 0; i <= R; i++) {
        if (nums[i] === 0) {
            ans = nums[L];
            nums[L] = nums[i];
            nums[i] = ans;
            L++;
        } else if (nums[i] === 2) {
            ans = nums[R];
            nums[R] = nums[i];
            nums[i] = ans;
            R--;
            i--;
        }
        
    }
    return nums;
};

31、下一个排列

 点评:

287、寻找重复数

点评:

var findDuplicate = function(nums) {
  let slowPointer = 0
  let fastPointer = 0
  while (true) {
    slowPointer = nums[slowPointer]
    fastPointer = nums[nums[fastPointer]]
    if (slowPointer == fastPointer) {
      let _slowPointer = 0
      while (nums[_slowPointer] !== nums[slowPointer]) {
        slowPointer = nums[slowPointer]
        _slowPointer = nums[_slowPointer]
      }
      return nums[_slowPointer]
    }
  }
};

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值