LeetCode习题整理(js)

分治算法思想:

LeetCode习题整理(js)_第1张图片
LeetCode习题整理(js)_第2张图片

递推算法思想:

LeetCode习题整理(js)_第3张图片

枚举算法思想:

LeetCode习题整理(js)_第4张图片

递归算法思想:

LeetCode习题整理(js)_第5张图片
求阶乘:
LeetCode习题整理(js)_第6张图片

贪婪算法思想:

LeetCode习题整理(js)_第7张图片

试探算法思想:

LeetCode习题整理(js)_第8张图片

1. 两数之和

给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。

示例:

给定 nums = [2, 7, 11, 15], target = 9

因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]

const twoSum = (nums, target) => {
  const prevNums = {};                    // 存储出现过的数字,和对应的索引               

  for (let i = 0; i < nums.length; i++) { // 遍历元素   
    const curNum = nums[i];               // 当前元素   
    const targetNum = target - curNum;    // 满足要求的目标元素   
    const targetNumIndex = prevNums[targetNum]; // 在prevNums中获取目标元素的索引
    if (targetNumIndex !== undefined) {   // 如果存在,直接返回 [目标元素的索引,当前索引]
      return [targetNumIndex, i];
    } else {                              // 如果不存在,说明之前没出现过目标元素
      prevNums[curNum] = i;               // 存入当前的元素和对应的索引
    }
  }
}

2.数字反转

给出一个 32 位的有符号整数,你需要将这个整数中每位上的数字进行反转。

示例 1:

输入: 123
输出: 321

示例 2:

输入: -123
输出: -321

示例 3:

输入: 120
输出: 21

注意:

假设我们的环境只能存储得下 32 位的有符号整数,则其数值范围为 [−231, 231 − 1]。请根据这个假设,如果反转后整数溢出那么就返回 0。

var reverse = function(x) {
    // x ->num
    var str = x.toString() //str->String
    let arr = str.split(''); //arr->Array
    // 判断是否有负号
    if(arr[0]!=="-"){
        let num =Number(arr.reverse().join('')); 
        if(num <= 2147483647 && num >= -2147483648){
            return num;
        }else{
            return 0
        }
    }else {
        delete arr[0];
        let num =Number(arr.reverse().join(''));
        if(num <= 2147483647 && num >= -2147483648){
            return ~num+1;
        }else{
            return 0
        }
    }
};

3.回文数

判断一个整数是否是回文数。回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。

示例 1:

输入: 121
输出: true

示例 2:

输入: -121
输出: false
解释: 从左向右读, 为 -121 。 从右向左读, 为 121- 。因此它不是一个回文数。

示例 3:

输入: 10
输出: false
解释: 从右向左读, 为 01 。因此它不是一个回文数。

/**
 * @param {number} x
 * @return {boolean}
 */
var isPalindrome = function(x) {
    var str = x.toString()
    var arr = str.split('')
    if(x>0 && arr[arr.length-1]!=0){
        let num =Number(arr.reverse().join('')); 
        if (x == num){
            return true
        }else {
            return false
        }
    } else if(x == 0){
        return true
    }else {
        return false
    }
};

简化版:

var isPalindrome = function(x) {
    return x === Number(x.toString().split('').reverse().join(''))
};

双指针的方法:

var isPalindrome = function(x) {
    if(x < 0) return false;
    if(x < 10) return true; 
    let right = 1;
    let left = 0;//初始为 x的总位数
    let sum = x;
    while(sum >= 1){//算出总位数
        sum /= 10;
        left++;
    }
    //获取第n位的数
    let getNum = (_x, n) => {
        return Math.floor(_x % Math.pow(10, n) / Math.pow(10, n - 1));
    }
    while(left > right){
        if(getNum(x, left) != getNum(x, right)) return false;
        left--;
        right++;
    }
    return true;
};

4.罗马数字转整数

罗马数字包含以下七种字符: I, V, X, L,C,D 和 M。

字符 数值
I 1
V 5
X 10
L 50
C 100
D 500
M 1000
例如, 罗马数字 2 写做 II ,即为两个并列的 1。12 写做 XII ,即为 X + II 。 27 写做 XXVII, 即为 XX + V + II 。

通常情况下,罗马数字中小的数字在大的数字的右边。但也存在特例,例如 4 不写做 IIII,而是 IV。数字 1 在数字 5 的左边,所表示的数等于大数 5 减小数 1 得到的数值 4 。同样地,数字 9 表示为 IX。这个特殊的规则只适用于以下六种情况:

