常见算法总结【leetcode】

目录

双指针

二分查找

滑动窗口

递归法

回溯法

分治法

深度优先搜索(DFS)

广度优先搜索(BFS)

贪心算法

并查集

记忆化搜索:

动态规划

排序算法

一道经典例题200. 岛屿数量

又一道非常典型的题:

一类会议安排题目:leetcode会员题


【注意:列出来的题目大部分是简单题;列出来的题目其最优解法不一定是本文中所说的算法,列举只是为了辅助更好的理解算法;有错误请帮我指出谢谢!】

双指针

普通双指针,碰撞双指针,快慢双指针

141. 环形链表 - 力扣(LeetCode)

881. 救生艇 - 力扣(LeetCode)

二分查找

一个特点是必须有序,然后一般与双指针结合在一起,一头一尾两个指针,往中间移动。704. 二分查找 - 力扣(LeetCode)35. 搜索插入位置 - 力扣(LeetCode)162. 寻找峰值 - 力扣(LeetCode)74. 搜索二维矩阵 - 力扣(LeetCode)

'''
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。
如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
'''
class Solution:
    def searchInsert(self, nums: List[int], target: int) -> int:
 
        l = 0 
        r = len(nums) -1
        while (l<=r):
            mid = l + (r-l)//2
            if target < nums[mid]:
                r = mid +1
            elif target >nums[mid]:
                l = mid - 1
        return l       
 
 总结!!!!!重点!!!!!!!!!!!!!!!!!!!!!!!!
 一共三种:
 第一种:左右都是闭区间
 class Solution:
    def searchInsert(self, nums: List[int], target: int) -> int:
        left = 0
        right = len(nums) - 1  ###闭区间为[left, right]
        while left <= right:   ####区间不能为空
            mid = (left + right) // 2
            if nums[mid] < target:
                left = mid + 1
            else:
                right = mid - 1
         return left
 第二种:左闭右开
  class Solution:
    def searchInsert(self, nums: List[int], target: int) -> int:
        left = 0
        right = len(nums)    ###左闭右开[left, right)
        while left < right:   ####区间不能为空
            mid = (left + right) // 2
            if nums[mid] < nums[mid+1]:
                left = mid + 1
            else:
                right = mid
        return left
第三种:左开右开
  class Solution:
    def searchInsert(self, nums: List[int], target: int) -> int:
        left = -1
        right = len(nums)    ###开区间(left, right)
        while left + 1 < right:   ####区间不能为空
            mid = (left + right) // 2
            if nums[mid] < nums[mid+1]:
                left = mid
            else:
                right = mid
        return right

'''
注意,这三个模板都是针对: >=  来说的
还有 >, <, <= 三种情况怎么办:
正常大于等于就是 searchInsert(target)
!!!!比如>的情况就可以看成是 >x+1,就比如上题目找的大于8,就可以改成>8+1,即>9::
searchInsert(target+1)
!!!!对于小于的情况,可以看成是: (>=8)-1,就是大于等于8的左边的那个数:
searchInsert(target)-1
!!!对于小于等于的情况,可以看成是:(>x)-1:
searchInsert(target+1)-1
'''

滑动窗口

解决数组中的定长问题,一般还有连续,一般滑动窗口也是使用双指针,然后改变j指针来改变窗口大小。209. 长度最小的子数组 - 力扣(LeetCode)1456. 定长子串中元音的最大数目 - 力扣(LeetCode)

#经典题目:209. 长度最小的子数组,就是说返回数组中个数最小的连续子数组,这个连续子数组的和等于target:
class Solution:
    def minSubArrayLen(self, target: int, nums: List[int]) -> int:
        i = 0 
        j = 0 
        total = 0 
        res = len(nums)+1
        while(j<len(nums)):
            total += nums[j]
            j+=1
            while(total>=target):
                res = min(res , j-i)
                total -= nums[i]
                i+=1
        if res == len(nums)+1:
            return 0
        else:
            return res

