【leetcode刷题之路】面试经典hot100(2)——普通数组+矩阵+链表

5 普通数组

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

题目链接:https://leetcode.cn/problems/maximum-subarray/description/?envType=study-plan-v2&envId=top-100-liked

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

  • 如果 d p [ i − 1 ] dp[i-1] dp[i1]大于0,则加上当前的数字作为 d p [ i ] dp[i] dp[i]的子数和

  • 如果 d p [ i − 1 ] dp[i-1] dp[i1]小于0,则停止计算子数和,从第 i i i个开始重新计算,赋值为0,并加上当前的数字作为 d p [ i ] dp[i] dp[i]的子数和

最后的最大子数组和就是这个数组中最大的那个

class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        ans = -10001
        cur = 0

        for n in nums:
            cur += n
            ans = max(cur,ans)
            if cur <= 0:
                cur = 0
        return ans
5.2 【排序】合并区间

题目链接:https://leetcode.cn/problems/merge-intervals/description/?envType=study-plan-v2&envId=top-100-liked

首先对区间进行排序,然后遍历并合并重叠区间,最终返回合并后的非重叠区间列表。

class Solution:
    def merge(self, intervals: List[List[int]]) -> List[List[int]]:
        intervals.sort()
        l,r = intervals[0][0],intervals[0][1]
        ans = []

        for i in range(1,len(intervals)):
            if intervals[i][0] <= r:
                r = max(intervals[i][1],r)
            else:
                ans.append([l,r])
                l,r = intervals[i][0],intervals[i][1]
        if [l,r] not in ans:
            ans.append([l,r])
        return ans
5.3 【数组】轮转数组

题目链接:https://leetcode.cn/problems/rotate-array/description/?envType=study-plan-v2&envId=top-100-liked

  • 翻转整个数组。
  • 翻转前 k 个元素,将它们移到数组的开始。
  • 翻转剩余的元素,将它们移到正确的位置。
class Solution:
    def rotate(self, nums: List[int], k: int) -> None:
        def reverse(i,j):
            while i < j:
                nums[i],nums[j] = nums[j],nums[i]
                i += 1
                j -= 1
        l = len(nums)
        if k!= 0:
            k %= l
            reverse(0,l-1)
            reverse(0,k-1)
            reverse(k,l-1)
5.4 【前缀和】除自身以外数组的乘积

题目链接:https://leetcode.cn/problems/product-of-array-except-self/description/?envType=study-plan-v2&envId=top-100-liked

分别计算索引左边的数字乘积和索引右边的数字乘积,两者乘积结果即为该索引的乘积结果。

# 方法一
class Solution:
    def productExceptSelf(self, nums: List[int]) -> List[int]:
        length = len(nums)
        left_ans, right_ans, answer = [0]*length, [0]*length, [0]*length

        left_ans[0] = 1
        for i in range(1,length):
            left_ans[i] = left_ans[i-1]*nums[i-1]
        
        right_ans[length-1] = 1
        for i in range(length-2,-1,-1):
            right_ans[i] = right_ans[i+1]*nums[i+1]
        
        for i in range(length):
            answer[i] = left_ans[i]*right_ans[i]
        
        return answer
# 方法二——不需要额外空间
class Solution:
    def productExceptSelf(self, nums: List[int]) -> List[int]:
        n = len(nums)
        suf = [1] * n
        for i in range(n - 2, -1, -1):
            suf[i] = suf[i + 1] * nums[i + 1]

        pre = 1
        for i, x in enumerate(nums):
            suf[i] *= pre
            pre *= x

        return suf
5.5 【哈希表】缺失的第一个正数

题目链接:https://leetcode.cn/problems/first-missing-positive/?envType=study-plan-v2&envId=top-100-liked

这个问题可以通过将数组中的每个数字放到它应该在的位置来解决,即 nums[i] 应该放在 nums[i-1] 的位置,相当于把原来的数组当作一个哈希表来使用,具体步骤如下:

  • 初始化:设置 max_len 为数组长度加1,这是我们需要处理的最大正整数。

  • 标记无效数字:将所有非正数或大于 n 的数字设置为0。

  • 放置数字:

    • 对于每个数字 nums[i],如果它在范围内(1 到 max_len - 1),则将其放到正确的位置 nums[nums[i] % max_len - 1],并加上数组的最大长度,标记这个位置的数已经出现过了。
    • 使用一个循环来重复这个过程,直到所有在范围内的数字都被放到了正确的位置。
  • 查找缺失的最小正整数:

    • 遍历数组,如果 nums[i] 小于 max_len,则 i+1 就是缺失的最小正整数。
    • 如果所有位置都填满了,那么缺失的最小正整数就是 max_len。
