二分查找

算法

🌙 二分查找

二分查找算法模板:

查找一维数组arr 中的 target:

// 普通二分查找
function binarySearch(arr, target) {
  let left = 0, right = arr.length - 1;

  while (left <= right) {
    let mid = Math.floor((left + right) / 2);

    if (arr[mid] === target) {
      return mid
    } else if (arr[mid] < target) {
      left = mid + 1
    } else if (arr[mid] > target) {
      right = mid - 1
    }
  }

  return -1
}

// 搜索左侧边界的二分查找1:左闭右开
function leftBoundBinarySearch1(arr, target) {
  let left = 0, right = arr.length;

  while (left < right) {
    let mid = Math.floor((left + right) / 2);

    if (arr[mid] === target) {
      right = mid;
    } else if (arr[mid] < target) {
      left = mid + 1;
    } else if (arr[mid] > target) {
      right = mid;
    }
  }

  return arr[left] === target ? left : -1
}

// 搜索左侧边界的二分查找2:左闭右闭
function leftBoundBinarySearch2(arr, target) {
  let left = 0, right = arr.length - 1;

  while (left <= right) {
    let mid = Math.floor((left + right) / 2);

    if (arr[mid] === target) {
      right = mid - 1;
    } else if (arr[mid] < target) {
      left = mid + 1;
    } else if (arr[mid] > target) {
      right = mid - 1;
    }
  }

  if (left >= arr.length || arr[left] !== target) {
    return -1;
  }

  return left
}


// 搜索右侧边界的二分查找1:左闭右开
function rightBoundBinarySearch1(arr, target) {
  let left = 0, right = arr.length;

  while (left < right) {
    let mid = Math.floor((left + right) / 2);

    if (arr[mid] === target) {
      left = mid + 1;
    } else if (arr[mid] < target) {
      left = mid + 1;
    } else if (arr[mid] > target) {
      right = mid;
    }
  }
  if (left == 0) return -1;

  return arr[left - 1] === target ? (left - 1) : -1;
}

// 搜索右侧边界的二分查找2:左闭右闭
function rightBoundBinarySearch2(arr, target) {
  let left = 0, right = arr.length - 1;

  while (left <= right) {
    let mid = Math.floor((left + right) / 2);

    if (arr[mid] === target) {
      left = mid + 1;
    } else if (arr[mid] < target) {
      left = mid + 1;
    } else if (arr[mid] > target) {
      right = mid - 1;
    }
  }
  if (right < 0 || arr[right] !== target) return -1;

  return right;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101

🌙 案例

🌙 1.搜索二维矩阵 (opens new window)

给你一个满足下述两条属性的 m x n 整数矩阵:

每行中的整数从左到右按非严格递增顺序排列。
每行的第一个整数大于前一行的最后一个整数。
给你一个整数 target ,如果 target 在矩阵中,返回 true ;否则,返回 false 。



示例 1:


输入:matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,60]], target = 3
输出:true
示例 2:


输入:matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,60]], target = 13
输出:false


提示:

m == matrix.length
n == matrix[i].length
1 <= m, n <= 100
-104 <= matrix[i][j], target <= 104
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
  • 暴力法
function searchMatrix(matrix, target) {
  for (let i = 0; i < matrix.length; i++) {
    if (matrix[i].includes(target)) return true
  }
  
  return false
}

1
2
3
4
5
6
7
8
  • 二分查找解法1

第一次:二分查找找到target所在的行

第二次:二分查找找到target所在的列

/**
 * @param {number[][]} matrix
 * @param {number} target
 * @return {boolean}
 */
