【leetcode刷题之路】面试经典150题(7)——分治+Kadane 算法+二分查找+堆

16 分治

16.1 【分治】将有序数组转换为二叉搜索树

题目地址:https://leetcode.cn/problems/convert-sorted-array-to-binary-search-tree/description/?envType=study-plan-v2&envId=top-interview-150

  详见代码。

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right

class Solution:
    def sortedArrayToBST(self, nums: List[int]) -> Optional[TreeNode]:
        def divide(nums,left,right):
            if left > right:
                return None
            else:
                m = (left+right)//2
                root = TreeNode(nums[m])
                root.left = divide(nums,left,m-1)
                root.right = divide(nums,m+1,right)
                return root
        return divide(nums,0,len(nums)-1)
16.2 【归并排序】排序链表

题目地址:https://leetcode.cn/problems/sort-list/description/?envType=study-plan-v2&envId=top-interview-150

  首先找到链表的中间位置,然后使用归并排序以此递归遍历链表。

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next

class Solution:
    def sortList(self, head: Optional[ListNode]) -> Optional[ListNode]:
        if not head or not head.next:
            return head
        slow,fast = head,head.next
        while fast and fast.next:
            slow,fast = slow.next,fast.next.next
        mid = slow.next
        slow.next = None
        left,right = self.sortList(head),self.sortList(mid)
        h = ans = ListNode()
        while left and right:
            if left.val < right.val:
                h.next = left
                left = left.next
            else:
                h.next = right
                right = right.next
            h = h.next
        h.next = left if left else right
        return ans.next
16.3 【分治】建立四叉树

题目地址:https://leetcode.cn/problems/construct-quad-tree/description/?envType=study-plan-v2&envId=top-interview-150

  首先需要判断每个方块中的数字是否是相同的,其次要找到递归的条件,这里的条件就是每个方块的四个组成部分,然后以此向内遍历,直到出现叶子结点就算结束。

"""
# Definition for a QuadTree node.
class Node:
    def __init__(self, val, isLeaf, topLeft, topRight, bottomLeft, bottomRight):
        self.val = val
        self.isLeaf = isLeaf
        self.topLeft = topLeft
        self.topRight = topRight
        self.bottomLeft = bottomLeft
        self.bottomRight = bottomRight
"""

class Solution:
    def construct(self, grid: List[List[int]]) -> 'Node':
        # if current grid is leaf or not
        def is_grid(grid):
            l = len(grid)
            ssum = 0
            for i in range(l):
                ssum += sum(grid[i])
            if ssum == l*l:
                return True
            elif ssum == 0:
                return False
            else:
                return None
        
        grid_flag = is_grid(grid)
        l = len(grid)
        if grid_flag == True:
            node = Node(True,True,None,None,None,None)
        elif grid_flag == False:
            node = Node(False,True,None,None,None,None)
        else:
            m = l // 2
            topleft_grid = [[grid[i][j] for j in range(m)] for i in range(m)]
            topright_grid = [[grid[i][j] for j in range(m,l)] for i in range(m)]
            bottomleft_grid = [[grid[i][j] for j in range(m)] for i in range(m,l)]
            bottomright_grid = [[grid[i][j] for j in range(m,l)] for i in range(m,l)]
            node = Node(False,False,self.construct(topleft_grid),self.construct(topright_grid),
                        self.construct(bottomleft_grid),self.construct(bottomright_grid))
        return node
16.4 【暴力】合并 K 个升序链表

题目地址:https://leetcode.cn/problems/merge-k-sorted-lists/description/?envType=study-plan-v2&envId=top-interview-150

  将链表两两合并。

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next

class Solution:
    def mergeKLists(self, lists: List[Optional[ListNode]]) -> Optional[ListNode]:
        def merge(l1,l2):
            cur = tmp = ListNode()
            while l1 and l2:
                if l1.val < l2.val:
                    tmp.next = l1
                    l1 = l1.next
                else:
                    tmp.next = l2
                    l2 = l2.next
                tmp = tmp.next
            if l1:
                tmp.next = l1
            if l2:
                tmp.next = l2
            return cur.next
        
        ans = None
        for i in lists:
            ans = merge(ans,i)
        return ans