class Solution:
    def firstMissingPositive(self, nums: List[int]) -> int:
        n = len(nums)
        max_len = n+1

        for i in range(n):
            if nums[i] <= 0 or nums[i] >= max_len:
                nums[i] = 0
        
        for i in range(n):
            if nums[i] % max_len != 0:
                cur = (nums[i] % max_len) - 1
                nums[cur] = (nums[cur] % max_len) + max_len
        
        for i in range(n):
            if nums[i] < max_len:
                return i+1
        return max_len

6 矩阵

6.1 【哈希表】矩阵置零

题目链接:https://leetcode.cn/problems/set-matrix-zeroes/description/?envType=study-plan-v2&envId=top-100-liked

用第一行和第一列的元素来表示每一行或者每一列是否存在零。

class Solution:
    def setZeroes(self, matrix: List[List[int]]) -> None:
        row = len(matrix)
        col = len(matrix[0])
        zero_first = [False,False]
        # 第一行是否有零
        for i in range(col):
            if matrix[0][i] == 0:
                zero_first[0] = True
                break
        # 第一列是否有零
        for i in range(row):
            if matrix[i][0] == 0:
                zero_first[1] = True
                break
        # 记录其他行和列的零
        for i in range(1,row):
            for j in range(1,col):
                if matrix[i][j] == 0:
                    matrix[i][0] = matrix[0][j] = 0
        # 矩阵置零
        for i in range(1,row):
            for j in range(1,col):
                if matrix[i][0] == 0 or matrix[0][j] == 0:
                    matrix[i][j] = 0
        if zero_first[0] == True:
            for i in range(col):
                matrix[0][i] = 0
        if zero_first[1] == True:
            for i in range(row):
                matrix[i][0] = 0
6.2 【模拟】螺旋矩阵

题目链接:https://leetcode.cn/problems/spiral-matrix/description/?envType=study-plan-v2&envId=top-100-liked

按照题目的意思,从左往右,从上往下,从右往左,从下往上,依次遍历矩阵元素,每次遍历完判断是否越界。

class Solution:
    def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
        l,r,t,b,res = 0,len(matrix[0])-1,0,len(matrix)-1,[]
        while True:
            # left to right
            for i in range(l,r+1):
                res.append(matrix[t][i])
            t += 1
            if t > b:
                break
            # top to bottom
            for i in range(t,b+1):
                res.append(matrix[i][r])
            r -= 1
            if r < l:
                break
            # right to left
            for i in range(r,l-1,-1):
                res.append(matrix[b][i])
            b -= 1
            if b < t:
                break
            # bottom to top
            for i in range(b,t-1,-1):
                res.append(matrix[i][l])
            l += 1
            if l > r:
                break
        return res
6.3 【模拟】旋转图像

题目链接:https://leetcode.cn/problems/rotate-image/description/?envType=study-plan-v2&envId=top-100-liked

主对角线翻转+左右翻转。