还有一种是只用一个指针i,然后判断i-k的值和i的值到底符不符合题意,然后计算,例题:1456. 定长子串中元音的最大数目

class Solution:
    def maxVowels(self, s: str, k: int) -> int:
        if s is None or len(s)==0 or len(s)<k:
            return 0 
        res= 0
        count = 0
        vowels = {'a', 'e', 'i', 'o', 'u'}
        for i in range(0,k):
            if s[i] in vowels:
                count = count +1
        res = max(res, count)
        for i in range(k,len(s)):
            if s[i-k] in vowels:
                count = count -1
            if s[i] in vowels:
                count = count +1
            res = max(res,count)
        return res

递归法

有一个标准的模板:509. 斐波那契数 - 力扣(LeetCode)206. 反转链表 - 力扣(LeetCode)344. 反转字符串 - 力扣(LeetCode)

recursion(int n):  #####首先这个就是那个递归函数,n是输入的参数
    if n == 0:   ###终止条件,这个时不需的
        return 0
    m = recursion(n-1)  ###拆解(如何递归到下一层)
    return m   #返回值
    
以反转字符串这一题为例(最好的方法是双指针,但这里用递归):
class Solution:
    def reverseString(self, s: List[str]) -> None:
        """
        Do not return anything, modify s in-place instead.
        """
        if len(s)==0 or s==None:
            return
        left = 0
        right = len(s)-1
        self.reverse(s,left,right)
        return s
    def reverse(self, s, left,right):
        if left>=right:
            return 
        self.reverse(s, left+1,right-1)
        s[left],s[right] = s[right],s[left]

回溯法

一般用于查找“所有的”这类题型。一个经典例题,【1,2,3】求所有子集,回溯法就是要先分层,然后套公式 22. 括号生成 - 力扣(LeetCode)78. 子集 - 力扣(LeetCode)

class Solution:
    def subsets(self, nums: List[int]) -> List[List[int]]:
        result = [[]]
        for i in range (1,len(nums)+1):
            self.backtracking(nums,i,[],result,0)######重点就是这个backtracking怎么定义
        return result

    def backtracking(self,nums,length,subset,result,index):
        if len(subset)==length:   ####这一块就是递归中的中止条件(剪枝的过程)
            result.append(subset[:])
            return
        for i in range(index,len(nums)):
            subset.append(nums[i])
            self.backtracking(nums,length    ,subset,result,i+1)####这里i+1需要注意,因为不允许重复
            subset.pop()   ####记得pop,因为还要返回走另一条路,所以要将新加的删去
            
22. 括号生成
数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。
class Solution:
    def generateParenthesis(self, n: int) -> List[str]:
        result = []
        str = ''
        self.backtracking(n, 0, 0 ,result , str)
        return result
    def backtracking(self, n ,left, right, result, str):
        if left<right:
            return
        if left == right == n:
            result.append(str)
        if left < n:
            self.backtracking(n, left+1, right,result, str+'(')
        if left>right:
            self.backtracking(n,left,right+1,result,str+')')

分治法

应用了递归的思想,则需要一个终止条件,然后将大问题分解为小问题169. 多数元素 - 力扣(LeetCode)53. 最大子数组和 - 力扣(LeetCode)

func(nums)——>int:
    return getmax(nums,0,len(nums)-1)
def getmax(nums,left,right):###实际上还是递归,不过分成几个小问题递归
    if left==right:
        return nums[left]   ##### 终止条件
    mid = left + (right-left)//2
    leftmax = self.getmax(nums,left,mid)
    rightmax = self.getmax(nums,mid+1,right)
    return max(leftmax,rightmax)  ###这只是一个例子,具体问题具体分析
    
一个例子:53. 最大子数组和
给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

深度优先搜索(DFS)

从root开始,尽可能深的搜索一个分支,把一个分支搜索完,再去搜索下一个分支