17 Kadane 算法

17.1 【动态规划】最大子数组和

题目地址:https://leetcode.cn/problems/maximum-subarray/description/?envType=study-plan-v2&envId=top-interview-150

  利用动态规划,首先定义数组 d p [ i ] dp[i] dp[i],表示终点下标为i的序列的最大子数组和,主要考虑以下两种情况:

  • 如果 d p [ i − 1 ] dp[i-1] dp[i1]大于 0 0 0,则继续向前增加下标为i的数值,作为 d p [ i ] dp[i] dp[i]的子数组和;
  • 如果 d p [ i − 1 ] dp[i-1] dp[i1]小于 0 0 0,则从这里开始停止,重新计算子数组和,赋值为 0 0 0后再加入下标为i的数值,作为 d p [ i ] dp[i] dp[i]的子数组和。

  最后的结果就是数组中最大的那个值。

class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        ans,dp = nums[0],nums[0]
        for i in range(1,len(nums)):
            dp = max(dp,0) + nums[i]
            if dp > ans:
                ans = dp
        return ans
17.2 【动态规划】环形子数组的最大和

题目地址:https://leetcode.cn/problems/maximum-sum-circular-subarray/description/?envType=study-plan-v2&envId=top-interview-150

  分为两种情况,如果答案在数组中间,则是最大子数组和,如果答案在数组两边,则是数字的和减去最小子数组和。

class Solution:
    def maxSubarraySumCircular(self, nums: List[int]) -> int:
        ans_max,dp_max = nums[0],nums[0]
        ans_min,dp_min = nums[0],nums[0]
        total = sum(nums)

        for i in range(1,len(nums)):
            dp_max = max(dp_max,0) + nums[i]
            if dp_max > ans_max:
                ans_max = dp_max
            dp_min = min(dp_min,0) + nums[i]
            if dp_min < ans_min:
                ans_min = dp_min
        
        if total - ans_min == 0:
            return ans_max
        else:
            return max(ans_max,total-ans_min)

18 二分查找

18.1 【二分】搜索插入位置

题目地址:https://leetcode.cn/problems/search-insert-position/description/?envType=study-plan-v2&envId=top-interview-150

  二分查找,注意左右边界的取值。

class Solution:
    def searchInsert(self, nums: List[int], target: int) -> int:
        left,right = 0,len(nums)-1
        while left <= right:
            m = (left+right)//2
            if target < nums[m]:
                right = m-1
            elif target > nums[m]:
                left = m+1
            else:
                return m
        return right+1
18.2 【二分】搜索二维矩阵

题目地址:https://leetcode.cn/problems/search-a-2d-matrix/?envType=study-plan-v2&envId=top-interview-150

  双二分查找。

class Solution:
    def searchMatrix(self, matrix: List[List[int]], target: int) -> bool:
        row,col = len(matrix),len(matrix[0])
        row_l,row_r = 0,row-1
        while row_l <= row_r:
            m = (row_l+row_r)//2
            if target < matrix[m][0]:
                row_r = m-1
            elif target > matrix[m][0]:
                row_l = m+1
            elif target == matrix[m][0]:
                return True
        if row_r == 0 and matrix[row_r][0] > target:
            return False
        col_l,col_r = 0,col-1
        while col_l <= col_r:
            m = (col_l+col_r)//2
            if target < matrix[row_r][m]:
                col_r = m-1
            elif target > matrix[row_r][m]:
                col_l = m+1
            elif target == matrix[row_r][m]:
                return True
        return False
18.3 【二分】寻找峰值

题目地址:https://leetcode.cn/problems/find-peak-element/description/?envType=study-plan-v2&envId=top-interview-150

  如果中间位置的值比左边大,那么在该索引的右边一定存在峰值;同理,如果中间位置的值比右边大,那么在该索引的左边一定存在峰值,最后注意中间索引的取值,避免出现循环。