class Solution:
    def rotate(self, matrix: List[List[int]]) -> None:
        length = len(matrix)
        # 主对角线
        for i in range(length):
            for j in range(i+1,length):
                matrix[i][j],matrix[j][i] = matrix[j][i],matrix[i][j]
        # 左右
        for i in range(length):
            for j in range(length//2):
                matrix[i][j],matrix[i][length-j-1] = matrix[i][length-j-1],matrix[i][j]
6.4 【分治】搜索二维矩阵 II

题目链接:https://leetcode.cn/problems/search-a-2d-matrix-ii/description/?envType=study-plan-v2&envId=top-100-liked

从矩阵左下角开始遍历,如果比当前的数小则索引上移,如果比当前的数大则索引右移。

class Solution:
    def searchMatrix(self, matrix: List[List[int]], target: int) -> bool:
        i,j = len(matrix)-1,0
        while i >= 0 and j < len(matrix[0]):
            if matrix[i][j] > target:
                i -= 1
            elif matrix[i][j] < target:
                j += 1
            else:
                return True
        return False

7 链表

7.1 【双指针】相交链表

题目链接:https://leetcode.cn/problems/intersection-of-two-linked-lists/description/?envType=study-plan-v2&envId=top-100-liked

让A和B同时遍历,A遍历完了换到B的开始,B遍历完了换到A的开始,两者相等时就是遇到了公共节点。

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

class Solution:
    def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> Optional[ListNode]:
        A,B = headA,headB
        while A != B:
            A = A.next if A else headB
            B = B.next if B else headA
        return A
7.2 【链表】反转链表

题目链接:https://leetcode.cn/problems/reverse-linked-list/description/?envType=study-plan-v2&envId=top-100-liked

头插法。

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

class Solution:
    def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:
        cur,pre = head,None
        while cur:
            cur.next,pre,cur = pre,cur,cur.next
        return pre
7.3 【双指针】【递归】回文链表

题目链接:https://leetcode.cn/problems/palindrome-linked-list/description/?envType=study-plan-v2&envId=top-100-liked

首先找到链表的中间位置,然后对链表的后半部分进行翻转,最后比较翻转后的部分和前半部分是否一致。

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

class Solution:
    def isPalindrome(self, head: Optional[ListNode]) -> bool:
        if not head:
            return True
        # search for the median node
        fast,slow = head,head
        while fast.next and fast.next.next:
            fast = fast.next.next
            slow = slow.next
        # reverse the right listnode
        cur,pre = slow.next,None
        while cur:
            cur.next,pre,cur = pre,cur,cur.next
        # compare
        while pre:
            if head.val != pre.val:
                return False
            head,pre = head.next,pre.next
        return True
7.4 【双指针】环形链表

题目链接:https://leetcode.cn/problems/linked-list-cycle/description/?envType=study-plan-v2&envId=top-100-liked

定义快慢指针,快指针一次走两步,慢指针一次走一步,如果存在环,则两个指针总会相遇,否则不存在环。

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

class Solution:
    def hasCycle(self, head: Optional[ListNode]) -> bool:
        if not head:
            return False
        fast,slow = head,head
        while fast.next and fast.next.next:
            fast = fast.next.next
            slow = slow.next
            if fast == slow:
                return True
        return False
7.5 【双指针】环形链表 II

题目链接:https://leetcode.cn/problems/linked-list-cycle-ii/description/?envType=study-plan-v2&envId=top-100-liked

这道题在环形链表的基础上做了升级,首先还是用双指针判断链表是否成环,如果成环的话,此时让head和slow同时移动,最后相遇的时候就是环形开始的地方。

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

class Solution:
    def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]:
        if not head:
            return None
        fast,slow = head,head
        while fast and fast.next:
            fast = fast.next.next
            slow = slow.next
            if fast == slow:
                while slow != head:
                    slow = slow.next
                    head = head.next
                return slow
        return None
7.6 【链表】合并两个有序链表

题目链接:https://leetcode.cn/problems/merge-two-sorted-lists/description/?envType=study-plan-v2&envId=top-100-liked

常规做法,挨个比较大小。

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def mergeTwoLists(self, list1: Optional[ListNode], list2: Optional[ListNode]) -> Optional[ListNode]:
        l1p,l2p = list1,list2
        ans = l = ListNode()
        while l1p and l2p:
            if l1p.val <= l2p.val:
                l.next = ListNode(l1p.val)
                l1p = l1p.next
                l = l.next
            else:
                l.next = ListNode(l2p.val)
                l2p = l2p.next
                l = l.next
        while l1p:
            l.next = ListNode(l1p.val)
            l1p = l1p.next
            l = l.next
        while l2p:
            l.next = ListNode(l2p.val)
            l2p = l2p.next
            l = l.next
        return ans.next
7.7 【链表】两数相加

题目链接:https://leetcode.cn/problems/add-two-numbers/description/?envType=study-plan-v2&envId=top-100-liked

挨个相加,保留进位,最后判断是否有多余的进位。

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

class Solution:
    def addTwoNumbers(self, l1: Optional[ListNode], l2: Optional[ListNode]) -> Optional[ListNode]:
        ans = l = ListNode()
        sign = 0

        while l1 or l2:
            l1_num = l1.val if l1 else 0
            l2_num = l2.val if l2 else 0
            num = l1_num + l2_num + sign
            l.next = ListNode(num%10)
            sign = num//10
            l = l.next
            if l1:
                l1 = l1.next
            if l2:
                l2 = l2.next
        if sign:
            l.next = ListNode(sign)
        return ans.next
7.8 【双指针】删除链表的倒数第 N 个结点

题目链接:https://leetcode.cn/problems/remove-nth-node-from-end-of-list/description/?envType=study-plan-v2&envId=top-100-liked

利用双指针left和right,首先让right遍历n个节点,再让两个指针同时遍历,当right遍历到链表结尾时,left所指的就是倒数第n个节点,正常删除即可。

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]:
        left = right = head
        cnt = 0
        while cnt < n:
            right = right.next
            cnt += 1
        
        if not right:
            return head.next
        
        while right.next:
            left = left.next
            right = right.next
        left.next = left.next.next
        return head
7.9 【递归】两两交换链表中的节点

题目链接:https://leetcode.cn/problems/swap-nodes-in-pairs/description/?envType=study-plan-v2&envId=top-100-liked