78. 子集 - 力扣(LeetCode)938. 二叉搜索树的范围和 - 力扣(LeetCode)200. 岛屿数量 - 力扣(LeetCode)

class Solution:
    def subsets(self, nums: List[int]) -> List[List[int]]:
        result = []
        self.dfs(nums,result,0,[])
        return result
    def dfs(self, nums, result, index, subset):
        result.append(subset[:])
        if index == len(nums):   ##终止条件
            return
        for i in range(index, len(nums)):
            subset.append(nums[i])
            self.dfs(nums,result,i+1,subset)
            subset.pop()
            
 ###还是那道经典例题,【1,2,3】求子集   没有什么技巧,就好好理解吧,画个图理解
 #结构就是先定义dfs,dfs肯定是一个递归,然后也许有循环,也许没有,总之肯定有一个标志符,index或者level
 ##层层递进,一般是+1,再dfs这样
 

广度优先搜索(BFS)

就是一层一层遍历,一般和队列,链表等结合,这两个例子都是二叉树

102. 二叉树的层序遍历 - 力扣(LeetCode)107. 二叉树的层序遍历 II - 力扣(LeetCode)

from collections import deque
class Solution:
    def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
        result = []
        if root is None:
            return result
        q = deque([])
        q.append(root)
        while len(q)>0:
            size = len(q)
            ls = []
            while size>0:
                cur = q.popleft()
                ls.append(cur.val)
                if cur.left is not None:
                    q.append(cur.left)
                if cur.right is not None:
                    q.append(cur.right)
                size = size-1
            result.append(ls[:])
        return result

贪心算法

每一步都取最好最大的,然后看能不能达到结果,不能就返回?大概这意思吧,一般和回溯什么的结合一下322. 零钱兑换 - 力扣(LeetCode)(背包问题,也可以回溯)1217. 玩筹码 - 力扣(LeetCode)55. 跳跃游戏 - 力扣(LeetCode)

class Solution:
    def canJump(self, nums: List[int]) -> bool:
        '''
        贪心:
        先每一步都走到最远,然后判断下一步能不能走到这个最远距离,能走到的话,
        那就再在这个最远距离内找最远距离
        一直更新这个最远距离,直到走到末尾
        如果以及到大最远了还是没有到达末尾那就输出false

        '''
        rightmost = 0
        for i in range(len(nums)):
            if i <= rightmost:
                rightmost = max(rightmost,nums[i]+i)
                if rightmost >= len(nums)-1:
                    return True
        return False

并查集

模板

class UnionFind:
    def __init__(self, grid):
        row = len(grid)
        col = len(grid[0])##看题目给的数据是什么
        self.root =[-1] * (row*col)###这里是定义一个数组的长度,n也可以
        self.count = (row*col)  ###一般会有一个计数的
        for i in range(row*col):
            self.root[i] = i  ####先把其节点定为自身

    def find(self, x):
        if x ==self.root[x]:
            return self.root[x]#判断x的根节点是否是其本身,是就返回
        else:
            self.root[x] = self.find(self.root[x])#不是的话,就寻找其根节点的根节点
            return #**** self.root[x]
    ####这个是快速find        
    def find(self, x):
        if x !=self.root[x]:
            self.root[x] = self.find(self.root[x])#不是的话,就寻找其根节点的根节点
            return self.root[x]
        
    def union(self, x, y):#####这个函数是将两个数的节点改为一个
        rootX = self.find(x)
        rootY = self.find(y)
        if rootX != rootY:  
            self.root[rootX] = rootY ###将X的根节点改为Y的根节点
            self.count -= 1
    
    ####这个是快速union
    def union(self, x, y):#####这个函数是将两个数的节点改为一个
        rootX = self.find(x)
        rootY = self.find(y)
        if rootX != rootY:
            if self.rank[rootX] > self.rank[rootY]:
                self.root[rootY] = rootX
            elif self.rank[rootX] < self.rank[rootY]:
                self.root[rootX] = rootY
            else:
                self.root[rootY] = rootX
                self.rank[rootX] += 1

