LeetCode《编程能力入门》刷题笔记(34 题全)

LeetCode《编程能力入门》刷题笔记

小标题以 _ 开头的题目和解法代表独立想到思路及编码完成,其他是对题解的学习。

每次做完题目(不管做没做出来)都会精心研究其他题解并做笔记认真学习。

思路最简单的解法、最优解法,这里都有

基本数据类型

1. 在区间范围内统计奇数数目

题目:1523. 在区间范围内统计奇数数目

_解法1:暴力迭代

/**
 * @param {number} low
 * @param {number} high
 * @return {number}
 * https://leetcode-cn.com/problems/count-odd-numbers-in-an-interval-range/
 * 暴力
 */
var countOdds = function (low, high) {
    let count = 0;
    for (let i = low; i <= high; i++)
        if (i & 1 == 1) count++
    return count
};

_解法2:数学(找规律)

/**
 * @param {number} low
 * @param {number} high
 * @return {number}
 * 数学
 */
var countOdds = function (low, high) {
    let lowOdd = (low & 1) === 1 // low 是否为奇数
    let highOdd = (high & 1) === 1 // high 是否为奇数
    if (lowOdd && highOdd) 
        return 1 + (high - low) / 2
    return (high - low) / 2      
};

2. 去掉最低工资和最高工资后的工资平均值

题目:1491. 去掉最低工资和最高工资后的工资平均值

_解法1:迭代

/**
 * @param {number[]} salary
 * @return {number}
 * 迭代
 */
var average = function (salary) {
    let sum = 0, min = salary[0], max = salary[0]

    salary.forEach(s => {
        min = Math.min(min, s)
        max = Math.max(max, s)
        sum += s
    })

    return (sum - max - min) / (salary.length - 2)
};

运算符

3. 位1的个数

题目:191. 位1的个数

_解法1:迭代

/**
 * @param {number} n - a positive integer
 * @return {number}
 * 暴力(超时)
 */
var hammingWeight = function (n) {
    let count = 0
    while (n != 0) {
        if (n & 1) count++;
        n <<= 1
    }
    return count
};

_解法2:位运算

/**
 * @param {number} n - a positive integer
 * @return {number}
 * 位运算
 */
var hammingWeight = function (n) {
    let count = 0
    while (n != 0) {
        n &= (n - 1) // 消去最后一位1
        count++
    }
    return count
};

4. 整数的各位积和之差

题目:1281. 整数的各位积和之差

JS 中转整数:

  • res = Math.floor(res);返回值为 小于等于 其数值参数的最大整数值
  • res = Math.ceil(res); 返回值为 大于等于 其数字参数的最小整数

_解法1:迭代

/**
 * @param {number} n
 * @return {number}
 * 迭代
 */
var subtractProductAndSum = function (n) {
    let sum = 0, product = 1
    while (n > 0) {
        let num = Math.floor(n % 10)
        sum += num
        product *= num
        n = Math.floor(n / 10)
    }
    return product - sum
}

解法2:转字符串再转数组

/**
 * @param {number} n
 * @return {number}
 * 转字符串转数组再迭代
 */
var subtractProductAndSum = function (n) {
    let nums = n.toString().split('').map(e => parseInt(e))
    let sum = 0, product = 1
    for (let num of nums) {
        sum += num
        product *= num
    }
    return product - sum
}

条件语句

5. 三角形的最大周长

_解法1:排序 + 迭代

/**
 * @param {number[]} nums
 * @return {number}
 * 排序后遍历相邻元素
 */
var largestPerimeter = function (nums) {
    nums = nums.sort((a, b) => b - a)
    for (let i = 2; i < nums.length; i++) {
        let a = nums[i - 2], b = nums[i - 1], c = nums[i]
        if (b + c > a)
            return a + b + c
    }
    return 0
};

6. 找到最近的有相同X或Y坐标的点

题目:1779. 找到最近的有相同 X 或 Y 坐标的点

_解法1:迭代

/**
 * @param {number} x
 * @param {number} y
 * @param {number[][]} points
 * @return {number}
 */
 var nearestValidPoint = function(x, y, points) {
    let min = Number.MAX_VALUE, idx = -1
    for (let i = 0; i < points.length; i++) {
        let point = points[i]
        let distance = getManhattanDistance(point, [x, y])
        // 剪枝, 遇到相同坐标直接返回
        if (distance === 0) return i
        // 寻找曼哈顿距离最小的下标
        if ((point[0] == x || point[1] == y) && distance < min) {
            min = distance
            idx = i
        }
    }
    return idx
};

