【leetcode刷题之路】面试经典150题(2)——双指针+滑动窗口+矩阵

文章目录

      • 2 双指针
        • 2.1 【双指针】验证回文串
        • 2.2 【双指针】判断子序列
        • 2.3 【双指针】两数之和 II - 输入有序数组
        • 2.4 【双指针】盛最多水的容器
        • 2.5 【双指针】三数之和
      • 3 滑动窗口
        • 3.1 【双指针】长度最小的子数组
        • 3.2 【滑动窗口】无重复字符的最长子串
        • 3.3 【哈希表】串联所有单词的子串
        • 3.4 【哈希表】最小覆盖子串
      • 4 矩阵
        • 4.1 【哈希表】有效的数独
        • 4.2 【模拟】螺旋矩阵
        • 4.3 【数学】旋转图像
        • 4.4 【哈希】矩阵置零
        • 4.5 【模拟】生命游戏

2 双指针

2.1 【双指针】验证回文串

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

  详见代码。

class Solution:
    def isPalindrome(self, s: str) -> bool:
        newStr = ""
        s = s.lower()
        for c in s:
            if (c >= "a" and c <= "z") or (c >= "0" and c <= "9"):
                newStr += c
        left, right = 0, len(newStr)-1
        while(left<=right):
            if newStr[left] != newStr[right]:
                return False
            left += 1
            right -= 1
        return True
2.2 【双指针】判断子序列

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

  双指针挨个遍历 s s s t t t中的字符,看能否找到相对位置一致的字串。

class Solution:
    def isSubsequence(self, s: str, t: str) -> bool:
        s_len = len(s)
        t_len = len(t)
        s_index = 0
        if s_len == 0:
            return True
        if t_len == 0:
            return False

        for i in range(t_len):
            if s_index == s_len:
                break
            if s[s_index] == t[i]:
                s_index += 1
        if s_index == s_len:
            return True
        else:
            return False
2.3 【双指针】两数之和 II - 输入有序数组

题目地址:https://leetcode.cn/problems/two-sum-ii-input-array-is-sorted/description/?envType=study-plan-v2&envId=top-interview-150

  双指针前后遍历元素,找到合适的值。

class Solution:
    def twoSum(self, numbers: List[int], target: int) -> List[int]:
        left, right = 0, len(numbers)-1
        while(left < right):
            if numbers[left] + numbers[right] > target:
                right -= 1
            elif numbers[left] + numbers[right] < target:
                left += 1
            else:
                break
        return [left+1,right+1]
2.4 【双指针】盛最多水的容器

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

  木桶原理,水最多取决于左右边界更短的那个边界,所以希望最短边界尽可能长。

class Solution:
    def maxArea(self, height: List[int]) -> int:
        left, right = 0, len(height)-1
        max_water = 0
        while left < right:
            tmp_water = min(height[left],height[right])*(right-left)
            max_water = max(max_water,tmp_water)
            if height[left] <= height[right]:
                left += 1
            else:
                right -= 1
        return max_water
2.5 【双指针】三数之和

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

  先排序,然后去重,每次固定一个数,在剩下的范围中用双指针找合为0的三元组。

class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        length = len(nums)
        ans = []

        if length < 3:
            return ans
        
        nums.sort()
        for i in range(length):
            if nums[i] > 0:
                return ans
            if i > 0 and nums[i] == nums[i-1]:
                continue
            left = i + 1
            right = length - 1
            while left < right:
                if nums[i] + nums[left] + nums[right] == 0:
                    ans.append([nums[i],nums[left],nums[right]])
                    while left<right and nums[left] == nums[left+1]:
                        left += 1
                    while left<right and nums[right] == nums[right-1]:
                        right -= 1
                    left += 1
                    right -= 1
                elif nums[i] + nums[left] + nums[right] < 0:
                    left += 1
                else:
                    right -= 1
        return ans

3 滑动窗口

3.1 【双指针】长度最小的子数组

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

  设置滑动窗口比较窗口内的数字之和与 t a r g e t target target,当满足条件时去掉窗口内首元素,将窗口右移,再次比较。

class Solution:
    def minSubArrayLen(self, target: int, nums: List[int]) -> int:
        length = len(nums)
        slow, fast = 0, 0
        sum, ans = 0, length+1
        while fast < length:
            sum += nums[fast]
            while sum >= target:
                ans = min(ans,fast-slow+1)
                sum -= nums[slow]
                slow += 1
            fast += 1
        return 0 if ans==length+1 else ans
3.2 【滑动窗口】无重复字符的最长子串

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

  详见代码。