记忆化搜索:

就是将已经算过的值,存起来,之后要用就直接取509. 斐波那契数 - 力扣(LeetCode)322. 零钱兑换 - 力扣(LeetCode)

动态规划

1、动态规划有三要素:初始状态,最终状态和方程式(状态方程式获得中间状态)2、动态规划一般用于:求有多少种方式,方法,路径;求最值:机器人从左到右的最大路径和;求存在性:有没有从左到右的一个路径可能509. 斐波那契数 - 力扣(LeetCode)62. 不同路径 - 力扣(LeetCode)121. 买卖股票的最佳时机 - 力扣(LeetCode)70. 爬楼梯 - 力扣(LeetCode)279. 完全平方数 - 力扣(LeetCode)221. 最大正方形 - 力扣(LeetCode)

动态规划比较难,但确实很有效

一个比较经典简单的问题:机器人从左上到左下一共有几种路径(只能往下往右走)
分析:一个位置的总路径,就是他上面的总路径加下面的总路径
class Solution:
    def uniquePaths(self, m: int, n: int) -> int:
        dp = [[1]*n for _ in range(m)]

        for i in range(1,m):
            for j in range(1,n):
                dp[i][j] = dp[i-1][j] + dp[i][j-1]
        return dp[-1][-1]

排序算法

'''
快速排序
快速排序的思想就是:先顶一个基准,一般以数组第一个数为基准
然后大于基准的放到后面,小于基准的放到前面
同时用low和high两个指针来记录前面和后面,先while循环high
因为此时low已经作为基准了,相当于low是空的,先从high遍历,可以将high值先放入到low中
这样low和high交替互换,最终得到结果
用递归是,每一次base都将nums分为左右两个nums,总nums排序完后,分nums也需要排序
'''
class Solusion:
    def quicksort(self, nums, start, end):
        if start >= end:
            return   ####这个是结束条件
        low = start
        high = end
        base = nums[start] ##一般以第一个为基准

        while low < high:
            while low < high and nums[high] >= base:
                high -= 1   ###如果大于基准就让其在右边不动,然后high指针往前挪
            nums[low] = nums[high]  ##这里是已经跳出循环了,也就是nums[high]小于base了,就将这个数移动到low的位置
            while low < high and nums[low] < base:
                low += 1
            nums[high] = nums[low]  ##同理,也是已经跳出while循环了,将大于base的low移动到high的位置

        nums[low] = base ###已经全部循环完了,low和high已经重合了,这个位置就是最终base要填入的位置,这里写成nums[high]也是一样的

        self.quicksort(nums, start, low - 1) ###递归,第一轮排完序了,开始排序第二轮左边的nums,这时候,开始还是0,但结束就是low-1
        self.quicksort(nums, low + 1, end)  ###排序第二轮的右边

        return nums

a = Solusion()
nums = [54, 26, 93, 17, 77, 31, 44, 55, 20]
b = a.quicksort(nums, 0, len(nums)-1)
print(b)


'''
归并排序
归并排序就是先通过一半一半这样分,直到分成一个一个的后,
然后两个两个这个合,一路向上回归
'''
def merge(left, right):  ###这个一块是归并
    result = []
    while left and right:
        if left[0] < right[0]:
            result.append(left.pop(0))
        else:
            result.append(right.pop(0))
    if left:
        result += left
    else:
        result += right
    return result


def merge_sort(lists):  ####这一块是递推
    if len(lists) <= 1:
        return lists

    mid = len(lists)//2
    arr1 = lists[:mid]
    arr2 = lists[mid:]

    left = merge_sort(arr1)
    right = merge_sort(arr2)

    return merge(left, right)  #

