秋招 hot 100 刷题记录【6】—— 刷过半数了!

1.实现Tire(前缀树)
class Node:
    def __init__(self):
        self.children = [None] * 26 # 子节点,最多只有26种可能
        self.isEnd = False          # 标记是否是尾节点


class Trie(object):

    def __init__(self):
        self.root = Node() # 初始化树的根节点


    def insert(self, word):
        """
        :type word: str
        :rtype: None
        """
        # 插入一个word的时候 注意是对每一个字母来设计的
        node = self.root # 首先到父节点
        for char in word:
            index = ord(char) - ord("a") # 找到当前节点所对应的索引位置
            if not node.children[index]: # 如果当前位置上没有节点 说明没有改字符 需要创建节点
                node.children[index] = Node()
            node = node.children[index] # 挪到下一个字符 并且进行下一个字符的处理
        node.isEnd = True # 最后一个节点的isEnd置为true,表示一个完整的字符串


    def search(self, word):
        """
        :type word: str
        :rtype: bool
        """
        node = self.searchPrefix(word) # 查找是否存在有以word为前缀的结点
        return node != None and node.isEnd == True # 当返回节点存在 且 标记位置为ture才证明存在word



    def startsWith(self, prefix):
        """
        :type prefix: str
        :rtype: bool
        """
        node = self.searchPrefix(prefix) # 查找是否存在有以word为前缀的结点
        return node != None # 如果返回的结点不为空即可

    def searchPrefix(self, word):
        node = self.root
        for char in word:
            node = node.children[ord(char) - ord("a")]
            if not node: # 如果找不到该节点 说明不存在
                return None
        return node #成功匹配该节点
    




