【leetcode】数组刷题总结

二分查找

LeetCode704题 二分查找

搜索区间为左闭右闭[left, right]

class Solution(object):
    def search(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: int
        """
        left = 0
        right = len(nums) - 1
        while left <= right:
            middle = (left + right) // 2
            if nums[middle] < target:
                left = middle + 1
            elif nums[middle] > target:
                right = middle - 1
            elif nums[middle] == target:
                return middle
        return -1

LeetCode167题 两数之和II-输入有序数组

二分查找变体-查找目标为一对数

class Solution:
    def twoSum(self, numbers: List[int], target: int) -> List[int]:
        left, right = 0, len(numbers) - 1
        while left < right:
            sum_val = numbers[left] + numbers[right]
            if sum_val == target:
                return [left + 1, right + 1]
            elif sum_val < target:
                left += 1
            else:
                right -= 1

LeetCode35题 搜索插入位置

二分查找变体-查找目标的插入位置

class Solution:
    def searchInsert(self, nums: List[int], target: int) -> int:
        left = 0
        right = len(nums) - 1
        while left <= right:
            mid = left + (right - left) // 2
            if nums[mid] == target:
                return mid
            elif nums[mid] < target:
                left = mid + 1
            elif nums[mid] > target:
                right = mid - 1
        if left > 0:
            return left
        else:
            return 0

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

查找有序重复数组的左右边界问题,与二分查找基础题相比,主要差异在对nums[mid]==target时的情况处理

class Solution:
    def searchRange(self, nums: List[int], target: int) -> List[int]:
        if len(nums) == 0:
            return [-1, -1]
        return [self.searchBound(nums, target, 'left'), self.searchBound(nums, target, 'right')]
        
    def searchBound(self, nums, target, side):
        left = 0
        right = len(nums) - 1
        while left <= right:
            mid = left + (right - left) // 2
            if nums[mid] < target:
                left = mid + 1
            elif nums[mid] > target:
                right = mid - 1
            else:
                if side == 'left':
                    # 收缩右侧边界
                    right = mid - 1
                else:
                    # 收缩左侧边界
                    left = mid + 1
        if side == 'left':
            return left if 0 <= left < len(nums) and nums[left] == target else -1
        else:
            return right if 0 <= right < len(nums) and nums[right] == target else -1

 LeetCode69题 x的平方根

本质是在找符合r*r<=x的r的右边界问题,所以返回right

class Solution:
    def mySqrt(self, x: int) -> int:
        left = 1
        right = x
        while left <= right:
            mid = left + (right - left) // 2
            r = mid * mid
            if r == x:
                return mid
            elif r < x:
                left = mid + 1
            elif r > x:
                right = mid - 1
        return right

LeetCode875题 爱吃香蕉的珂珂

吃香蕉速度k和耗时h是负相关关系,所以可以用二分查找,查找符合条件的k的左边界

class Solution:
    def minEatingSpeed(self, piles: List[int], h: int) -> int:
        m = max(piles)
        left = 1
        right = m
        while left <= right:
            mid = left + (right - left) // 2
            mid_res = self.cal_hours(piles, mid)
            if mid_res > h:
                left = mid + 1
            else:
                right = mid - 1
        if right < m:
            return left
        else:
            return right

    
    def cal_hours(self, piles, k):
        res = 0
        for i in piles:
            if i % k != 0:
                res += i // k + 1
            else:
                res += i // k
        return res

 LeetCode1011题 在D天内送达包裹的能力

运载能力和耗费天数是负相关关系,所以可以用二分查找,查找符合条件的运载能力的左边界

class Solution:
    def shipWithinDays(self, weights: List[int], days: int) -> int:
        max_val = float('-inf')
        sum_val = 0
        for i in weights:
            max_val = max(max_val, i)
            sum_val += i
        # 最小运载量是max_val,否则有的包裹永远无法被装载
        # 最大运载量时sum_val,对应days=1情况
        left = max_val
        right = sum_val
        while left <= right:
            mid = left + (right - left) // 2
            mid_res = self.cal_days(weights, mid)
            if mid_res > days:
                left = mid + 1
            else:
                right = mid - 1
        if right < sum_val:
            return left
        else:
            return right


    def cal_days(self, weights, carrying):
        res = 0
        cur = carrying
        for i in weights:
            # 剩余运载能力装不下时,天数加一
            if cur < i:
                res += 1
                cur = carrying
            cur -= i
        return res + 1

 LeetCode410题 分割数组的最大值

这道题在问分割数组和的最大值的最小值,可以用二分查找搜索这个最小值,也就是二分查找左边界问题(如果要直接求解使得数组和的最大值最小的分割数组方案,复杂度会高一些)

class Solution:
    def splitArray(self, nums: List[int], k: int) -> int:
        max_val = float('-inf')
        sum_val = 0
        for i in nums:
            max_val = max(max_val, i)
            sum_val += i
        left = max_val
        right = sum_val
        while left <= right:
            mid = left + (right - left) // 2
            is_valid = self.valid(nums, k, mid)
            if not is_valid:
                left = mid + 1
            else:
                # 向左侧压缩
                right = mid - 1
        return left if right < sum_val else right

    def valid(self, nums, k, x):
        # 判断是否满足最大分割子数组和不超过x
        total = 0
        cnt = 1
        for num in nums:
            if total + num > x:
                cnt += 1
                total = num
            else:
                total += num
        return cnt <= k

 LeetCode240题 搜索二维矩阵II

借助递归函数进行搜索,每次搜索排除1/3的搜索区域

class Solution:
    def searchMatrix(self, matrix: List[List[int]], target: int) -> bool:
        self.res = False
        m, n = len(matrix), len(matrix[0])
        self.search(matrix, target, 0, m - 1, 0, n - 1)
        return self.res

    def search(self, matrix, target, x1, x2, y1, y2):
        if self.res or x1 > x2 or y1 > y2:
            return
        x_mid = x1 + (x2 - x1) // 2
        y_mid = y1 + (y2 - y1) // 2
        if matrix[x_mid][y_mid] == target:
            self.res = True
        elif matrix[x_mid][y_mid] > target:
            self.search(matrix, target, x1, x_mid - 1, y1, y2) # 上方
            self.search(matrix, target, x_mid, x2, y1, y_mid - 1) # 左下方
        elif matrix[x_mid][y_mid] < target:
            self.search(matrix, target, x_mid + 1, x2, y1, y2) # 下方
            self.search(matrix, target, x1, x_mid, y_mid + 1, y2) # 右上方

双指针的应用

LeetCode26题 删除有序数组中的重复项

快慢指针:让慢指针走在后面,快指针走在前面探路,找到一个不重复的元素就赋值给慢指针,并让慢指针前进一步,这样就保证了慢指针之前的元素都是无重复的

class Solution:
    def removeDuplicates(self, nums: List[int]) -> int:
        slow = 1
        cur_num = nums[0]
        for fast in range(1, len(nums)):
            if nums[fast] != cur_num:
                cur_num = nums[fast]
                nums[slow] = nums[fast]
                slow += 1
        return slow

LeetCode27题 移除元素

思路同上题,快慢指针

class Solution:
    def removeElement(self, nums: List[int], val: int) -> int:
        slow = 0
        for fast in range(len(nums)):
            if nums[fast] != val:
                nums[slow] = nums[fast]
                slow += 1
        return slow

 LeetCode283题 移动零

思路同上面两个题,快慢指针,但本题需要保证数组中元素和原有数组一致,只是改变顺序,所以需要对快慢指针元素进行交换

class Solution:
    def moveZeroes(self, nums: List[int]) -> None:
        """
        Do not return anything, modify nums in-place instead.
        """
        slow = 0
        for fast in range(len(nums)):
            if nums[fast] != 0:
                nums[slow], nums[fast] = nums[fast], nums[slow]
                slow += 1

LeetCode870题 优势洗牌

贪心思路:如果nums1的最大值比不过nums2的最大值,那其他元素肯定是白给了,显然这种情况应该用nums1的最小值去送人头,降低损失保存实力。如果nums1的最大值大于nums2的最大值,那就硬刚好了,反正这把nums1可以赢

所以我们可以对两个数组排序,然后用left指针代表nums1现存最大值,right指针代表nums1现存最小值,left比得过nums2[i]就硬刚,比不过就用right送人头

class Solution:
    def advantageCount(self, nums1: List[int], nums2: List[int]) -> List[int]:
        res = [0] * len(nums1)
        left = 0
        right = len(nums1) - 1
        nums1.sort(reverse=True)
        nums2 = [(i, nums2[i]) for i in range(len(nums2))]
        nums2.sort(key=lambda x: x[1], reverse=True)
        for i in range(len(nums1)):
            if nums1[left] > nums2[i][1]:
                res[nums2[i][0]] = nums1[left]
                left += 1
            else:
                res[nums2[i][0]] = nums1[right]
                right -= 1
        return res

滑动窗口

LeetCode209题 长度最小的子数组

双指针(滑动窗口),不断的调节子序列的起始位置和终止位置,从而得出想要的结果

  • 窗口就是满足和>=target的连续子数组
  • 窗口的起始位置如何移动:如果当前窗口的值>=target,窗口就要向前移动了
  • 窗口的结束位置如何移动:窗口的结束位置就是遍历数组的指针
class Solution(object):
    def minSubArrayLen(self, target, nums):
        slow = 0
        fast = 0
        cum_sum = 0
        res = float('inf')
        while fast < len(nums):
            cum_sum += nums[fast]
            while cum_sum >= target:
                res = min(res, fast - slow + 1)
                cum_sum -= nums[slow]
                slow += 1
            fast += 1
        if res != float('inf'):
            return res
        else:
            return 0

LeetCode904题 水果成篮

class Solution:
    def totalFruit(self, fruits: List[int]) -> int:
        res = float('-inf')
        visited = [-1, -1]
        left = 0
        right = 0
        while right < len(fruits):
            if fruits[right] not in visited and visited[0] >= 0:
                for i in range(right - 1, left - 1, -1):
                    if fruits[i] == visited[0]:
                        left = i + 1
                        break
            res = max(res, right - left + 1)
            if fruits[right] != visited[1]:
                visited[0], visited[1] = visited[1], fruits[right]
            right += 1
        return res

前缀和数组

前缀和主要适用的场景是原始数组不会被修改的情况下,频繁查询某个区间的累加和

LeetCode303题 区域和检索-数组不可变

前缀和数组应用,前缀和数组索引i存储的是nums[:i]的和,可以使区域和检索复杂降为O(1)

lass NumArray:

    def __init__(self, nums: List[int]):
        # 前缀和数组,索引i存储的是nums[:i]的和
        self.pre_sum_list = nums
        total = 0
        for i in range(len(nums)):
            total += nums[i]
            self.pre_sum_list[i] = total

    def sumRange(self, left: int, right: int) -> int:
        if left == 0:
            return self.pre_sum_list[right]
        else:
            return self.pre_sum_list[right] - self.pre_sum_list[left - 1]

LeetCode304题 二维区域和检索-矩阵不可变 

class NumMatrix:

    def __init__(self, matrix: List[List[int]]):
        m, n = len(matrix), len(matrix[0])
        self.pre_sum_matrix = [[0] * n for _ in range(m)]
        # 初始化
        row_sum = col_sum = 0
        for i in range(n):
            row_sum += matrix[0][i]
            self.pre_sum_matrix[0][i] = row_sum
        for i in range(m):
            col_sum += matrix[i][0]
            self.pre_sum_matrix[i][0] = col_sum
        # 通过dp数组方式得到二维前缀和数组
        for i in range(1, m):
            for j in range(1, n):
                self.pre_sum_matrix[i][j] = self.pre_sum_matrix[i - 1][j] + self.pre_sum_matrix[i][j - 1] + \
                                            matrix[i][j] - self.pre_sum_matrix[i - 1][j - 1]


    def sumRegion(self, row1: int, col1: int, row2: int, col2: int) -> int:
        if row1 == 0 and col1 == 0:
            return self.pre_sum_matrix[row2][col2]
        elif row1 == 0:
            return self.pre_sum_matrix[row2][col2] - self.pre_sum_matrix[row2][col1 - 1]
        elif col1 == 0:
            return self.pre_sum_matrix[row2][col2] - self.pre_sum_matrix[row1 - 1][col2]
        else:
            return self.pre_sum_matrix[row2][col2] + self.pre_sum_matrix[row1 - 1][col1 - 1] - \
                   self.pre_sum_matrix[row1 - 1][col2] - self.pre_sum_matrix[row2][col1 - 1]

差分数组

差分数组的主要适用场景是频繁对原始数组某个区间的元素进行增减

定义差分数组为diff,diff[i] = nums[i] - nums[i-1](diff[0]=nums[0]),这样就可以快速进行区间增减的操作,如果你想对区间i~j的元素全部加3,那么只需要让diff[i] + 3,然后再让diff[j+1] - 3即可。最后根据diff再进行数组还原

LeetCode1094题 拼车

class Solution:
    def carPooling(self, trips: List[List[int]], capacity: int) -> bool:
        # 定义差分数组
        dif_list = [capacity] + [0 for i in range(1000)]
        # 差分数组更新
        for i in trips:
            dif_list[i[1]] -= i[0]
            dif_list[i[2]] += i[0]
        # 数组还原,判断是否超载
        for i in range(1001):
            if i > 0:
                dif_list[i] = dif_list[i - 1] + dif_list[i]
            if dif_list[i] < 0:
                return False
        return True

带权重的随机选择算法

LeetCode528题 按权重随机选择

前缀和数组 + 二分查找

class Solution:

    def __init__(self, w: List[int]):
        self.total = w[0]
        # 前缀和数组
        self.pre_sum_list = [0] * len(w)
        self.pre_sum_list[0] = w[0]
        for i in range(1, len(w)):
            self.pre_sum_list[i] = self.pre_sum_list[i - 1] + w[i]
            self.total += w[i]

    def pickIndex(self) -> int:
        import random
        rand_num = random.randint(1, self.total)
        # 二分查找,查找满足rand_num <= pre_sum_list[i]的左侧边界索引
        left = 0
        right = len(self.pre_sum_list) - 1
        while left <= right:
            mid = left + (right - left) // 2
            if self.pre_sum_list[mid] < rand_num:
                left = mid + 1
            else:
                right = mid - 1
        if right == len(self.pre_sum_list) - 1:
            return right
        else:
            return left

数组花式遍历

LeetCode48题 旋转图像

先按左上右下转置矩阵,然后交换每一行元素位置即可

class Solution:
    def rotate(self, matrix: List[List[int]]) -> None:
        """
        Do not return anything, modify matrix in-place instead.
        """
        n = len(matrix[0])        
        # 按左上右下转置矩阵
        for i in range(n):
            for j in range(i, n):
                matrix[j][i], matrix[i][j] = matrix[i][j], matrix[j][i] 
        # 交换每一行元素位置
        for i in range(n):
            matrix[i].reverse()

LeetCode54题 螺旋矩阵

在循环中,保持左闭右开逻辑(循环不变量)

class Solution:
    def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
        res = []
        m, n = len(matrix), len(matrix[0])
        min_val = min(m, n)
        num = min_val // 2 + min_val % 2
        for i in range(num):
            if i < n - i - 1 and i < m - i - 1:
                for j in range(i, n - i - 1):
                    res.append(matrix[i][j])
                for j in range(i, m - i - 1):
                    res.append(matrix[j][n - i - 1])
                for j in range(n - i - 1, i, -1):
                    res.append(matrix[m - i - 1][j])
                for j in range(m - i - 1, i, -1):
                    res.append(matrix[j][i])
            elif i == n - i - 1 and i == m - i - 1:
                res.append(matrix[i][i])
            elif i < n - i - 1:
                res.extend(matrix[i][i: n - i])
            elif i < m - i - 1:
                for j in range(i, m - i):
                    res.append(matrix[j][i])
        return res
  • 17
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
LeetCode 是一个在线编程学习平台,提供大量编程题目供用户练习,特别适合准备技术面试的开发者。在 Java 方面,LeetCode 提供了各种难度的练习题,从基础的数组、字符串操作,到复杂的图和动态规划问题。下面是一份简单的 Java 刷题指南: 1. **熟悉 Java 基础**:在开始刷题之前,确保你对 Java 语言有扎实的理解,包括数据类型、数组、集合、异常处理、流、lambda 表达式等。 2. **理解题目要求**:仔细阅读题目描述,理解输入输出格式和限制条件,如时间复杂度和空间复杂度的限制。 3. **编写测试用例**:在编写代码之前,先编写一些测试用例来验证你的想法是否正确。LeetCode 平台本身提供了测试用例,但自己编写测试用例有助于加深对问题的理解。 4. **编写代码**:开始编写代码时,尽量使代码简洁易懂。注意代码风格和命名规范,以便于他人(或未来的你)阅读和维护。 5. **调试和优化**:编写完代码后,通过 LeetCode 提供的测试用例进行调试。如果失败,检查逻辑错误并优化代码。注意时间复杂度和空间复杂度,尝试找到更优解。 6. **学习算法和数据结构**:很多 LeetCode 题目都需要使用特定的算法和数据结构。通过刷题,你可以学习和巩固这些知识,例如排序算法、树、图、动态规划等。 7. **参与讨论和查看解法**:如果在解决问题时遇到困难,可以查看 LeetCode 上其他人的解法和讨论,这有助于开阔思路,学习新的解题技巧。 8. **定期回顾**:定期回顾已经解决的问题,以加深记忆和巩固知识。 9. **参加 LeetCode 比赛**:参加 LeetCode 定期举办的线上编程竞赛,可以锻炼你在压力下的编程能力和解题速度。 10. **制定计划和目标**:为自己制定一个学习计划,设定短期和长期目标。例如,每天解决一题或者每周掌握一种新的算法
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值