class Solution:
    def findPeakElement(self, nums: List[int]) -> int:
        l,r = 0,len(nums)-1
        while l < r:
            m = (l+r+1)//2
            if nums[m] > nums[m-1]:
                l = m
            else:
                r = m-1
        return l
18.4 【二分】搜索旋转排序数组

题目地址:https://leetcode.cn/problems/search-in-rotated-sorted-array/description/?envType=study-plan-v2&envId=top-interview-150

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

class Solution:
    def search(self, nums: List[int], target: int) -> int:
        left,right = 0,len(nums)-1
        while left <= right:
            m = (left+right+1)//2
            if nums[m] == target:
                return m
            if nums[m] >= nums[left]:
                if nums[left] <= target < nums[m]:
                    right = m-1
                else:
                    left = m+1
            else:
                if nums[m] < target <= nums[right]:
                    left = m+1
                else:
                    right = m-1
        return -1
18.5 【二分】在排序数组中查找元素的第一个和最后一个位置

题目地址:https://leetcode.cn/problems/find-first-and-last-position-of-element-in-sorted-array/description/?envType=study-plan-v2&envId=top-interview-150

  两次二分,第一次找出第一个位置,第二次找到 t a r g e t + 1 target+1 target+1的第一个位置,该位置左边就是 t a r g e t target target最后一个位置。

class Solution:
    def searchRange(self, nums: List[int], target: int) -> List[int]:
        def bin_sort(left,right,tgt):
            while left <= right:
                m = (left+right+1)//2
                if nums[m] < tgt:
                    left = m+1
                else:
                    right = m-1
            return left
        
        first = bin_sort(0,len(nums)-1,target)
        if first == len(nums) or nums[first]!=target:
            return [-1,-1]
        else:
            last = bin_sort(0,len(nums)-1,target+1) - 1
            return [first,last]
18.6 【二分】寻找旋转排序数组中的最小值

题目地址:https://leetcode.cn/problems/find-minimum-in-rotated-sorted-array/description/?envType=study-plan-v2&envId=top-interview-150

  详见代码。

class Solution:
    def findMin(self, nums: List[int]) -> int:
        left,right = 0,len(nums)-1
        while left <= right:
            m = (left+right+1)//2
            if nums[m] < nums[0]:
                right = m-1
            else:
                left = m+1
        if left == len(nums):
            return nums[0]
        else:
            return nums[left]
18.7 【二分】寻找两个正序数组的中位数

题目地址:https://leetcode.cn/problems/median-of-two-sorted-arrays/description/?envType=study-plan-v2&envId=top-interview-150

  如果数组的总长度是奇数,那么中位数就是第 m + n + 1 2 \frac{m+n+1}{2} 2m+n+1小的元素;如果数组的总长度是偶数,那么中位数就是第 m + n + 1 2 \frac{m+n+1}{2} 2m+n+1和第 m + n + 2 2 \frac{m+n+2}{2} 2m+n+2小的元素的平均值。

  函数get_k_min是一个辅助函数,它用于找到两个已排序数组的第 k k k小的数。它通过比较两个数组的第 k / 2 k/2 k/2个元素来实现这个功能。如果数组 1 1 1的第 k / 2 k/2 k/2个元素小于数组 2 2 2的第 k / 2 k/2 k/2个元素,那么数组 1 1 1的前 k / 2 k/2 k/2个元素一定不会是第 k k k小的数,所以可以将它们排除在外。反之亦然。这个过程会一直持续到 k k k等于 1 1 1或者其中一个数组为空。