I 可以放在 V (5) 和 X (10) 的左边,来表示 4 和 9。
X 可以放在 L (50) 和 C (100) 的左边,来表示 40 和 90。
C 可以放在 D (500) 和 M (1000) 的左边,来表示 400 和 900。
给定一个罗马数字,将其转换成整数。输入确保在 1 到 3999 的范围内。

示例 1:

输入: “III”
输出: 3

示例 2:

输入: “IV”
输出: 4

示例 3:

输入: “IX”
输出: 9

示例 4:

输入: “LVIII”
输出: 58
解释: L = 50, V= 5, III = 3.

示例 5:

输入: “MCMXCIV”
输出: 1994
解释: M = 1000, CM = 900, XC = 90, IV = 4.

/**
 * @param {string} s
 * @return {number}
 */
var romanToInt = function(s) {
    var hashMap = {
        'M': 1000,
        'D': 500,
        'C': 100,
        'L': 50,
        'X': 10,
        'V': 5,
        'I': 1
    };
    var num = 0;
    for(i=0;i

5.最长公共前缀

编写一个函数来查找字符串数组中的最长公共前缀。

如果不存在公共前缀,返回空字符串 “”。

示例 1:

输入: [“flower”,“flow”,“flight”]
输出: “fl”
示例 2:

输入: [“dog”,“racecar”,“car”]
输出: “”
解释: 输入不存在公共前缀。
说明:

所有输入只包含小写字母 a-z 。

var longestCommonPrefix = function(strs) { 
    if(strs.length==0){
        return ''
    }
    var ans = strs[0]
    for(i=0;i

6.有效的括号

给定一个只包括 ‘(’,’)’,’{’,’}’,’[’,’]’ 的字符串,判断字符串是否有效。

有效字符串需满足:

左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
注意空字符串可被认为是有效字符串。

示例 1:

输入: “()”
输出: true

示例 2:

输入: “()[]{}”
输出: true

示例 3:

输入: “(]”
输出: false

示例 4:

输入: “([)]”
输出: false

示例 5:

输入: “{[]}”
输出: true

/**
 * @param {string} s
 * @return {boolean}
 */
var isValid = function(s) {
    const n = s.length;
    if (n % 2 === 1) {
        return false;
    }
    const pairs = new Map([
        [')', '('],
        [']', '['],
        ['}', '{']
    ]);
    const stk = [];
    s.split('').forEach(ch => {
        if (pairs.has(ch)) {
            if (!stk.length || stk[stk.length - 1] !== pairs.get(ch)) {
                return false;
            }
            stk.pop();
        } 
        else {
            stk.push(ch);
        }
    });
    return !stk.length;
};

7.合并两个有序链表

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

示例:

输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4

/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */
/**
 * @param {ListNode} l1
 * @param {ListNode} l2
 * @return {ListNode}
 */
// 非递归
/*
var mergeTwoLists = function(l1, l2) {
    var cew = new ListNode()
    var cewq = cew
    while(l1!=null&l2!=null){
        if(l1.val

8.外观数列

给定一个正整数 n(1 ≤ n ≤ 30),输出外观数列的第 n 项。

注意:整数序列中的每一项将表示为一个字符串。

「外观数列」是一个整数序列,从数字 1 开始,序列中的每一项都是对前一项的描述。前五项如下:

  1. 1
    
  2. 11
    
  3. 21
    
  4. 1211
    
  5. 111221
    

第一项是数字 1

描述前一项,这个数是 1 即 “一个 1 ”,记作 11

描述前一项,这个数是 11 即 “两个 1 ” ,记作 21

描述前一项,这个数是 21 即 “一个 2 一个 1 ” ,记作 1211

描述前一项,这个数是 1211 即 “一个 1 一个 2 两个 1 ” ,记作 111221

示例 1:

输入: 1
输出: “1”
解释:这是一个基本样例。

示例 2:

输入: 4
输出: “1211”
解释:当 n = 3 时,序列是 “21”,其中我们有 “2” 和 “1” 两组,“2” 可以读作 “12”,也就是出现频次 = 1 而 值 = 2;类似 “1” 可以读作 “11”。所以答案是 “12” 和 “11” 组合在一起,也就是 “1211”。

正则表达式解析: \d表示匹配数字,g表示全局匹配, 如:“1aa11aa111”.replace(/\d/g,6)
返回的是"6aa66aa666",就是找出所有数字,并替换成6; \1表示匹配的是 所获取的第1个()匹配的引用,
例如,’(\d)\1’ 匹配两个连续数字字符。如33aa 中的33。如"1a11aa221".replace(/(\d)\1/g,6),
返回的是"1a6aa61",匹配了"11"和"22"; 表示匹配{0,无穷大}次, \1就是表示\1可以出现0次或者更多次,
如"1a11aa221".replace(/(\d)\1*/g,6), 返回的是"6a6aa66",匹配了1,11,22,1;

replace用法: 看个例子: “1aa11aa111”.replace(/(\d)\1*/g,i => i . l e n g t h {i.length} i.length{i[0]}),
返回的是"11aa21aa31",就是1变成1,11变成21,111变成31;replace第二个参数是箭头函数,返回了一个模板字符串

/**
 * @param {number} n
 * @return {string}
 */
// 通过正则合并相同元素完成累加
var countAndSay = function(n) {
    let prev = '1'
    for(let i = 1; i < n; i++){
        prev = prev.replace(/(\d)\1*/g, item =>`${item.length}${item[0]}`)
    }
    return prev
};

递归 + 双指针方法:

核心思想:
创建慢指针和快指针 left、right 来确定每个字符的重复数量。

具体思路:
当 left 和 right 所指字符相同时,right 向右移动一位,直到 right 所指与 left 不同。
确定相同字符的个数,既 right - left 的值,该值表示的即是 left 所指字符的个数。
将结果转为以字符的形式储存。
继续历遍,因为 left 和 right 之间的字符已经历遍过了,所以更新指针 left 至 right。
当 right 超出字符范围时,结束循环。

//递归 + 双指针
var countAndSay = function (n) {
    if (n === 1) {
        return "1";
    };
    let pre = countAndSay(n - 1);
    let result = "", left = 0, right = 0;
    while (right < pre.length) {
        while (pre[left] === pre[right] && right < pre.length) {
            right++;
        };
        result += (right - left).toString() + pre[left];
        left = right;
    }
    return result;
};

9.最大子序和

给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

示例:

输入: [-2,1,-3,4,-1,2,1,-5,4]
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。

/**
 * @param {number[]} nums
 * @return {number}
 */
var maxSubArray = function(nums) {
    let pre = 0, maxAns = nums[0];
    nums.forEach((x) => {
        pre = Math.max(pre + x, x);
        maxAns = Math.max(maxAns, pre);
    });
    return maxAns;
};

10.最后一个单词的长度

给定一个仅包含大小写字母和空格 ’ ’ 的字符串 s,返回其最后一个单词的长度。如果字符串从左向右滚动显示,那么最后一个单词就是最后出现的单词。

如果不存在最后一个单词,请返回 0 。

说明:一个单词是指仅由字母组成、不包含任何空格字符的 最大子字符串。

示例:

输入: “Hello World”
输出: 5

/**
 * @param {string} s
 * @return {number}
 */
var lengthOfLastWord = function(s) {
    s3 = s.trim();
    // 找出目标字符串中所有空白字符替换为一个空白字符
    s2 = s3.replace(/\s+/g, ' ')
    var a = s2.lastIndexOf(' ')
    var i = s2.length-1-a
    if(s2.length == 0){
        return 0
    }else if(s2.length != 0 && i == -1){
        return s2.length
    }else{
        return i
    }
};

11.加一(考虑有无进位)

给定一个由整数组成的非空数组所表示的非负整数,在该数的基础上加一。

最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。

你可以假设除了整数 0 之外,这个整数不会以零开头。

示例 1:

输入: [1,2,3]
输出: [1,2,4]
解释: 输入数组表示数字 123。

示例 2:

输入: [4,3,2,1]
输出: [4,3,2,2]
解释: 输入数组表示数字 4321。

/**
 * @param {number[]} digits
 * @return {number[]}
 */
var plusOne = function(digits) {
    const len = digits.length;
    for(let i = len - 1; i >= 0; i--) {
        digits[i]++;
        digits[i] %= 10;
        if(digits[i]!=0)
            return digits;
    }
    /*
    digits = [...Array(len + 1)].map(_=>0);;
    digits[0] = 1;
    return digits;
    */
    return [1,...digits]
};

12.二进制求和

给你两个二进制字符串,返回它们的和(用二进制表示)。

输入为 非空 字符串且只包含数字 1 和 0。

示例 1:

输入: a = “11”, b = “1”
输出: “100”

示例 2:

输入: a = “1010”, b = “1011”
输出: “10101”

提示:
每个字符串仅由字符 ‘0’ 或 ‘1’ 组成。
1 <= a.length, b.length <= 10^4
字符串如果不是 “0” ,就都不含前导零。

/**
 * @param {string} a
 * @param {string} b
 * @return {string}
 */

// 二进制前缀0b,例如:var a = 0b11;   // a=3
// 数字转各进制字符串用num.toString(n),n为2-36的数字,表示2-36进制
var addBinary = function(a, b) {
  return (BigInt('0b' + a) + BigInt('0b' + b)).toString(2)
}

13.删除排序链表中的重复元素

给定一个排序链表,删除所有重复的元素,使得每个元素只出现一次。

示例 1:

输入: 1->1->2
输出: 1->2

示例 2:

输入: 1->1->2->3->3
输出: 1->2->3

/**
 * Definition for singly-linked list.
 * function ListNode(val) {
 *     this.val = val;
 *     this.next = null;
 * }
 */
/**
 * @param {ListNode} head
 * @return {ListNode}
 */
var deleteDuplicates = function(head) {
    var pre = head;
    while(pre&&pre.next){
        if(pre.val==pre.next.val){
            pre.next = pre.next.next
        }  else {
           pre = pre.next 
        }
    }
    return head
};

14.合并两个有序数组

给你两个有序整数数组 nums1 和 nums2,请你将 nums2 合并到 nums1 中,使 nums1 成为一个有序数组。

说明:

初始化 nums1 和 nums2 的元素数量分别为 m 和 n 。
你可以假设 nums1 有足够的空间(空间大小大于或等于 m + n)来保存 nums2 中的元素。

示例:

输入:
nums1 = [1,2,3,0,0,0], m = 3
nums2 = [2,5,6], n = 3

输出:[1,2,2,3,5,6]

/**
 * @param {number[]} nums1
 * @param {number} m
 * @param {number[]} nums2
 * @param {number} n
 * @return {void} Do not return anything, modify nums1 in-place instead.
 */
var merge = function(nums1, m, nums2, n) {
  let index1 = m - 1
  let index2 = n - 1
  let tail = m + n - 1
  while (index2 >= 0) {
    if (nums1[index1] > nums2[index2]) { 
      nums1[tail] = nums1[index1]
      index1--
      tail--
    } else {
      nums1[tail] = nums2[index2]
      index2--
      tail--
    }
  }
};

15.相同的树

给定两个二叉树,编写一个函数来检验它们是否相同。

如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的。

LeetCode习题整理(js)_第9张图片

/**
 * Definition for a binary tree node.
 * function TreeNode(val, left, right) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.left = (left===undefined ? null : left)
 *     this.right = (right===undefined ? null : right)
 * }
 */
/**
 * @param {TreeNode} p
 * @param {TreeNode} q
 * @return {boolean}
 */
var isSameTree = function(p, q) {
    if(p == null && q == null) 
        return true;
    if(p == null || q == null) 
        return false;
    if(p.val != q.val) 
        return false;
    return isSameTree(p.left, q.left) && isSameTree(p.right, q.right);
};

16.对称二叉树

给定一个二叉树,检查它是否是镜像对称的。
LeetCode习题整理(js)_第10张图片
递归:

/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
/**
 * @param {TreeNode} root
 * @return {boolean}
 */
/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
/**
 * @param {TreeNode} root
 * @return {boolean}
 */
var isSymmetric = function (root) {
    const check = (left, right) => {   // 参数接收左右子树
    if (!left && !right) return true // 左右子树都不存在 也是对称的 
    if (left && right) {             // 左右子树都存在,要继续判断
      return left.val === right.val &&   // 左右子树的顶点值要相等
        check(left.left, right.right) && // 左子树的left和右子树的right相等
        check(left.right, right.left)    // 左子树的right和右子树的left相等
    }
    return false;  // 左右子树中的一个不存在,一个存在,不是对称的
  }
  return !root || check(root.left, root.right) // root为null也是对称的
                                               // 不满足则调用check判断左右子树
};

迭代:

/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.root1 = this.root2 = null;
 * }
 */
/**
 * @param {TreeNode} root
 * @return {boolean}
 */
const isSymmetric = (root) => {
  // 1. 设置当前层
  const nowRoot = [root, root];
  // 2. 判断当前层是否可以延续
  while (nowRoot.length) {
    // 3. 获取左右部分
    const root1 = nowRoot.pop();
    const root2 = nowRoot.pop();
    // 4. 如果两者是空节点,继续
    if (!root1 && !root2) {
      continue;
    }
    // 5. 如果左边右边只有一个空,或者左边的值不等于右边
    if (!root1 || !root2 || root1.val !== root2.val) {
      return false;
    }
    // 6. 重点:添加值
    nowRoot.push(root1.left);
    nowRoot.push(root2.right);
    nowRoot.push(root1.right);
    nowRoot.push(root2.left);
  }
  // 7. 如果上面没情况发生
  return true;
};

17.二叉树的最大深度

LeetCode习题整理(js)_第11张图片

/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number}
 */
var maxDepth = function(root) {
    if(!root) {
        return 0;
    } else {
        const left = maxDepth(root.left);
        const right = maxDepth(root.right);
        return Math.max(left, right) + 1;
    }
};

18.二叉树的层次遍历

LeetCode习题整理(js)_第12张图片

/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
/**
 * @param {TreeNode} root
 * @return {number[][]}
 */

/*     BFS
var levelOrderBottom = function(root) {
    if(!root) return []
    let res = [], 
        queue = [root]
    while(queue.length) {
        let curr = [],
            temp = []    
        while(queue.length) {
            let node = queue.shift()
            curr.push(node.val)
            if(node.left) temp.push(node.left)
            if(node.right) temp.push(node.right)
        }
        res.push(curr)
        queue = temp
    }
    return res.reverse()
};
*/
// DFS
var levelOrderBottom = function(root) {
    const res = []
    var dep = function (node, depth){
        if(!node) return
        res[depth] = res[depth]||[]
        res[depth].push(node.val)
        dep(node.left, depth + 1)
        dep(node.right, depth + 1)
    }
    dep(root, 0)
    return res.reverse()   
};

19.将有序数组转换为二叉搜索树

LeetCode习题整理(js)_第13张图片

/**
 * Definition for a binary tree node.
 * function TreeNode(val) {
 *     this.val = val;
 *     this.left = this.right = null;
 * }
 */
/**
 * @param {number[]} nums
 * @return {TreeNode}
 */
var sortedArrayToBST = function (nums) {
  if (nums.length == 0) return null;
  let cen = Math.floor(nums.length / 2)
  let node = new TreeNode(nums[cen])
  node.left = sortedArrayToBST(nums.slice(0, cen))
  node.right = sortedArrayToBST(nums.slice(cen + 1))
  return node;
};
答一:自顶向下(暴力法)
解题思路: 自顶向下的比较每个节点的左右子树的最大高度差,如果二叉树中每个节点的左右子树最大高度差小于等于 1 ,即每个子树都平衡时,此时二叉树才是平衡二叉树

代码实现:


var isBalanced = function (root) {
  if(!root) return true
  return Math.abs(depth(root.left) - depth(root.right)) <= 1
        && isBalanced(root.left)
        && isBalanced(root.right)
}
var depth = function (node) {
    if(!node) return -1
    return 1 + Math.max(depth(node.left), depth(node.right))
}
复杂度分析:

时间复杂度:O(nlogn),计算 depth 存在大量冗余操作
空间复杂度:O(n)
解答二:自底向上(优化)
解题思路: 利用后续遍历二叉树(左右根),从底至顶返回子树最大高度,判定每个子树是不是平衡树 ,如果平衡,则使用它们的高度判断父节点是否平衡,并计算父节点的高度,如果不平衡,返回 -1 。

遍历比较二叉树每个节点 的左右子树深度:

比较左右子树的深度,若差值大于 1 则返回一个标记 -1 ,表示当前子树不平衡
左右子树有一个不是平衡的,或左右子树差值大于 1 ,则二叉树不平衡
若左右子树平衡,返回当前树的深度(左右子树的深度最大值 +1 )
代码实现:


var isBalanced = function (root) {
    return balanced(root) !== -1
};
var balanced = function (node) {
    if (!node) return 0
    const left = balanced(node.left)
    const right = balanced(node.right)
    if (left === -1 || right === -1 || Math.abs(left - right) > 1) {
        return -1
    }
    return Math.max(left, right) + 1
}
复杂度分析:

时间复杂度:O(n)
空间复杂度:O(n)

你可能感兴趣的:(js)