目录

一、双指针

二、排序

三、贪心算法

四、二分查找

五、分治算法

六、搜索(DFS&BFS)

七、动态规划

八、递归

九、回溯

十、查并集

十一、滑动窗口

十二、记忆化搜索

 

 

 

一、双指针

题号:86、167、209

【典型题目】给定一个已按照 升序排列 的整数数组 numbers ,请你从数组中找出两个数满足相加之和等于目标数 target

class Solution:

    def twoSum(self, numbers: List[int], target: int) -> List[int]:

        i = 0

        j = len(numbers) - 1

        while i < j:

            if target - numbers[j] == numbers[i]:

                return [i + 1, j + 1]

            elif numbers[i] + numbers[j] > target:

                j -= 1

            else:

                i += 1

        return []

 

 

二、排序

题号:207、210

1. 转换成堆,堆 :时间复杂度 O(NlogK),空间复杂度 O(K)。

【典型题目】在某个集合中找出最大或最小的N个元素

 

import heapq

>>> nums=[1,8,2,23,7,-4,18,23,42,37,2]

>>> print(heapq.nlargest(3,nums))

[42, 37, 23]

>>> print(heapq.nsmallest(3,nums))

[-4, 1, 2]

 

 

 



 

【典型题目】如果正在寻找最大或者最小的N个元素,且同集合中元素的总数目相比,N很小,那么下面这些函数就可以提供更好的性能。

这些函数首先会在底层将数据转化成列表,且元素会以堆得顺序排列。例如:

 

>>> nums=[1,8,2,23,7,-4,18,23,42,37,2]

>>> import heapq

>>> heap=list(nums)

>>> heapq.heapify(heap)#将列表原地转换成堆

>>> heap

[-4, 2, 1, 23, 7, 2, 18, 23, 42, 37, 8]

 

2. 排序 :时间复杂度 O(NlogN),空间复杂度 O(1)

3. 快速选择 :时间复杂度 O(N),空间复杂度 O(1)

4. 拓扑排序是专门应用于有向图的算法BFS 的写法就叫「拓扑排序」,这里还用到了贪心算法的思想,贪的点是:当前让入度为 0 的那些结点入队

 

 

三、贪心算法

题号:322、1217、55

 

 

四、二分查找

题号:704、35、162、74

【典型题目】给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target  ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1

class Solution:

    def search(self, nums: List[int], target: int) -> int:

        left, right = 0, len(nums)-1

        while left <= right:

            mid = left + (right - left) // 2

            if nums[mid] > target:

                right = mid - 1

            elif nums[mid] < target:

                left = mid + 1

            elif nums[mid] == target:

                return mid

        return -1

 

 

【典型题目】编写一个高效的算法来判断 m x n 矩阵中,是否存在一个目标值