/**
 * 计算曼哈顿距离
 * @param {number[]} p1
 * @param {number[]} p2
 * @return {number}
 */
var getManhattanDistance = (p1, p2) => {
    return Math.abs(p1[0] - p2[0]) + Math.abs(p1[1] - p2[1]) 
}

循环

7. 数组元素积的符号

题目:Loading Question… - 力扣(LeetCode) (leetcode-cn.com)

_解法1:reduce

/**
 * @param {number[]} nums
 * @return {number}
 */
var arraySign = function (nums) {
    return nums.reduce((pre, cur) => pre * (cur > 0 ? 1 : (cur < 0 ? -1 : 0)), 1)
};

_解法2:迭代

 var arraySign = function(nums) {
    let res = 1
    for (let i = 0; i < nums.length; i++) {
        // 剪枝
        if (nums[i] == 0) return 0;
        res *= nums[i] > 0 ? 1 : -1
    }
    return res
};

8. 判断能否形成等差数列

题目:1502. 判断能否形成等差数列

解法1:模拟(迭代)

/**
 * https://leetcode-cn.com/problems/can-make-arithmetic-progression-from-sequence/
 * @param {number[]} arr
 * @return {boolean}
 */
var canMakeArithmeticProgression = function (arr) {
    // js 负数排序, 需要传入函数, 否则是字符串排序
    arr = arr.sort((a, b) => a - b)
    let step = arr[1] - arr[0] // 根据前两个数算出步长 
    for (let i = 1; i < arr.length; i++)
        if (arr[i] != arr[i - 1] + step)
            return false;
    return true;
};

var canMakeArithmeticProgression = function (arr) {
    arr = arr.sort((a, b) => a - b)
    for (let i = 1; i < arr.length - 1; i++)
        return false;
    return true;
};

9. 快乐数

题目:202. 快乐数

_解法1:模拟

/**
 * 快乐数
 * @param {number} n
 * @return {boolean}
 * 模拟(map)
 */
var isHappy = function (n) {
    let newVal = 0, set = new Set()
    while (newVal != 1) {
        newVal = 0
        while (n) {
            newVal += Math.pow(~~(n % 10), 2)
            n /= 10
        }
        if (set.has(newVal)) return false;
        set.add(newVal)
        n = newVal
    }
    return true
};

比较优雅的写法:

var isHappy = function (n) {
    let set = new Set()
    while (n != 1) {
        n = bitSquareSum(n)
        if (set.has(n)) return false;
        set.add(n)
    }
    return true
};

/**
 * 求每位平方和
 */
var bitSquareSum = function (n) {
    let sum = 0
    while (n) {
        sum += ~~(n % 10) * ~~(n % 10)
        n /= 10
    }
    return sum
}

解法2:快慢指针

判断循环要想到快慢指针。

/**
 * 快慢指针
 */
var isHappy = function (n) {
    let slow = n, fast = n
    do {
        slow = bitSquareSum(slow);
        fast = bitSquareSum(fast);
        fast = bitSquareSum(fast);
    } while (slow != fast)

    return slow == 1
};

/**
 * 求每位平方和
 */
var bitSquareSum = function (n) {
    let sum = 0
    while (n) {
        sum += ~~(n % 10) * ~~(n % 10)
        n /= 10
    }
    return sum
}

10. 仅执行一次字符串交换能否使两个字符串相等

题目:仅执行一次字符串交换能否使两个字符串相等

提示:

  • 1 <= s1.length, s2.length <= 100
  • s1.length == s2.length
  • s1s2 仅由小写英文字母组成

_解法1:模拟

思路:

  1. count 记录不同的字符的个数,必须为 2
    • 循环过程中发现大于 2 直接返回(剪枝操作),比如 “aaaxyz” “bbbxyz”
    • 循环结束发现只有 1 个字符不同,肯定无法交换成一样的,比如 “aa” “ab”
  2. 由于确定不同字符个数必须为 2,idx1, idx2 分别记录位置
    • 循环结束后,查看 s1[idx2] 是否等于 s2[idx1],s2[idx1] 是否等于 s1[idx2]