lists = [17, 56, 71, 38, 61, 62, 48, 28, 57, 42]
print(merge_sort(lists))

一道经典例题200. 岛屿数量

  • DFS法:一个一个遍历,当遇到"1”时,结果就加1,然后就用dfs将其上下左右都变为0,然后继续往后遍历

    class Solution:
        def numIslands(self, grid: List[List[str]]) -> int:
            if grid is None or len(grid) ==0:
                return 0
            result = 0
            row = len(grid)
            col = len(grid[0])
            for i in range(0,row):
                for j in range(0,col):
                    if grid[i][j] == '1':
                        result += 1
                        self.dfs(grid, i, j, row,col)
            return result
        def dfs(self, grid, x, y, row, col):
            if x<0 or y<0 or x>=row or y>=col or grid[x][y] == '0':
                return
            grid[x][y] = '0'
            self.dfs(grid, x-1 , y, row,col)
            self.dfs(grid, x+1 , y, row,col)
            self.dfs(grid, x , y-1, row,col)
            self.dfs(grid, x , y+1, row,col)
  • BFS法:道理是一样的,也是寻找1,找到一个1就将result+1,然后将周围的1变为0,但是BFS就和队列结合在一起,检测到一个1就加入到队列,然后循环再将其pop出去,再检测其周围的1,这样下去。队列的目的,应该就是使用while循环

    from collections import deque
    class Solution:
        def numIslands(self, grid: List[List[str]]) -> int:
            if grid is None or len(grid) ==0:
                return 0
            result = 0
            row = len(grid)
            col = len(grid[0])
            q = deque()
            for i in range(0,row):
                for j in range(0,col):
                    if grid[i][j] == '1':
                        result += 1
                        q.append([i,j])
                        grid[i][j] = '0'
                        while(len(q))>0:
                            cur = q.pop()
                            x = cur[0]
                            y = cur[1]
                            if x-1>=0 and grid[x-1][y] =='1':
                                grid[x-1][y] ='0'
                                q.append([x-1,y])
                            if x+1 < row and grid[x+1][y] =='1':
                                grid[x+1][y] ='0'
                                q.append([x+1,y])
                            if y-1>=0 and grid[x][y-1] =='1':
                                grid[x][y-1] ='0'
                                q.append([x,y-1])
                            if y+1 < col and grid[x][y+1] =='1':
                                grid[x][y+1] ='0'
                                q.append([x,y+1]) 
            return result
  • 并查集法:这个方法就是首先将所有的值都找一个和她本身一样的一个祖先,然后遍历,一旦找到一个1,就将其上下左右的1的祖先都变成和他一个祖先,然后就可以将count-1,然后还有计算water(也就是0)的数量,要将所有的water也减去,这样算下来的就是岛屿的数量。

    from collections import deque
    class UnionFind:
        def __init__(self, grid):
            row = len(grid)
            col = len(grid[0])##看题目给的数据是什么
            self.root =[-1] * (row*col)###这里是定义一个数组的长度,n也可以
            self.count = row*col
            for i in range(row*col):
                self.root[i] = i  ####先把其节点定为自身
    
        def find(self, x):
            if x ==self.root[x]:
                return self.root[x]#判断x的根节点是否是其本身,是就返回
            else:
                self.root[x] = self.find(self.root[x])#不是的话,就寻找其根节点的根节点
                return self.root[x]
            
        def union(self, x, y):#####这个函数是将两个数的节点改为一个
            rootX = self.find(x)
            rootY = self.find(y)
            if rootX != rootY:  
                self.root[rootX] = rootY ###将X的根节点改为Y的根节点
                self.count -= 1
    
    class Solution:
        def numIslands(self, grid: List[List[str]]) -> int:
            row =len(grid)
            col = len(grid[0])
            if grid is None or len(grid)==0:
                return 0
            water = 0
            uf = UnionFind(grid)
            for i in range(len(grid)):
                for j in range(len(grid[0])):
                    if grid[i][j] == '0':
                        water += 1
                    else:
                        dinections = [[0,1], [0,-1],[1,0],[-1,0]]
                        for d in dinections:
                            x = i + d[0]
                            y = j + d[1]  #####这个用法很特殊,要好好看一下
                            if x>=0 and x<row and y>=0 and y<col and grid[x][y]=='1':
                                uf.union(x*col+y, i*col+j)  ##这一步就是为了同化操作,也是遍历了1的上
                                #下左右,然后将也为1
                                ###的祖先改为原本遍历到的1的祖先
            return uf.count-water

    又一道非常典型的题:

    回溯法:
    class Solution:
        def subsets(self, nums: List[int]) -> List[List[int]]:
            result = [[]]
            for i in range (1,len(nums)+1):
                self.backtracking(nums,i,[],result,0)
            return result
    
        def backtracking(self,nums,length,subset,result,index):
            if len(subset)==length:
                result.append(subset[:])
                return
            for i in range(index,len(nums)):
                subset.append(nums[i])
                self.backtracking(nums,length,subset,result,i+1)
                subset.pop()        
    DFS法:
    class Solution:
        def subsets(self, nums: List[int]) -> List[List[int]]:
            result = []
            self.dfs(nums,result,0,[])
            return result
        def dfs(self, nums, result, index, subset):
            result.append(subset[:])
            if index == len(nums):
                return
            for i in range(index, len(nums)):
                subset.append(nums[i])
                self.dfs(nums,result,i+1,subset)
                subset.pop()
    扩展法:
    class Solution:
        def subsets(self, nums: List[int]) -> List[List[int]]:
            result = []
            result.append([])
            for num in  nums:
                temp = []
                for res in  result:
                    r = res.copy()
                    r.append(num)
                    temp.append(r)
                for te in temp:
                    result.append(te)
            return result
    枚举:
    class Solution:
        def subsets(self, nums: List[int]) -> List[List[int]]:
            res = [[]]
            for num in nums:
                for index in range(len(res)):
                    s = res[index]
                    c = [num]
                    res.append(s + c)
            return res
    ####res.append(s+b)中,s =[1], b=[2],加起来就是【1,2】,很神奇,记住

    一类会议安排题目:leetcode会员题

    思路:首先对结束时间升序排序,然后找出最早结束的一场作为开始,
    然后就依次判断后一场的开始时间是不是比前一场要大,大就算进去
    一般比如说出的类型为:
    [[0,2],[3,4],[4,8],[2,6],[6,7],[7,8]]
    
    s = [0, 3, 4, 2, 6, 7]
    f = [2, 4, 8, 6, 7, 8]
    ####这是排序过程
    for i in range(len(f)):
        for j in range(0, len(f) - i - 1):
            if f[j] > f[j + 1]:
                f[j], f[j + 1] = f[j + 1], f[j]
                s[j], s[j + 1] = s[j + 1], s[j]
    print(s)
    print(f)
    
    '''
    ziplist = list(zip(f, s))
    zipsorted = sorted(ziplist)
    list1, list2 = zip(*zipsorted)
    f = list(list1)
    s = list(list2)
    print(s)
    print(f)
    '''
    
    ####这个就是添加过程
    def greedy(s, f, n):
        a = [True for x in range(n)]
        # 初始选择第一个活动
        j = 0
        for i in range(1, n):
            # 如果下一个活动的开始时间大于等于上个活动的结束时间
            if s[i] >= f[j]:
                a[i] = True
                j = i
            else:
                a[i] = False
        return a
    A = greedy(s,f,6)
    
    res = []
    for k in range(len(A)):
        if A[k]:
            res.append('({},{})'.format(s[k],f[k]))
    print(' '.join(res))
    输出:(0,2) (3,4) (6,7) (7,8)

    暂时到这,之后有题目和方法再加。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值