class Solution:
    def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float:
        def k_min(start1,end1,start2,end2,k):
            cur_nums1 = end1-start1+1
            cur_nums2 = end2-start2+1
            if cur_nums1 == 0:
                return nums2[start2+k-1]
            if cur_nums2 == 0:
                return nums1[start1+k-1]
            if k == 1:
                return min(nums1[start1],nums2[start2])
            m1 = start1 + min(cur_nums1,k//2) - 1
            m2 = start2 + min(cur_nums2,k//2) - 1
            if nums1[m1] <= nums2[m2]:
                return k_min(m1+1,end1,start2,end2,k-(m1-start1+1))
            else:
                return k_min(start1,end1,m2+1,end2,k-(m2-start2+1))
        m,n = len(nums1),len(nums2)
        a,b = (m+n+1)//2,(m+n+2)//2
        x = k_min(0,m-1,0,n-1,a)
        y = k_min(0,m-1,0,n-1,b)
        return (x+y)/2

19 堆

19.1 【二分】数组中的第K个最大元素

题目地址:https://leetcode.cn/problems/kth-largest-element-in-an-array/description/?envType=study-plan-v2&envId=top-interview-150

  详见代码。

#方法一
class Solution:
    def findKthLargest(self, nums: List[int], k: int) -> int:
        def quick_sort(num,k):
            big,equal,small = [],[],[]
            for n in num:
                if n > num[0]:
                    big.append(n)
                elif n < num[0]:
                    small.append(n)
                else:
                    equal.append(n)
            if k <= len(big):
                return quick_sort(big,k)
            elif k > len(equal) + len(big):
                return quick_sort(small,k-len(equal)-len(big))
            else:
                return num[0]
        return quick_sort(nums,k)
#方法二
class Solution:
    def findKthLargest(self, nums: List[int], k: int) -> int:
        nums.sort()
        l = len(nums)
        return nums[l-k]
19.2 【贪心】502. IPO

题目地址:https://leetcode.cn/problems/ipo/description/?envType=study-plan-v2&envId=top-interview-150

  在当前的本金小于等于当前资本的项目中,每次都选择利益最大的那个,但要注意特殊情况,当前的本金已经不能投资任何项目时,直接结束。

class Solution:
    def findMaximizedCapital(self, k: int, w: int, profits: List[int], capital: List[int]) -> int:
        pro_cap = sorted(zip(profits,capital),key = lambda x:x[1])
        idx,l = 0,len(profits)
        cur = []
        while k:
            while idx < l and pro_cap[idx][1] <= w:
                heapq.heappush(cur,-pro_cap[idx][0])
                idx += 1
            if cur:
                w -= heapq.heappop(cur)
            else:
                break
            k -= 1
        return w
19.3 【优先队列】查找和最小的 K 对数字

题目地址:https://leetcode.cn/problems/find-k-pairs-with-smallest-sums/description/?envType=study-plan-v2&envId=top-interview-150

  多路归并思想,每次将三元组(两数组之和,数组1下标idx1,数组2下标idx2)加入到优先队列中,以两个数组中较小长度的为数组1,较大长度的为数组2,每次将优先队列的栈顶出列(当前未被加入到答案的所有点对中的最小值),然后将下一组下标加入优先队列中。

class Solution:
    def kSmallestPairs(self, nums1: List[int], nums2: List[int], k: int) -> List[List[int]]:
        flag = True
        if len(nums1) > len(nums2):
            nums1,nums2 = nums2,nums1
            flag = False
        n1,n2 = len(nums1),len(nums2)
        ans,pq = [],[]
        for i in range(min(n1,k)):
            heapq.heappush(pq,(nums1[i]+nums2[0],i,0))
        while len(ans) < k:
            _,idx1,idx2 = heapq.heappop(pq)
            if flag:
                ans.append([nums1[idx1],nums2[idx2]])
            else:
                ans.append([nums2[idx2],nums1[idx1]])
            if idx2+1 < n2:
                heapq.heappush(pq,(nums1[idx1]+nums2[idx2+1],idx1,idx2+1))
        return ans
(nums2)
        ans,pq = [],[]
        for i in range(min(n1,k)):
            heapq.heappush(pq,(nums1[i]+nums2[0],i,0))
        while len(ans) < k:
            _,idx1,idx2 = heapq.heappop(pq)
            if flag:
                ans.append([nums1[idx1],nums2[idx2]])
            else:
                ans.append([nums2[idx2],nums1[idx1]])
            if idx2+1 < n2:
                heapq.heappush(pq,(nums1[idx1]+nums2[idx2+1],idx1,idx2+1))
        return ans
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小天才才

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

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

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

打赏作者

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

抵扣说明:

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

余额充值