/**
 * https://leetcode-cn.com/problems/check-if-one-string-swap-can-make-strings-equal/
 * 仅执行一次字符串交换能否使两个字符串相等
 * @param {string} s1
 * @param {string} s2
 * @return {boolean}
 */
var areAlmostEqual = function (s1, s2) {
    let count = 0, idx1 = -1, idx2 = -1
    for (let i in s1) {
        if (s1[i] != s2[i]) {
            idx1 < 0 ? idx1 = i : idx2 = i
            count++
        }
        if (count > 2) return false
    }
    if (count == 1) return false;
    return s1[idx1] == s2[idx2] && s1[idx2] == s2[idx1]
};

函数

11. N 叉树的前序遍历

题目:589. N 叉树的前序遍历

_解法1:递归

/**
 * // Definition for a Node.
 * function Node(val, children) {
 *    this.val = val;
 *    this.children = children;
 * };
 */

/**
 * @param {Node|null} root
 * @return {number[]}
 * 递归
 */
var preorder = function (root) {
    if (root == null) return []
    let res = [root.val]
    for (let n of root.children)
        res.push(...preorder(n))
    return res
};

12. 下一个更大元素 I

题目:496. 下一个更大元素 I

提示:

  • 1 <= nums1.length <= nums2.length <= 1000
  • 0 <= nums1[i], nums2[i] <= 104
  • nums1nums2 中所有整数 互不相同
  • nums1 中的所有整数同样出现在 nums2

_解法1:模拟

/**
 * @param {number[]} nums1
 * @param {number[]} nums2
 * @return {number[]}
 */
var nextGreaterElement = function (nums1, nums2) {
    let res = new Array(nums1.length), idx = 0
    for (let i = 0; i < nums1.length; i++) {
        for (let j = nums2.indexOf(nums1[i]); j < nums2.length; j++) {
            if (nums2[j] > nums1[i]) {
                res[idx++] = nums2[j]
                break
            }
            if (j == nums2.length - 1)
                res[idx++] = -1
        }
    }
    return res
};

_解法2:单调栈

13. 缀点成线

题目:1232. 缀点成线

提示:

  • 2 <= coordinates.length <= 1000
  • coordinates[i].length == 2
  • -10^4 <= coordinates[i][0], coordinates[i][1] <= 10^4
  • coordinates 中不含重复的点

_解法1:数学 + 模拟

/**
 * @param {number[][]} coordinates
 * @return {boolean}
 * 数学 + 模拟
 */
var checkStraightLine = function (coordinates) {
    // 取两个点来决定 fx 方程
    let p1 = coordinates[0], p2 = coordinates[1]

    // x = a
    if (p1[0] - p2[0] == 0) {
        for (let coordinate of coordinates)
            if (coordinate[0] != p1[0])
                return false
        return true
    }

    // y = b
    if (p1[1] - p2[1] == 0) {
        for (let coordinate of coordinates)
            if (coordinate[1] != p1[1])
                return false
        return true
    }

    // y = kx + b
    let k = (p1[1] - p2[1]) / (p1[0] - p2[0])
    let b = p1[1] - k * p1[0] // b = y - kx
    const fx = (x, k, b) => k * x + b
    for (let coordinate of coordinates)
        if (coordinate[1] != fx(coordinate[0], k, b))
            return false
    return true
};

数组

14. 所有奇数长度数组的和

题目:1588. 所有奇数长度子数组的和

解法1:暴力

// 暴力
// [1,4,2,5,3]
// 1, 14, 142, 14253
// 4, 425
// 2, 253
// 5
// 3
var sumOddLengthSubarrays = function (arr) {
    let sum = 0;
    const n = arr.length;
    for (let start = 0; start < n; start++) {
        for (let length = 1; start + length <= n; length += 2) { // 奇数
            const end = start + length - 1;
            for (let i = start; i <= end; i++)
                sum += arr[i];
        }
    }
    return sum;
};

解法2:前缀和

/**
 * 前缀和
 * 利用前缀和,可以快速的计算出两个下标之间的所有元素的和
 */