function searchMatrix(matrix, target) {
    let m = matrix.length, n = matrix[0].length
    
    // 找到target所在的行
    let left = 0, right = m - 1
    while (left <= right) {
        let mid = Math.floor((left + right) / 2)
        if (matrix[mid][0] === target) {
            return true
        }
        if (matrix[mid][0] < target) {
            left = mid + 1
        }
        if (matrix[mid][0] > target) {
            right = mid - 1
        }
    }
    
    // 判断 是否找到target所在的行
    if(right < 0 || matrix[right][0] > target) {
        return false
    }
    if(matrix[right][0] === target) {
        return true
    }
    
    let row = right
    // 找到target所在的列
    left = 0, right = n - 1
    while (left <= right) {
        let mid = Math.floor((left + right) / 2)
        if (matrix[row][mid] === target) {
            return true
        }
        if (matrix[row][mid] < target) {
            left = mid + 1
        }
        if (matrix[row][mid] > target) {
            right = mid - 1
        }
    }
    
    let col = right
    return matrix[row][col] === target
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
  • 二分查找解法2

将二维数组展开为一维数组,直接进行二分查找

/**
 * @param {number[][]} matrix
 * @param {number} target
 * @return {boolean}
 */
function searchMatrix(matrix, target) {
    let m = matrix.length, n = matrix[0].length
    
    let left = 0, right = m * n - 1
    while (left <= right) {
        let mid = Math.floor((left + right) / 2)
        let cur = matrix[Math.floor(mid / n)][mid % n]
        if (cur === target) {
            return true
        }
        
        if (cur < target) {
            left = mid + 1
        }
        
        if (cur > target) {
            right = mid - 1
        }
    }
    
    return false
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
  • 二叉搜索树思路

将二维数组看做是以右上角为根节点的二叉树。

/**
 * @param {number[][]} matrix
 * @param {number} target
 * @return {boolean}
 */
function searchMatrix(matrix, target) {
    let m = matrix.length, n = matrix[0].length
    
    function isValid(x, y) {
        return x >= 0 && x < m && y >= 0 && y < n
    }
    
    let row = 0, col = n - 1
    
    while(isValid(row, col) && matrix[row][col] !== target) {
        if(matrix[row][col] < target) {
            row++
        } else {
            col--
        }
    }
    
    return isValid(row, col) && matrix[row][col] === target
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

🌙 2.寻找两个正序数组的中位数 (opens new window)

给定两个大小分别为 m 和 n 的正序(从小到大)数组 nums1 和 nums2。请你找出并返回这两个正序数组的 中位数 。

算法的时间复杂度应该为 O(log (m+n)) 。

 

示例 1:

输入:nums1 = [1,3], nums2 = [2]
输出:2.00000
解释:合并数组 = [1,2,3] ,中位数 2
示例 2:

输入:nums1 = [1,2], nums2 = [3,4]
输出:2.50000
解释:合并数组 = [1,2,3,4] ,中位数 (2 + 3) / 2 = 2.5
 

 

提示:

nums1.length == m
nums2.length == n
0 <= m <= 1000
0 <= n <= 1000
1 <= m + n <= 2000
-106 <= nums1[i], nums2[i] <= 106
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
  • 暴力法

先将两个数组合并,然后进行排序,最后取中位数。

function findMedianSortedArrays(nums1: number[], nums2: number[]): number {
  // 合并数组, 找到 中间值 空间复杂度: O(m+n) 时间复杂度: O(m+n)
  let m = nums1.length, n = nums2.length
  let mn = m + n
  let arr = []
  
  let i=0, j=0
  while(i < m && j < n) {
    if(nums1[i] <= nums2[j]) {
      arr.push(nums1[i++])
    } else {
      arr.push(nums2[j++])
    }
  }
  while(i < m) {
    arr.push(nums1[i++])
  }
  while(j < n) {
    arr.push(nums2[j++])
  }
  
  return (arr[~~((mn - 1)/2)] + arr[~~(mn/2)]) / 2
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
  • 优化暴力法

不用排序,只需要找到中位数。

function findMedianSortedArrays(nums1: number[], nums2: number[]): number {
  // 直接去找中间值, 时间复杂度 O(m+n) 空间复杂度 O(1)
  let m = nums1.length, n = nums2.length
  let mn = m+n

  // 分别记录左右中间值
  let left = 0, right = 0

  // 遍历下标
  let i = 0, j = 0
  // 中间下标
  let mid = ~~(mn / 2)
  for(let k=0; k<=mid; k++) {
    // left 记录 right 之前的值, 保证 left 总是 right上一个值,便于计算中位数(偶数个数的时候)
    left = right
    // 下面更新right的值
    if(i < m && (j >=n || nums1[i] < nums2[j])) {
      right = nums1[i++]
    } else {
      right = nums2[j++]
    }
  }
  return mn % 2 === 0 ? (left + right) / 2 : right
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
  • 二分法
function findMedianSortedArrays(nums1: number[], nums2: number[]): number {
  let m = nums1.length, n = nums2.length
  let mn = m+n

  if(mn % 2 === 0) {
    return (findKthNum(nums1, nums2, mn / 2) + findKthNum(nums1, nums2, mn / 2 + 1)) / 2
  } else {
    return findKthNum(nums1, nums2, (mn + 1) / 2)
  }
};

function findKthNum(n1: number[], n2: number[], k: number): number {
  let s1 = 0, e1 = n1.length - 1
  let s2 = 0, e2 = n2.length - 1

  // [s1, ..., mid1, ..., e1]
  // [s2, ..., mid2, ..., e1]
  while(s1 <=e1 && s2 <= e2) {
    let mid1 = ~~((s1 + e1) / 2)
    let mid2 = ~~((s2 + e2) / 2)

    let c1 = mid1 - s1 + 1
    let c2 = mid2 - s2 + 1
    // 将 n1 和 n2 分成两半,每次判断 k 落在哪半部分,缩小范围
    if(n1[mid1] <= n2[mid2]) {
      if(c1 + c2 > k) {
        // nums2 取前半部分
        e2 = mid2 - 1
      } else {
        // nums1 取后半部分,并缩小k值
        k -= (mid1 - s1 + 1)
        s1 = mid1 + 1
      }
    } else {
      if(c1 + c2 > k) {
        // nums1 取前半部分
        e1 = mid1 - 1
      } else {
        // nums2 取后半部分,并缩小k值
        k -= (mid2 - s2 + 1)
        s2 = mid2 + 1
      }
    }
  }

  if(s1 > e1) {
    return n2[s2 + k - 1]
  } else {
    return n1[s1 + k - 1]
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51

🌙 3.两数相除 (opens new window)

给你两个整数,被除数 dividend 和除数 divisor。将两数相除,要求 不使用 乘法、除法和取余运算。

整数除法应该向零截断,也就是截去(truncate)其小数部分。例如,8.345 将被截断为 8 ,-2.7335 将被截断至 -2 。

返回被除数 dividend 除以除数 divisor 得到的 商 。

注意:假设我们的环境只能存储 32 位 有符号整数,其数值范围是 [−231,  231 − 1] 。本题中,如果商 严格大于 231 − 1 ,则返回 231 − 1 ;如果商 严格小于 -231 ,则返回 -231 。

示例 1:

输入: dividend = 10, divisor = 3
输出: 3
解释: 10/3 = 3.33333.. ,向零截断后得到 3 。
示例 2:

输入: dividend = 7, divisor = -3
输出: -2
解释: 7/-3 = -2.33333.. ,向零截断后得到 -2 。
提示:

-231 <= dividend, divisor <= 231 - 1
divisor != 0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
  • 迭代法(会超时)
function divide(dividend: number, divisor: number): number {
  // 迭代法
  if(dividend === 0) {
    return 0
  }
  // 除数不能为0
  if(divisor === 0) {
    return NaN
  }
  if(divisor === 1) {
    return dividend
  }
  // 32位有符号整数,溢出处理
  if(dividend === -2147483648 && divisor === -1) {
    return 2147483647
  }

  let res = 0
  // 判断正数还是负数
  let flag = 1
  if (dividend < 0) {
    flag *= -1
    dividend = -dividend
  }
  if (divisor < 0) {
    flag *= -1
    divisor = -divisor
  }
  while (dividend >= divisor) {
    // 每次减去除数
    dividend -= divisor
    res++
  }
  return flag === 1 ? res : -res
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
  • 倍增乘法: 每次用被除数减去除数的最大的2^x
function divide(dividend: number, divisor: number): number {
  // 处理溢出
  if (dividend === -2147483648 && divisor === -1) {
    return 2147483647
  }

  // 记录符号
  let flag = -1
  if(dividend > 0 && divisor > 0 || dividend < 0 && divisor < 0) {
    flag = 1
  }

  // 全部转为正数
  dividend = Math.abs(dividend)
  divisor = Math.abs(divisor)

  let ans = 0
  // 每次减去除数的 2^x 倍数,以此类推
  while(dividend >= divisor) {
    let temp = divisor
    let i = 1
    while(temp <= dividend - temp) {
      temp += temp
      i += i
    }
    // 被除数减去除数的 2^x 倍数做为新的被除数
    dividend -= temp
    ans += i
  }
  return flag < 0 ? -ans : ans
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
  • 二分法:
function divide(dividend: number, divisor: number): number {
  // 处理溢出
  if (dividend === -2147483648 && divisor === -1) {
    return 2147483647
  }
  if (dividend === 0) return 0;
  if (divisor === 1) return dividend;

  // 记录符号
  let flag = dividend > 0 === divisor > 0

  // 全部转为正数
  dividend = Math.abs(dividend)
  divisor = Math.abs(divisor)

  // 答案一定在区间[0, dividend]之间, 由于是向下取整,我们找左侧边界
  let left = 0, right = dividend
  while (left < right) {
    // 二分查找
    let mid = left + right + 1 >> 1
    if (quickMulti(divisor, mid) <= dividend) {
      left = mid
    } else {
      right = mid - 1
    }
  }

  // 快速乘法 x*y = x + x + ... + x (y次)
  function quickMulti(x: number, y: number) {
    let ans = 0
    while (y > 0) {
      // y & 1 === 1 表示最低位为1
      if ((y & 1) === 1) {
        ans += x
      }
      // x * 2
      x <<= 1
      // y / 2
      y >>= 1
    }
    return ans
  }

  return flag ? left : -left
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
  • 递归:
function divide(dividend: number, divisor: number): number {
  // 32 位的有符号整数
  const MAX_VALUE = 2 ** 31 - 1, MIN_VALUE = -(2 ** 31);
  // 考虑被除数为最小值的情况
  if (dividend === MIN_VALUE) {
    if (divisor === 1) {
      return MIN_VALUE;
    }
    if (divisor === -1) {
      return MAX_VALUE;
    }
  }
  // 考虑除数为最小值的情况
  if (divisor === MIN_VALUE) {
    return dividend === MIN_VALUE ? 1 : 0;
  }
  // 考虑被除数为 0 的情况
  if (dividend === 0) {
    return 0;
  }

  // 全部转为负数,避免溢出
  if (dividend > 0) return -divide(-dividend, divisor);
  if (divisor > 0) return -divide(dividend, -divisor);
  if (dividend > divisor) return 0;

  let res = 1, tmp = divisor;
  while ((dividend - divisor) <= divisor) {
    res += res;
    divisor += divisor;
  }
  return res + divide(dividend - divisor, tmp);
}; 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

🌙 4.搜索旋转排序数组 (opens new window)

整数数组 nums 按升序排列,数组中的值 互不相同 。

在传递给函数之前,nums 在预先未知的某个下标 k(0 <= k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]](下标 从 0 开始 计数)。例如, [0,1,2,4,5,6,7] 在下标 3 处经旋转后可能变为 [4,5,6,7,0,1,2] 。

给你 旋转后 的数组 nums 和一个整数 target ,如果 nums 中存在这个目标值 target ,则返回它的下标,否则返回 -1 。

你必须设计一个时间复杂度为 O(log n) 的算法解决此问题。

示例 1:

输入:nums = [4,5,6,7,0,1,2], target = 0
输出:4
示例 2:

输入:nums = [4,5,6,7,0,1,2], target = 3
输出:-1
示例 3:

输入:nums = [1], target = 0
输出:-1
提示:

1 <= nums.length <= 5000
-104 <= nums[i] <= 104
nums 中的每个值都 独一无二
题目数据保证 nums 在预先未知的某个下标上进行了旋转
-104 <= target <= 104
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
  • 暴力法
function search(nums: number[], target: number): number {
  for(let i=0; i<nums.length; i++) {
    if(nums[i] === target) return i
  }

  return -1
}
1
2
3
4
5
6
7
  • 二分法

    不管怎么进行旋转,数组都会被分为有序的两部分,每次进行二分前比较一下中间索引与左右边界的值,如果nums[m]>=nums[left],则索引左边有序,否则右边有序。

function search(nums: number[], target: number): number {
  let len = nums.length;
  if (len === 0) {
    return -1;
  }
  if (len === 1) {
    return nums[0] === target ? 0 : -1;
  }

  let left = 0, right = len - 1;

  // 二分法
  while (left <= right) {
    let mid = (left + right) >> 1;

    // 找到了,直接返回
    if (nums[mid] === target) {
      return mid;
    }

    // 原数组 1,2,3,4,5,6
    // 旋转后 5, 6, 1,2,3,4
    // 旋转后 4, 5, 6, 1,2,3
    // 旋转后 6, 1,2,3,4,5
    // 由于是升序排序,旋转之后只需要判断 nums[mid] > nums[left], 那么mid左边就是有序的,反之,右边有序
    if(nums[mid] >= nums[left]) {
      if(nums[left] <= target && target < nums[mid]) {
        right = mid - 1;
      } else {
        left = mid + 1;
      }
    } else {
      if(nums[mid] < target && target <= nums[right]) {
        left = mid + 1;
      } else {
        right = mid - 1;
      }
    }
  }
  return -1;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

🌙 5.在排序数组中查找元素的第一个和最后一个位置 (opens new window)

给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。

如果数组中不存在目标值 target,返回 [-1, -1]。

你必须设计并实现时间复杂度为 O(log n) 的算法解决此问题。

 

示例 1:

输入:nums = [5,7,7,8,8,10], target = 8
输出:[3,4]
示例 2:

输入:nums = [5,7,7,8,8,10], target = 6
输出:[-1,-1]
示例 3:

输入:nums = [], target = 0
输出:[-1,-1]
 

提示:

0 <= nums.length <= 105
-109 <= nums[i] <= 109
nums 是一个非递减数组
-109 <= target <= 109
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

非递减排列:指一个数列中的元素从左到右依次不减,或者说不降序排列。也就是说,如果数列中某个元素的值比它前面的元素小,那么它的值至少和前面的元素相等,即数列中不存在逆序对。如果数列中有相邻的元素相等,也认为是非递减排列。简单理解为 升序排列,但存在相等

  • 直接使用 JS api:
function searchRange(nums: number[], target: number): number[] {
    // 使用 js api
    let left = nums.indexOf(target)
    let right = nums.lastIndexOf(target)

    return [left, right]
};
1
2
3
4
5
6
7
  • 经典二分:
function searchRange(nums: number[], target: number): number[] {
    if(nums.length < 1) return [-1, -1]

    // leftBoundBinarySearch 和 rightBoundBinarySearch 见前面的实现
    let left = leftBoundBinarySearch(nums, target)
    let right = rightBoundBinarySearch(nums, target)

    return [left, right]
};
1
2
3
4
5
6
7
8
9

🌙 题目汇总

题目题解难度推荐指数
4. 寻找两个正序数组的中位数 LeetCode 题解链接困难🤩🤩🤩🤩
29. 两数相除LeetCode 题解链接中等🤩🤩🤩
33. 搜索旋转排序数组LeetCode 题解链接中等🤩🤩🤩🤩🤩
34. 在排序数组中查找元素的第一个和最后一个位置LeetCode 题解链接中等🤩🤩🤩🤩🤩
35. 搜索插入位置LeetCode 题解链接简单🤩🤩🤩🤩🤩
74. 搜索二维矩阵LeetCode 题解链接中等🤩🤩🤩🤩
81. 搜索旋转排序数组 IILeetCode 题解链接中等🤩🤩🤩🤩
153. 寻找旋转排序数组中的最小值LeetCode 题解链接中等🤩🤩🤩
154. 寻找旋转排序数组中的最小值 IILeetCode 题解链接困难🤩🤩🤩
162. 寻找峰值LeetCode 题解链接中等🤩🤩🤩🤩🤩
220. 存在重复元素 IIILeetCode 题解链接中等🤩🤩🤩
274. H 指数LeetCode 题解链接中等🤩🤩🤩
275. H 指数 IILeetCode 题解链接中等🤩🤩🤩
278. 第一个错误的版本LeetCode 题解链接简单🤩🤩🤩🤩
352. 将数据流变为多个不相交区间LeetCode 题解链接困难🤩🤩🤩🤩
354. 俄罗斯套娃信封问题LeetCode 题解链接困难🤩🤩🤩
363. 矩形区域不超过 K 的最大数值和LeetCode 题解链接困难🤩🤩🤩
374. 猜数字大小LeetCode 题解链接简单🤩🤩🤩
441. 排列硬币LeetCode 题解链接简单🤩🤩🤩
528. 按权重随机选择LeetCode 题解链接中等🤩🤩🤩🤩
611. 有效三角形的个数LeetCode 题解链接中等🤩🤩🤩🤩
704. 二分查找LeetCode 题解链接简单🤩🤩🤩🤩🤩
778. 水位上升的泳池中游泳LeetCode 题解链接困难🤩🤩🤩
852. 山脉数组的峰顶索引LeetCode 题解链接简单🤩🤩🤩🤩🤩
981. 基于时间的键值存储LeetCode 题解链接中等🤩🤩🤩🤩
1004. 最大连续1的个数 IIILeetCode 题解链接中等🤩🤩🤩
1011. 在 D 天内送达包裹的能力LeetCode 题解链接中等🤩🤩🤩🤩
1208. 尽可能使字符串相等LeetCode 题解链接中等🤩🤩🤩
1337. 矩阵中战斗力最弱的 K 行LeetCode 题解链接简单🤩🤩🤩
1438. 绝对差不超过限制的最长连续子数组LeetCode 题解链接中等🤩🤩🤩
1482. 制作 m 束花所需的最少天数LeetCode 题解链接中等🤩🤩🤩
1707. 与数组中元素的最大异或值LeetCode 题解链接困难🤩🤩🤩
1713. 得到子序列的最少操作次数LeetCode 题解链接困难🤩🤩🤩
1751. 最多可以参加的会议数目 IILeetCode 题解链接困难🤩🤩🤩
1818. 绝对差值和LeetCode 题解链接中等🤩🤩🤩🤩🤩
1838. 最高频元素的频数LeetCode 题解链接中等🤩🤩🤩
1894. 找到需要补充粉笔的学生编号LeetCode 题解链接中等🤩🤩🤩🤩
剑指 Offer 53 - I. 在排序数组中查找数字 ILeetCode 题解链接简单🤩🤩🤩🤩🤩
剑指 Offer II 069. 山峰数组的顶部LeetCode 题解链接简单🤩🤩🤩🤩🤩