详见代码。

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def swapPairs(self, head: Optional[ListNode]) -> Optional[ListNode]:
        node0 = dummy = ListNode(next=head)
        node1 = head

        while node1 and node1.next:
            node2 = node1.next
            node3 = node2.next

            node0.next = node2
            node2.next = node1
            node1.next = node3

            node0 = node1
            node1 = node3
        return dummy.next
7.10 【链表】K 个一组翻转链表

题目链接:https://leetcode.cn/problems/reverse-nodes-in-k-group/description/?envType=study-plan-v2&envId=top-100-liked

详见代码。

class Solution:
    def reverseKGroup(self, head: Optional[ListNode], k: int) -> Optional[ListNode]:
        n = 0
        cur = head
        while cur:
            n += 1
            cur = cur.next

        p0 = dummy = ListNode(next=head)
        pre = None
        cur = head

        while n >= k:
            n -= k
            for _ in range(k):
                nxt = cur.next
                cur.next = pre
                pre = cur
                cur = nxt

            nxt = p0.next
            nxt.next = cur
            p0.next = pre
            p0 = nxt
        return dummy.next
7.11 【哈希表】随机链表的复制

题目链接:https://leetcode.cn/problems/copy-list-with-random-pointer/description/?envType=study-plan-v2&envId=top-100-liked

利用哈希表,首先构建链表中的每个结点,再根据哈希表中的结点分别构造nextrandom引用,相当于对原始链表遍历两次。

"""
# Definition for a Node.
class Node:
    def __init__(self, x: int, next: 'Node' = None, random: 'Node' = None):
        self.val = int(x)
        self.next = next
        self.random = random
"""

class Solution:
    def copyRandomList(self, head: 'Optional[Node]') -> 'Optional[Node]':
        dict = {}
        cur = head
        while cur:
            dict[cur] = Node(cur.val)
            cur = cur.next
        cur = head
        while cur:
            dict[cur].next = dict.get(cur.next)
            dict[cur].random = dict.get(cur.random)
            cur = cur.next
        if not head:
            return head
        else:
            return dict[head]
7.12 【排序】排序链表

题目链接:https://leetcode.cn/problems/sort-list/description/?envType=study-plan-v2&envId=top-100-liked

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

# 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
7.13 【分治】合并 K 个升序链表

题目链接:https://leetcode.cn/problems/merge-k-sorted-lists/description/?envType=study-plan-v2&envId=top-100-liked

两两合并。

# 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
7.14 【哈希表】LRU 缓存

题目链接:https://leetcode.cn/problems/lru-cache/description/?envType=study-plan-v2&envId=top-100-liked

详见代码,通过构造双向链表和哈希表来模拟LRU缓存,要注意及时将最久未使用的结点移动到链表末尾,便于删除,这里采取的措施是定义一个函数,将链表中的某个结点移动到链表末尾,在getput方法中都会遇到这种情况。

class ListNode:
    # 双向链表构建
    def __init__(self, key=None, value=None):
        self.key = key
        self.value = value
        self.prev = None
        self.next = None

class LRUCache:
    def __init__(self, capacity: int):
        self.capacity = capacity
        self.hashmap = {}
        # 头结点head和尾结点tail
        self.head = ListNode()
        self.tail = ListNode()
        # 初始化双向链表
        self.head.next = self.tail
        self.tail.prev = self.head

    # 将链表中的某个结点移到链表末尾tail
    def move_to_tail(self, key):
        node = self.hashmap[key]
        # 取出node结点
        node.next.prev = node.prev
        node.prev.next = node.next
        # 移到末尾
        node.next = self.tail
        node.prev = self.tail.prev
        self.tail.prev.next = node
        self.tail.prev = node

    def get(self, key: int) -> int:
        if key in self.hashmap:
            self.move_to_tail(key)
        res = self.hashmap.get(key)
        if res == None:
            return -1
        else:
            return res.value

    def put(self, key: int, value: int) -> None:
        if key in self.hashmap:
            self.hashmap[key].value = value
            self.move_to_tail(key)
        else:
            if len(self.hashmap) == self.capacity:
                # 删除最久未访问结点
                self.hashmap.pop(self.head.next.key)
                self.head.next = self.head.next.next
                self.head.next.prev = self.head
            newNode = ListNode(key, value)
            self.hashmap[key] = newNode
            newNode.prev = self.tail.prev
            newNode.next = self.tail
            self.tail.prev.next = newNode
            self.tail.prev = newNode

# Your LRUCache object will be instantiated and called as such:
# obj = LRUCache(capacity)
# param_1 = obj.get(key)
# obj.put(key,value)
  • 17
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小天才才

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

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

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

打赏作者

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

抵扣说明:

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

余额充值