var sumOddLengthSubarrays = function (arr) {
    const n = arr.length;
    const prefixSums = new Array(n + 1).fill(0);
    for (let i = 0; i < n; i++)
        prefixSums[i + 1] = prefixSums[i] + arr[i];

    let sum = 0;
    for (let start = 0; start < n; start++) {
        for (let length = 1; start + length <= n; length += 2) {
            const end = start + length - 1;
            sum += prefixSums[end + 1] - prefixSums[start];
        }
    }
    return sum;
};

15. 移动零

题目:283. 移动零 - 力扣(LeetCode) (leetcode-cn.com)

解法1:双指针

常规思路:

  • 遍历时将整数全部移动到前面, 剩下的补 0
  • left 记录不为 0 的数字个数
var moveZeroes = function (nums) {
    let left = 0, right = 0
    while (right < nums.length) {
        if (nums[right] != 0)
            nums[left++] = nums[right]
        right++
    }
    while (left < nums.length)
        nums[left++] = 0
};

优化成一轮循环:

  • 遍历时 right 遇到不为 0 的数字,就和前面为 0 的 left 交换
  • 遇到为 0 的数字,left 不动,right 继续往前
var moveZeroes = function (nums) {
    let left = 0, right = 0
    while (right < nums.length) {
        if (nums[right] != 0) {
            if (nums[left] == 0) {
                [nums[left], nums[right]] = [nums[right], nums[left]]
            }
            left++
        }
        right++
    }
};

16. 最富有客户的资产总量

题目:1672. 最富有客户的资产总量

_解法1:暴力模拟

/**
 * @param {number[][]} accounts
 * @return {number}
 */
var maximumWealth = function (accounts) {
    let max = -1
    for (let i = 0; i < accounts.length; i++) {
        let sum = 0
        for (let j = 0; j < accounts[0].length; j++)
            sum += accounts[i][j]
        max = Math.max(max, sum)
    }
    return max
};

/**
 * 高阶函数简化代码
 */
var maximumWealth = function (accounts) {
    let max = -1
    for (let account of accounts)
        max = Math.max(max, account.reduce((pre, cur) => pre + cur), 0)
    return max
}

17. 矩阵对角元素的和

题目:矩阵对角线元素的和

_解法1:模拟

var diagonalSum = function (mat) {
	  let sum = 0, len = mat.length
    // 左上角 -> 右下角
    let i = 0, j = 0
    while (i < len && j < len) {
        sum += mat[i][j]
        mat[i++][j++] = -1
    }
  	// 右上角 -> 左下角
    i = 0, j = len - 1
    while (i < len && j >= 0) {
        if (mat[i][j] != -1)
            sum += mat[i][j]
        i++
        j--
    }
    return sum
};

优化写法:一轮循环

var diagonalSum = function (mat) {
    let len = mat.length, sum = 0
    for (let i = 0; i < len; i++)
        sum += mat[i][i] + mat[i][len - i - 1]
    return len & 1 ? sum - mat[~~(len / 2)][~~(len / 2)] : sum
};

18. 重塑矩阵

题目:566. 重塑矩阵

_解法1:模拟

/**
 * @param {number[][]} mat
 * @param {number} r
 * @param {number} c
 * @return {number[][]}
 */
var matrixReshape = function (mat, r, c) {
    let m = mat.length, n = mat[0].length
    if (m * n != r * c) return mat

    let idx = 0, tmpArr = new Array(), res = []
    for (let i = 0; i < m; i++) {
        for (let j = 0; j <= n; j++) {
            if (idx == c) {
                res.push(tmpArr)
                tmpArr = []
                idx = 0
            }
            if (j == n) break
            tmpArr.push(mat[i][j])
            idx++
        }
    }
    return res
};

解法2:数学

class Solution {
    public int[][] matrixReshape(int[][] mat, int r, int c) {
        int m = mat.length, n = mat[0].length;
        if (m * n != r * c) return mat;

        int[][] res = new int[r][c];
        for (int i = 0; i < m * n; i++) 
            res[i / c][i % c] = mat[i / n][i % n];
        return res;
    }
}

字符串

19. 交替合并字符串

题目:1768. 交替合并字符串

提示:

  • 1 <= word1.length, word2.length <= 100
  • word1word2 由小写英文字母组成

_解法1:模拟