# Your Trie object will be instantiated and called as such:
# obj = Trie()
# obj.insert(word)
# param_2 = obj.search(word)
# param_3 = obj.startsWith(prefix)
2.全排列——回溯 待 刷!!!
class Solution(object):
    def permute(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
       # 注意是全排列 每个数字只能取1次 数字位置与答案数量有关—— used数组
       # 注意这里的回溯 从0开始的原因是 其他的分支可以从头开始取数
        res = []
        path = [] 
        used = [0] * len(nums)
        self.backtracking(nums, path, used, res)
        return res

    def backtracking(self, nums, path, used, res):
        if len(path) == len(nums): # 路径结束的条件
            res.append(path[:])
            return 
        for i in range(len(nums)):
            if used[i] == 1:
                continue # 说明该数字已经使用过
            # 添加该路径
            path.append(nums[i])
            used[i] = 1
            # 回溯
            self.backtracking(nums, path, used, res)
            # 复原
            used[i] = 0
            path.pop()


3.子集
  • 代码链接
  • 组合问题
  • 时间复杂度: O(n * 2^n)
    空间复杂度: O(n)
class Solution(object):
    def subsets(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        # 思路:子集问题本质是组合 (注意组合与排列的区别 排列与顺序有关 和组合是集合 是无序的)
        # 子集是收集树形结构中树的所有节点的结果;而组合问题、分割问题是收集树形结构中叶子节点的结果。
        res = []
        path = []
        self.backtracking(nums, 0, path, res)
        return res
    # startIndex! 因为取过的数字并不会重复取 因为组合与顺序无关 因此需要startIndex进行限制
    def backtracking(self, nums, startIndex, path, res):
        # !收集子集,要放在终止添加的上面,否则会漏掉自己
        res.append(path[:])
        # 回溯终止的条件
        if startIndex >= len(nums):
            return
        for i in range(startIndex, len(nums)):
            path.append(nums[i])
            self.backtracking(nums, i + 1, path, res)
            path.pop()
4.电话号码的字母组合
class Solution(object):
    def __init__(self):
        self.res = []
        self.path = ""
        self.letterMap = [
            "", # 0 index为0所对应的
            "",
            "abc",
            "def",
            "ghi",
            "jkl",
            "mno",
            "pqrs",
            "tuv",
            "wxyz",
        ]
    def trackbacking(self, nums, startIndex):
        #  startIndex 不是指代划分位置 而是指代的是此时是对第几个字母进行处理
        if startIndex == len(nums):
            self.res.append(self.path) # 只有当所有字母都访问结束后,才加入res
            return
        dight = int(nums[startIndex]) # 此时待处理的数字
        print(dight)
        letter = self.letterMap[dight]
        for i in letter:
            self.path += i
            self.trackbacking(nums, startIndex + 1)
            self.path = self.path[:-1] # 回溯返回一个数字

    def letterCombinations(self, digits):
        """
        :type digits: str
        :rtype: List[str]
        """
    # 题目出现“所有组合”词语的时候 想到回溯的方法
        if not digits: return []
        self.trackbacking(digits, 0)
        return self.res
5.组合总和
class Solution(object):
    def combinationSum(self, candidates, target):
        """
        :type candidates: List[int]
        :type target: int
        :rtype: List[List[int]]
        """
        # 组合问题——回溯
        res = []
        path = []
        self.trackbacking(candidates, target, 0, path, res)
        return res

    def trackbacking(self, candidates, target, startIndex, path, res):
        if startIndex >= len(candidates):
            return
        if sum(path) == target:
            res.append(path[:])
            return 
        
        for i in range(startIndex, len(candidates)):
            if sum(path) > target:
                return
            path.append(candidates[i])
            # 字母可以重复取的时候 i不+1
            self.trackbacking(candidates, target, i, path, res)
            path.pop()


6.括号生成
class Solution(object):
    def generateParenthesis(self, n):
        """
        :type n: int
        :rtype: List[str]
        """
        # 递归 想到画二叉树 然后遍历
        if n <= 0:
            return []
        res = []
        path = ""

        def dfs(path, left, right):
            # !注意这里很关键的点在于左边不能大于n个 且合理的括号生成过程中 右边的括号一定是小于等于左边的
            if left > n or right > left:
                return  
            if len(path) == n * 2:
                res.append(path)
            
            dfs(path + "(", left + 1, right)
            dfs(path + ")", left, right + 1)
        
        dfs(path, 0, 0)
        return res

7.单词搜索
class Solution(object):
    def exist(self, board, word):
        """
        :type board: List[List[str]]
        :type word: str
        :rtype: bool
        """
        #  深度优先搜索+剪枝
        def dfs(i, j, k): # i,j指的是目前在搜索的格子行 k指的是其在word上的索引
            # 1.越界
            if not 0 <= i < len(board) or not 0 <= j < len(board[0]): return False
            # 2.不匹配
            if board[i][j] != word[k]:
                return False
            #  3.当指针遍历到最后一个字母 那就是找到了
            if k == len(word) - 1:
                return True
            # 访问过得格子设为“”
            board[i][j] = ''
            # 寻找下一个字母
            res = dfs(i + 1, j, k + 1) or dfs(i - 1, j, k + 1) or dfs(i, j - 1, k + 1) or dfs(i, j + 1, k + 1) 
            # 复原
            board[i][j] = word[k]
            return res
        # 尝试在每个位置上进行深度搜索
        for i in range(len(board)):
            for j in range(len(board[0])):
                if dfs(i, j, 0):
                    return True
        return False
            
8.分割回文串
class Solution(object):
    def partition(self, s):
        """
        :type s: str
        :rtype: List[List[str]]
        """
        # 切割 —— 组合问题 —— 回溯 —— 且有startIndex
        res = []
        path = []
        self.trackbacking(s, 0, path, res)
        return res
    def trackbacking(self, s, startIndex, path, res):
        if startIndex >= len(s):
            res.append(path[:])
            return
        
        for i in range(startIndex, len(s)):
            if self.is_pali(s, startIndex, i):
                path.append(s[startIndex : i + 1])
                self.trackbacking(s, i + 1, path, res)
                path.pop()
            else:
                continue # 如果不是回文就不用考虑该切割方式了
            
    def is_pali(self, s, start, end):
        i = start
        j = end
        while i < j:
            if s[i] != s[j]:
                return False
            i += 1
            j -= 1
        return True


9.N皇后
class Solution(object):
    def solveNQueens(self, n):
        """
        :type n: int
        :rtype: List[List[str]]
        """
        # 棋盘的递归 —— 想到回溯
        # for 横向遍历-宽度
        # 递归 纵向遍历
        res = []
        # 注意初始化为n行,有n个“.”的字符串 需要摆放皇后的位置替换为Q
        board = [ '.' * n for i in range(n)]
        print(board)
        self.trackbacking(n, 0, board, res)
        return res
    
    # 从第几行开始遍历
    def trackbacking(self, n, row, board, res):
        if row == n:
            # 此时已经遍历到叶子结点 说明棋盘已经填满
            res.append(board[:])
            return 
        # 深度是遍历到第几行 宽度是遍历到第几列
        for col in range(n):
                if self.isValid(row, col, board):
                    board[row] = board[row][:col]+ 'Q' +  board[row][col + 1:]
                    self.trackbacking(n, row + 1, board, res)
                    board[row] = board[row][:col]+ '.' +  board[row][col + 1:]


    def isValid(self, row, col, board):
        # 1.检查同列有没有皇后 注意每行我们只遍历了一次 所以每行一定只有一个
        for i in range(row):
            if board[i][col] == "Q":
                return False
        # 2.检查左上方是否有皇后
        i, j = row - 1, col - 1
        while i >= 0 and j >=0:
            if board[i][j] == "Q":
                return False
            i -= 1
            j -= 1
        # 3.检查右上方是否有皇后  — 因为是每行依次遍历的 所以不检查上方
        i, j = row - 1, col + 1
        while i >= 0 and j < len(board):
            if board[i][j] == 'Q':
                return False
            i -= 1
            j += 1

        return True
10.搜索插入位置
class Solution(object):
    def searchInsert(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: int
        """
        # 时间复杂度 O(log n)
        left, right = 0, len(nums) - 1
        # 注意二分法的条件以及返回值
        while left <= right:
            mid = (left + right) / 2
            if nums[mid] ==  target:
                return mid
            elif nums[mid] > target:
                right = mid - 1
            else:
                left = mid + 1
        return left

11.搜索二维矩阵
class Solution(object):
    def searchMatrix(self, matrix, target):
        """
        :type matrix: List[List[int]]
        :type target: int
        :rtype: bool
        """
        # 思路:二分法的变体 只要注意查询的起点是左下角
        row, col = len(matrix) - 1, len(matrix[0]) - 1
        i, j = row, 0
        while 0 <= i <= row and 0 <= j <= col:
            if matrix[i][j] == target:
                return True
            elif matrix[i][j] > target:
                i -= 1
            elif matrix[i][j] < target:
                j += 1
        return False
12.在排序数组中寻找第一个和最后一个位置
class Solution(object):
    def searchRange(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: List[int]
        """
        # 注意对边界值进行处理
        left, right = 0, len(nums) - 1 
        ans = []
        while left <= right:
            mid = (left + right) / 2
            if nums[mid] == target:
                left_index, right_index = mid, mid
                while nums[left_index] == target:
                    left_index -= 1
                    if left_index < 0: 
                        # left_index = left_index + 1
                        break
                while nums[right_index] == target :
                    right_index += 1
                    if right_index >= len(nums):
                        # right_index -= 1
                        break
                return [left_index + 1, right_index - 1]
            elif nums[mid] > target:
                right = mid - 1
            else:
                left = mid + 1
        return [-1, -1]
13.寻找旋转排序数组中的最小值
class Solution(object):
    def findMin(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        left, right = 0, len(nums) - 1
        # 1.先判断数组序旋转后是否还是有序的
        if (len(nums) == 1) or (nums[left] < nums[right]):
            return nums[left]
        while left <= right:
            mid = (left + right) / 2
            #
            if nums[0] <= nums[mid]:
                # 此时序列无序 且 0到mid是有序的 因此必在右边
                left = mid + 1
            elif nums[mid] > nums[mid -1]:
                # 此时序列无序 且0到mid是无序的 mid-1又mid小 
                right = mid - 1
            else:
                # 其余情况下mid都刚好是最小值
                return nums[mid]
        return nums[left]
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值