class Solution:

    def searchMatrix(self, matrix: List[List[int]], target: int) -> bool:

        n , m = len(matrix) , len(matrix[0])

        if m == 0 or n == 0 :return False

        l , r = 0 , m * n - 1

        while (l <= r):

            mid = (l + r) // 2

            if(matrix[mid // m][mid % m] > target):

                r = mid - 1

            elif(matrix[mid // m][mid % m] < target):

                l = mid + 1

            else:

                return True

        return False                

 

 

 

五、分治算法

题号:169、53

【典型题目】给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

注:便捷算法

class Solution:

    def maxSubArray(self, nums: List[int]) -> int:

        res = nums[0]

        sum = 0

        for num in nums:

            if sum > 0 :

                sum += num

            else:

                sum = num

            res = max(res,sum)

        return res

 

 

 

六、搜索(DFS&BFS)

1. DFS 题号:938、78、200

【典型题目】给定二叉搜索树的根结点 root,返回值位于范围 [low, high] 之间的所有结点的值的和。

class Solution:

    def rangeSumBST(self, root: TreeNode, low: int, high: int) -> int:

        if root==None:

            return 0

        ans_left = self.rangeSumBST(root.left, low, high)

        ans_right = self.rangeSumBST(root.right, low, high)

        rootval = root.val if low<=root.val<=high else 0

        return ans_left + ans_right + rootval

 

 

【典型题目】给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。

方法一==>DFS:

class Solution:

    def dfs(self, res, nums, cur, index):

        if index >= len(nums):

            res.append(cur.copy())  # 注意这里要用copy 要不然cur的改变会影响到res里的cur

            return

        # 下面有两个dfs 表示每个状态有两个子状态可供选择

        # 每个index前后保证状态不变

        cur.append(nums[index])

        self.dfs(res, nums, cur, index + 1)

        cur.pop()

        self.dfs(res, nums, cur, index + 1)

        

    def subsets(self, nums: List[int]) -> List[List[int]]:

        # 本题定义状态为(cur, index)

        res = []

        self.dfs(res, nums, [], 0)

        return res

 

方法二==>递归:

class Solution(object):

    def subsets(self, nums):

        if nums ==[]:

            return [[]]

        return self.subsets(nums[:-1]) + [i+[nums[-1]] for i in self.subsets(nums[:-1])]

 

 

【典型题目】岛屿数量

给你一个由 '1'(陆地)和 '0'(水)组成的的二维网格,请你计算网格中岛屿的数量。

岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。

此外,你可以假设该网格的四条边均被水包围。

方法一==>DFS

class Solution:

    def numIslands(self, grid: List[List[str]]) -> int:

        if not grid: return 0

        

        count = 0

        for i in range(len(grid)):

            for j in range(len(grid[0])):

                if grid[i][j] == '1':

                    self.dfs(grid, i, j)

                    count += 1

                    

        return count

    

    def dfs(self, grid, i, j):

        if i < 0 or j < 0 or i >= len(grid) or j >= len(grid[0]) or grid[i][j] != '1':

            return

        grid[i][j] = '0'

        self.dfs(grid, i + 1, j)

        self.dfs(grid, i - 1, j)

        self.dfs(grid, i, j + 1)

        self.dfs(grid, i, j - 1)

 

 

方法二==>BFS

class Solution:

    def numIslands(self, grid: List[List[str]]) -> int:

        count = 0

        for row in range(len(grid)):

            for col in range(len(grid[0])):

                if grid[row][col] == '1':  # 发现陆地

                    count += 1  # 结果加1

                    grid[row][col] = '0'  # 将其转为 ‘0’ 代表已经访问过

                    # 对发现的陆地进行扩张即执行 BFS,将与其相邻的陆地都标记为已访问

                    # 下面还是经典的 BFS 模板

                    land_positions = collections.deque()

                    land_positions.append([row, col])

                    while len(land_positions) > 0:

                        x, y = land_positions.popleft()

                        for new_x, new_y in [[x, y + 1], [x, y - 1], [x + 1, y], [x - 1, y]]:  # 进行四个方向的扩张

                            # 判断有效性

                            if 0 <= new_x < len(grid) and 0 <= new_y < len(grid[0]) and grid[new_x][new_y] == '1':

                                grid[new_x][new_y] = '0'  # 因为可由 BFS 访问到,代表同属一块岛,将其置 ‘0’ 代表已访问过

                                land_positions.append([new_x, new_y])

        return count

 

 

2. BFS 题号:102、107、200

【典型题目】102题:给你一个二叉树,请你返回其按 层序遍历 得到的节点值。 (即逐层地,从左到右访问所有节点)。

当遍历到新的一层的时候(也就是depth>=res数组的长度的时候),就往res里append一个空列表用于存储新的一层的结点值

同时往该结点对应那一层的列表里(res[depth])append结点的值对该结点的左子节点和右子节点重复以上工作,由于是从上到下存储的各层元素,所以最后需要倒转一下列表。

DFS

class Solution:

    def levelOrderBottom(self, root: TreeNode) -> List[List[int]]:

        res = []

        def dfs(root, depth):

            if not root:

                return

            if len(res)<depth+1:

                res.append([])

            res[depth].append(root.val)

            dfs(root.left, depth+1)

            dfs(root.right, depth+1)

        dfs(root,0)

        return res[::-1]

 

 

七、动态规划

题号:62、322、1143、509、121、70、279、22

【典型题目】给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。你可以认为每种硬币的数量是无限的。

class Solution:

    def coinChange(self, coins: List[int], amount: int) -> int:

        dp = [float('inf')] * (amount + 1)

        dp[0] = 0

 

        for coin in coins:

            for i in range(coin, amount + 1):

                dp[i] = min(dp[i], 1 + dp[i - coin])

        

        return dp[amount] if dp[amount] != float('inf') else -1

 

 

【典型题目】给定两个字符串 text1 和 text2,返回这两个字符串的最长公共子序列的长度。

一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。

例如,"ace" 是 "abcde" 的子序列,但 "aec" 不是 "abcde" 的子序列。两个字符串的「公共子序列」是这两个字符串所共同拥有的子序列。

若这两个字符串没有公共子序列,则返回 0。

class Solution(object):

    def longestCommonSubsequence(self, text1, text2):

        """

        :type text1: str

        :type text2: str

        :rtype: int

        """

        if not text1 or not text2:

            return 0

        #base case

        n = len(text1)

        m = len(text2)

        dp = [[0 for x in range(n+1)] for y in range(m+1)]

        for i in range(1,m+1):

            for j in range(1,n+1):

                if text1[j-1] == text2[i-1]:

                    dp[i][j] = dp[i-1][j-1] + 1

                else:

                    dp[i][j] = max(dp[i-1][j], dp[i][j-1])

 

        return dp[m][n]

 

 

 

八、递归

题号:509、206、344、687

 

【典型题目】斐波那契数列

方法一==>纯递归

class Solution:

    def fib(self, n: int) -> int:

        if n == 0 or n == 1 : return n

        else:

            return self.fib(n - 1) + self.fib( n - 2)

 

 

方法二==>递归+记忆法(数组)            

class Solution:

    def fib(self, n: int) -> int:

        memo=[0,1]

        i=2

        if n<2:

            return memo[n]        

        while i<=n:

            memo.append(memo[i-1]+memo[i-2])

            i+=1

        return memo[-1]

 

方法三==>动态规划

class Solution:

    def fib(self, n: int) -> int:

        if n == 0:

            return 0

        if n <= 2:

            return 1

        first = 1

        second = 1

        for i in range(3, n+1):

            first, second = second, first + second

        return second

 

 

【典型题目】反转一个链表

class Solution:

    def reverseList(self, head: ListNode) -> ListNode:

        if not head or head.next == None: return head

        res = self.reverseList(head.next)

        head.next.next = head

        head.next = None

        return res

 

 

【典型题目】反转字符串

双指针法

class Solution:

    def reverseString(self, s: List[str]) -> None:

        """

        Do not return anything, modify s in-place instead.

        """

        # double pointer

        i,j = 0,len(s)-1

        while i<j:

            s[i],s[j] = s[j],s[i]

            i+=1;j-=1

 

 

 

九、回溯

题号:22、78、77、46

 

【典型题目】给定两个整数 n 和 k,返回 1 ... n 中所有可能的 k 个数的组合。

class Solution:

    def combine(self, n: int, k: int) -> List[List[int]]:

        res = []

        def backtrack(i, k, tmp):

            if k == 0:

                res.append(tmp)

                return

            for j in range(i, n + 1):

                backtrack(j+1, k-1, tmp + [j])

        backtrack(1, k, [])

        return res

 

  十、并查集

题号:200、547、721

 

 

十一、滑动窗口

题号:209、1456

 

 

十二、记忆化搜索

题号:509、322