/**
 * @param {string} word1
 * @param {string} word2
 * @return {string}
 */
var mergeAlternately = function (word1, word2) {
    let chars1 = [...word1], chars2 = [...word2]
    let p1 = 0, p2 = 0, res = []
    while (p1 < chars1.length || p2 < chars2.length) {
        if (p1 < chars1.length) res.push(chars1[p1++])
        if (p2 < chars2.length) res.push(chars2[p2++])
    }
    return res.join("")
};

代码优化:

var mergeAlternately = function (word1, word2) {
    let res = []
    for (let i = 0; i < word1.length || i < word2.length; i++) {
        if (i < word1.length) res.push(word1[i])
        if (i < word2.length) res.push(word2[i])
    }
    return res.join('')
};

20. 设计 Goal 解析器

题目:1678. 设计 Goal 解析器

提示:

  • 1 <= command.length <= 100
  • command"G""()" 和 / 或 "(al)" 按某种顺序组成

_解法1:字符串替换

var interpret = function (command) {
    return command.replaceAll('()', 'o').replaceAll('(al)', 'al')
};

_解法2:迭代 + 分情况

var interpret = function (command) {
    let res = []
    for (let i = 0; i < command.length; i++) {
        if (command[i] == 'G') res.push('G')
        if (command[i] == '(') {
            if (command[i + 1] == ')') {
                res.push('o')
                i++
            } else {
                res.push('al')
                i += 3
            }
        }
    }
    return res.join('')
};

21. 找不同

题目:389. 找不同

_解法1:哈希

 var findTheDifference = function (s, t) {
    let map = new Map()
    for (let c of s)
        map.set(c, map.has(c) ? map.get(c) + 1 : 1)
    for (let c of t) {
        if (!map.has(c)) return c
        if (map.get(c) > 0)
            map.set(c, map.get(c) - 1)
        else if (map.get(c) == 0)
            return c
    }
    return ''
};

_解法2:位运算

class Solution {
    public char findTheDifference(String s, String t) {
        int res = 0;
        for (char c : t.toCharArray())
            res += c;
        for (char c : s.toCharArray())
            res -= c;
        return (char) res;
    }
	
  	/*
  	 * 位运算 一行
  	 */
    public char findTheDifference1(String s, String t) {
        return (char) (s + t).chars().reduce(0, (a, b) -> a ^ b);
    }
}

解法3:字符和之差

public char findTheDifference(String s, String t) {
  int res = 0;
  for (char c : t.toCharArray())
    res += c;
  for (char c : s.toCharArray())
    res -= c;
  return (char) res;
}

22. 转换成小写字母

题目:709. 转换成小写字母

_解法1:模拟

var toLowerCase = function (s) {
    return [...s].map(e => e >= 'A' && e <= 'Z' ? e.toLowerCase() : e).join('')
};

解法2:位运算

  • 大写变小写、小写变大写:a ^= 32
  • 大写变小写、小写变小写:a |= 32
  • 小写变大写、大写变大写:a &= -33
func toLowerCase(s string) string {
	res := ""
	for _, v := range s {
		if v >= 'A' && v <= 'Z' {
			v |= 32
		}
		res += fmt.Sprintf("%c", v)
	}
	return res
}

23. 解码字母到整数映射

题目:1309. 解码字母到整数映射

提示:

  • 1 <= s.length <= 1000
  • s[i] 只包含数字('0'-'9')和 '#' 字符。
  • s 是映射始终存在的有效字符串。

解法1:模拟

思路有 正向遍历反向遍历

class Solution {
    /**
     * 正向遍历
     */
    public String freqAlphabets(String s) {
        StringBuilder sb = new StringBuilder();
        char[] chars = s.toCharArray();
        for (int i = 0; i < chars.length; i++) {
            if (i + 2 < chars.length && chars[i + 2] == '#') {
                int tmp = (int) (chars[i] - '0') * 10 + (int) (chars[i + 1] - '0');
                sb.append((char) (tmp + 'a' - 1));
                i += 2;
            } else {
                sb.append((char) (chars[i] + 'a' - '1'));
            }
        }
        return sb.toString();
    }