class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        max_len = 0
        tmp_str = ""
        length = len(s)
        i = 0

        while i < length:
            if s[i] not in tmp_str:
                tmp_str += s[i]
                max_len = max(max_len,len(tmp_str))
                i += 1
            else:
                while s[i] in tmp_str:
                    tmp_str = tmp_str[1:]
        return max_len
3.3 【哈希表】串联所有单词的子串

题目地址:https://leetcode.cn/problems/substring-with-concatenation-of-all-words/description/?envType=study-plan-v2&envId=top-interview-150

  依次比较每个滑动窗口中单词的出现次数与 w o r d s words words是否一致即可。

class Solution:
    def findSubstring(self, s: str, words: List[str]) -> List[int]:
        word_len = len(words[0])
        word_num = len(words)
        window = word_len * word_num
        ans = []

        cnt = {word:0 for word in words}
        word_cnt = cnt.copy()
        for word in words:
            word_cnt[word] += 1
        
        start = 0
        while start < len(s) - window + 1:
            tmp_cnt = cnt.copy()
            for i in range(start, start+window, word_len):
                tmp_word = s[i:i+word_len]
                if tmp_word in tmp_cnt:
                    tmp_cnt[tmp_word] += 1
                else:
                    break
            if tmp_cnt == word_cnt:
                ans.append(start)
            start += 1
        return ans
3.4 【哈希表】最小覆盖子串

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

  • 不断增加使滑动窗口增大,直到窗口包含了t的所有元素;
  • 不断增加使滑动窗口缩小,将不必要的元素排除在外,直到碰到一个必须包含的元记录此时滑动窗口的长度,并保存最小值;
  • 再增加一个位置,这个时候滑动窗口肯定不满足条件了,继续从步骤一开始执行,寻找新的满足条件的滑动窗口。
class Solution:
    def minWindow(self, s: str, t: str) -> str:
        s_len, t_len, needCnt = len(s), len(t), len(t)
        need = collections.defaultdict(int)
        for c in t:
            need[c] += 1
        ans = (0,float('inf'))

        # 增加右边界使滑窗包含t
        i = 0
        for j,c in enumerate(s):
            if need[c] > 0:
                needCnt -= 1
            need[c] -= 1
            # 收缩左边界直到无法再去掉元素
            if needCnt == 0:
                while True:
                    ch = s[i]
                    if need[ch] == 0:
                        break
                    else:
                        need[ch] += 1
                        i += 1
                if j-i < ans[1]-ans[0]:
                    ans = (i,j+1)
                # i多增加一个位置,准备开始下一次循环
                need[s[i]] += 1
                needCnt += 1
                i += 1
        return ""if ans[1]>s_len else s[ans[0]:ans[1]]

4 矩阵

4.1 【哈希表】有效的数独

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

  将数组分别按照行、列、块构造哈希表,判断是否有重复元素。

class Solution:
    def isValidSudoku(self, board: List[List[str]]) -> bool:

        row = [[0] * 9 for _ in range(9)]
        col = [[0] * 9 for _ in range(9)]
        block = [[0] * 9 for _ in range(9)]

        for i in range(9):
            for j in range(9):
                if board[i][j] != '.':
                    num = int(board[i][j]) - 1
                    b = (i // 3) * 3 + j // 3
                    if row[i][num] or col[j][num] or block[b][num]:
                        return False
                    row[i][num] = col[j][num] = block[b][num] = 1
        return True
4.2 【模拟】螺旋矩阵

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

  设置上下左右边界,按照从左到右、从上到下、从右到左、从下到上的顺序挨个遍历即可。

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
4.3 【数学】旋转图像

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

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

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]
4.4 【哈希】矩阵置零

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

  首先判断第一行和第一列是否存在零,然后分别用第一行和第一列的空间表示该行或者该列是否存在零,最后统一置零即可。

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
4.5 【模拟】生命游戏

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

  卷积运算,计算每个细胞的得分。

import numpy as np

class Solution:
    def gameOfLife(self, board: List[List[int]]) -> None:
        row,col = len(board),len(board[0])
        # zero padding
        board_tmp = np.array([[0 for _ in range(col+2)] for _ in range(row+2)])
        board_tmp[1:row+1,1:col+1] = np.array(board)
        # kernel
        kernel = np.array([[1,1,1],[1,0,1],[1,1,1]])
        # conv
        for i in range(1,row+1):
            for j in range(1,col+1):
                tmp = np.sum(kernel * board_tmp[i-1:i+2,j-1:j+2])
                if board_tmp[i][j] == 1:
                    if tmp < 2 or tmp > 3:
                        board[i-1][j-1] = 0
                else:
                    if tmp == 3:
                        board[i-1][j-1] = 1

你可能感兴趣的:(#,力扣,leetcode,面试,矩阵,算法)