    /**
     * 反向遍历
     */
    public String freqAlphabets1(String s) {
        StringBuilder sb = new StringBuilder();
        for (int i = s.length() - 1; i >= 0; i--) {
            if (s.charAt(i) == '#') {
                int tmp = s.charAt(--i) - '1' + (s.charAt(--i) - '0') * 10;
                sb.append((char) ('a' + tmp));
            } else {
                sb.append((char) ('a' + s.charAt(i) - '1'));
            }
        }
        return sb.reverse().toString();
    }
}

JS 正向遍历:

JS 利用 模板字符串,将两个字符拼到一起很方便

var freqAlphabets = function (s) {
    let res = ""
    for (let i = 0; i < s.length; i++) {
        if (s[i + 2] === '#') {
            let value = +`${s[i]}${s[i + 1]}` + 96
            res += String.fromCharCode(value)
            i += 2
        } else {
            res += String.fromCharCode(+s[i] + 96)
        }
    }
    return res
}

24. 验证外星语词典(todo)

题目:953. 验证外星语词典

提示:

  • 1 <= words.length <= 100
  • 1 <= words[i].length <= 20
  • order.length == 26
  • words[i]order 中的所有字符都是英文小写字母。

_解法1:map + 迭代

其实就是每两个单词,从首字母开始比较 字典序(按照给出的 order 中字母的顺序,越前面的字母相当于字典序越大)

  • 两个字母相同:就比较这两个单词的下一个 字母

  • 两个字母不同:看是不是前面的单词的字母的字典序比较大

    • 是的话,说明这两个单词的顺序没问题,继续比较下两个 单词
    • 不是的话,直接返回 false
  • 以上过程注意处理特殊情况

    比如 ‘apple’ 和 ‘app’,取第4个索引时,JS 就是在比较 'l'undefined,因此提前给 undefined 设置最大字典序就行(其他语言不能这么玩)

function isAlienSorted(words: string[], order: string): boolean {
    let map = new Map()
    // 注:这样映射, 对应的数字越小, 字典序越大
    for (let i = 0; i < order.length; i++)
        map.set(order[i], i)
    // JS 中字符串取超出长度时为 undefined, 设置为最大字典序
    map.set(undefined, -1)
    // 单词之间两两比较
    for (let i = 0; i < words.length - 1; i++) {
        for (let j = 0; j < Math.max(words[i].length, words[i + 1].length); j++) {
            // 相同字母, 跳到下轮比较
            if (words[i][j] == words[i + 1][j]) continue
            // 前面的字母字典序中比较小(数字大), 则返回 false
            else if (map.get(words[i][j]) > map.get(words[i + 1][j]))
                return false;
            // 前面的字母字典序比较大, 进入 下两个单词的比较
            else break
        }
    }
    return true
};

解法2:高阶函数

思路:相当于先翻译,再用默认排序,然后比较排序结果和翻译的结果

function isAlienSorted (words: string[], order: string): boolean {
    // 将每个单词的每个字母,替换成【order 中的索引对应的 ASCII 码】
    //(目的是防止索引较长出现多个字符,ASCII 码只对应一个字符)
    const newWords = words.map(word => {
        return word.split('').map(c => String.fromCharCode(order.indexOf(c))).join('')
    })
    // 浅拷贝后排序
    const sortedNewWords = [...newWords].sort()
    return equalArr(newWords, sortedNewWords);
}
// 比较两个数组的每个元素是否相同
const equalArr = (arrA, arrB) => arrA.every((x, i) => x === arrB[i])

链表 & 树

25. 二进制链表转整数

题目:1290. 二进制链表转整数

提示:

  • 链表不为空。
  • 链表的结点总数不超过 30
  • 每个结点的值不是 0 就是 1

_解法1:哈希

/**
 * @param {ListNode} head
 * @return {number}
 * 哈希
 */
var getDecimalValue = function (head) {
    let res = 0
    let map = new Map(), idx = 0
    while (head) {
        map.set(idx++, head.val)
        head = head.next
    }
    map.forEach((k, v) => res += Math.pow(2, idx - v - 1) * k)
    return res
};

其实不需要用哈希,使用数组即可:

var getDecimalValue = function (head) {
    let res = 0, arr = []
    while (head) {
        arr.push(head.val)
        head = head.next
    }
    arr.forEach((val, i) => res += (2 ** (arr.length - i - 1)) * val)
    return res
};

解法2:位运算

var getDecimalValue = function (head) {
    let res = 0
    while (head) {
        res = (res << 1) + head.val
        head = head.next
    }
    return res
}

26. 链表的中间结点

题目:876. 链表的中间结点

解法:快慢指针。

此题做过。

27. 二叉树的最大深度

题目:104. 二叉树的最大深度

_解法1:递归

var maxDepth = function (root) {
    if (!root) return 0
    return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1
};

解法2:DFS

let maxLevel = 0
var maxDepth = function (root) {
    dfs(root, 1)
    return maxLevel
};

const dfs = (root, level) => {
    if (!root) return
    level > maxLevel && (maxLevel = level)
    dfs(root.left, level + 1)
    dfs(root.right, level + 1)
}

解法3:BFS

var maxDepth = function (root) {
    if (!root) return 0
    let level = 0, queue = [root]
    while (queue.length) {
        level++
        for (let i = queue.length - 1; i >= 0; i--) {
            let node = queue.pop()
            node.left && queue.unshift(node.left)
            node.right && queue.unshift(node.right)
        }
    }
    return level
};

28. 左叶子之和

题目:404. 左叶子之和

提示:

  • 节点数在 [1, 1000] 范围内
  • -1000 <= Node.val <= 1000

_解法1:DFS

var sumOfLeftLeaves = function (root) {
    let sum = 0
    return dfs(root, false, sum)
};

const dfs = (root, isLeft, sum) => {
    if (!root) return 0
    // 是左节点, 且是叶子节点
    if (isLeft && !root.left && !root.right)
        sum += root.val
    return sum + dfs(root.left, true, sum) + dfs(root.right, false, sum)
}

另一种递归写法:

var sumOfLeftLeaves = function (root) {
    if (!root) return 0
    let sum = 0
    // 判断节点是否是左叶子节点, 是则累加其和
    if (root.left && !root.left.left && !root.left.right)
        sum += root.left.val
    return sumOfLeftLeaves(root.left) + sumOfLeftLeaves(root.right) + sum
};

容器 & 库

29. 根据数字二进制下1的数目排序

题目:1356. 根据数字二进制下 1 的数目排序

_解法1:自定义排序

function sortByBits(arr: number[]): number[] {
    arr.sort((a, b) => {
        let aNum = getBinOneNum(a), bNum = getBinOneNum(b)
        return aNum == bNum ? a - b : aNum - bNum
    })
    return arr
};

/**
 * 获取 num 的二进制表示中数字 1 的个数
 */
const getBinOneNum = (num: number) => {
    let count = 0
    while (num) {
        num &= num - 1
        count++
    }
    return count
}

30. 用栈实现队列

题目:232. 用栈实现队列

提示:

  • 1 <= x <= 9
  • 最多调用 100 次 pushpoppeekempty
  • 假设所有操作都是有效的 (例如,一个空的队列不会调用 pop 或者 peek 操作)

进阶:

  • 你能否实现每个操作均摊时间复杂度为 O(1) 的队列?换句话说,执行 n 个操作的总时间复杂度为 O(n) ,即使其中一个操作可能花费较长时间。

_解法1:模拟

// ts
class MyQueue {
    inStack: number[]
    outStack: number[]
    constructor() {
        this.inStack = []
        this.outStack = []
    }

    push(x: number) {
        this.inStack.push(x)
    }

    pop(): number {
        if (!this.outStack.length) {
            while (this.inStack.length) {
                this.outStack.push(this.inStack.pop())
            }
        }
        return this.outStack.pop()
    }

    peek(): number {
        if (!this.outStack.length) {
            while (this.inStack.length) {
                this.outStack.push(this.inStack.pop())
            }
        }
        return this.outStack[this.outStack.length - 1]
    }

    empty(): boolean {
        return !this.inStack.length && !this.outStack.length
    }
}
// java
class MyQueue {
    Stack<Integer> inStack, outStack;

    public MyQueue() {
        inStack = new Stack<>();
        outStack = new Stack<>();
    }

    public void push(int x) {
        inStack.push(x);
        if (outStack.isEmpty()) {
            while (!inStack.isEmpty()) {
                outStack.push(inStack.pop());
            }
        }
    }

    public int pop() {
        if (outStack.isEmpty()) {
            while (!inStack.isEmpty()) {
                outStack.push(inStack.pop());
            }
        }
        return outStack.pop();
    }

    public int peek() {
        if (outStack.isEmpty()) {
            while (!inStack.isEmpty()) {
                outStack.push(inStack.pop());
            }
        }
        return outStack.peek();
    }

    public boolean empty() {
        return inStack.isEmpty() && outStack.isEmpty();
    }
}

31. 有效的字母异同位

题目:242. 有效的字母异位词

_解法1:sort API

function isAnagram(s: string, t: string): boolean {
    return [...s].sort().toString() == [...t].sort().toString()
};

_解法2:哈希 / 数组

function isAnagram(s: string, t: string): boolean {
    let map = new Map<string, number>()
    // 将 s 中的字符与其出现次数做映射
    for (let c of s)
        map.set(c, (map.get(c) ?? 0) + 1)
    // 遍历 t 中的字符, map 中存在其映射则 -1
    for (let c of t) {
        // 剪枝, 如果 t 中找不到 s 中某个字符, 一定不满足条件
        if (!map.has(c)) return false
        map.set(c, map.get(c) - 1)
    }
    // 如果 map 中字符的映射次数还有 > 0 的, 说明不满足条件
    for (let val of map.values())
        if (val > 0) return false
    return true
};

似乎只要范围是 英文字母,基本都可以把哈希优化成数组

function isAnagram(s: string, t: string): boolean {
    let counts = new Array<number>(26).fill(0)
    for (let c of s)
        counts[c.charCodeAt(0) - 'a'.codePointAt(0)]++
    for (let c of t) 
        if (counts[c.charCodeAt(0) - 'a'.codePointAt(0)] == 0) return false
    for (let num of counts)
        if (num != 0) return false
    return true
}

32. 存在重复元素

题目:217. 存在重复元素

_解法1:set

const containsDuplicate = function (nums: number[]): boolean {
    let set = new Set<number>()
    for (let num of nums) {
        if (set.has(num)) return true
        set.add(num)
    }
    return false
};

简化写法:

const containsDuplicate = function (nums: number[]): boolean {
    return new Set(nums).size < nums.length
}

Java 简化写法:

class Solution {
    public boolean containsDuplicate(int[] nums) {
        return Arrays.stream(nums).distinct().count() < nums.length;
    }
}

类 & 对象

33. 设计停车系统

题目:1603. 设计停车系统

_解法1:模拟

class ParkingSystem {
    private int big;
    private int medium;
    private int small;

    public ParkingSystem(int big, int medium, int small) {
        this.big = big;
        this.medium = medium;
        this.small = small;
    }

    public boolean addCar(int carType) {
        if (carType == 1) {
            return big > 0 ? (big-- >= 0) : false;
        } else if (carType == 2) {
            return medium > 0 ? (medium-- >= 0) : false;
        } else if (carType == 3) {
            return small > 0 ? (small-- >= 0) : false;
        } else
            return false; // 非法数据
    }
}

利用数组存储代码更简洁:

class ParkingSystem {
    int[] cars = new int[3];

    public ParkingSystem(int big, int medium, int small) {
        cars[0] = big;
        cars[1] = medium;
        cars[2] = small;
    }

    public boolean addCar(int carType) {
        return cars[carType - 1]-- > 0;
    }
}

34. 区域和检索 - 数组不可变

题目:303. 区域和检索 - 数组不可变

_解法1:模拟

class NumArray {

    private int[] nums;

    public NumArray(int[] nums) {
        this.nums = nums; 
    }
    
    public int sumRange(int left, int right) {
        int sum = 0;
        for (int i = left; i <= right; i++) 
            sum += nums[i];
        return sum;
    }
}

_解法2:前缀和数组

class NumArray {
    
    int[] preSums;
    
    public NumArray(int[] nums) {
        preSums = new int[nums.length + 1]; 
        for (int i = 1; i < preSums.length; i++)
            preSums[i] = preSums[i - 1] + nums[i - 1];
        System.out.println(Arrays.toString(preSums));
    }
    
    public int sumRange(int left, int right) {
        return preSums[right + 1] - preSums[left];
    }
}
// nums = [1, 2, 3, 4, 5]
// preSums = [0, 1, 3, 6, 10, 15]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

萌宅鹿同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值