八月力扣

前言

刷了好久了,但是八月的现在才总结,因为今天往回看,刷了一些题,但是有些题早已经忘记,有些算法下意识还是使用了不好的思路,或者思路不够清晰,所以,直面自己早就想但是总是逃避的总结,通过总结来回顾,引申。

8.1

老规矩,直接上题在这里插入图片描述
这个题可以使用动态规划,或者滑动窗口,如果使用动态规划,就要找到状态转移方程,首先分析题,这个就是在多个数组中,找到一个范围,然后这个范围大小最小,最大最小问题用动态规划。可以把每个数组的头放到一个数组中,每次最小的头出去 ,这个数组中的下一个元素进来,然后有个数组的元素全用过,因为动的是最小的,所以如果全动完,这个数组的最后一个元素肯定是头,头就不能动了,然后后边的元素如果动,一定是变大,最后一个范围只能更大,所以这个时候肯定是最小。
滑动窗口也是这个道理。

class Solution:#动态规划
    def smallestRange(self, nums):
        def find_min(num):
            return num.index(min(num))

        lenth=[]
        k1=0
        k=len(nums)
        for j in range(k):
            lenth.append(0)
            k1+=len(nums[j])
        dp = [[0 for i in range(k)] for i in range(k1)]  # 建立DP数组
        for j in range(k):
            dp[0][j]=nums[j][0]

        min_temp=find_min(dp[0])
        a=lenth[min_temp]+1
        if a>len(nums[min_temp])-1:
            return [min(dp[0]),max(dp[0])]
        lenth.pop(min_temp)
        lenth.insert(min_temp,a)
        tem = []
        for j in range(k):
            tem.append(nums[j][lenth[j]])
        for i in range(1,k1):
            b=max(tem)-min(tem)
            c=max(dp[i-1])-min(dp[i-1])
            if b>=c:
                dp[i]=dp[i-1].copy()
            else:
                dp[i]=tem.copy()
            min_temp = find_min(tem)
            a = lenth[min_temp] + 1
            if a > len(nums[min_temp])-1:
                return [min(dp[i]),max(dp[i])]
            lenth[min_temp]+=1
            tem[min_temp]=nums[min_temp][lenth[min_temp]]
class Solution1:#滑动窗口
    def smallestRange(self, nums) :
        k = len(nums)
        if k == 1: return [nums[0][0], nums[0][0]]
        indexs = [0] * k
        lists = []
        largest = -10 ** 5
        import heapq
        # 生成大小为k的堆 是小顶堆,每次都将最小值取出去,然后换成这个值所在数组的下一个元素
        for i in range(k):
            num = nums[i][0]
            heapq.heappush(lists, (num, i))
            largest = max(largest, num)

        left, right = lists[0][0], largest

        while True:
            num, i = heapq.heappop(lists)
            # 取出最小的数,以及对应的列表的下标
            indexs[i] += 1
            # 下标+1
            if indexs[i] >= len(nums[i]):  # 退出循环
                break
            # 通过下标i找到列表,再通过indexs数组找到下一个元素的下标
            newnum = nums[i][indexs[i]]
            heapq.heappush(lists, (newnum, i))  # 插入新的数
            largest = max(largest, newnum)  # 更新最大值
            if largest - lists[0][0] < right - left:  # 更新区间
                left, right = lists[0][0], largest
        return [left, right]

8.3

在这里插入图片描述
这个是老题了,三种方式,
从头哈希表遍历,出现次数最多的就是
排序,中间的那个值肯定是
摩尔投票:众数和非众数
众数是超过长度一半的数,
票数正负抵消: 设数组 nums 中的众数为 x ,数组长度为 n 。若 nums 的前 a 个数字的 票数和 = 0,则 数组后 (n−a) 个数字的 票数和一定仍 >0 (即后 (n-a) 个数字的 众数仍为 x )
为0的时候重新设定众数,

class Solution:
    def majorityElement(self, nums: List[int]) -> int:
        votes = 0
        for num in nums:
            if votes == 0: x = num
            votes += 1 if num == x else -1
        return x

在这里插入图片描述
三个要素:加数,被加数,进位
返回值是字符型

class Solution:
    def addStrings(self, num1: str, num2: str) -> str:
        ## 主要是用于大数相加,对于python来说没问题
        ## 首先将长度归一化
        m = len(num1)
        n = len(num2)
        while m > n:
            num2 = '0' + num2
            n += 1
        while m < n:
            num1 = '0' + num1
            m += 1
        res = ''
        carry = 0
        for i in range(m - 1, -1, -1):
            a = int(num1[i])
            b = int(num2[i])
            sum = a + b + carry
            res = str(sum % 10) + res
            carry = sum // 10
        if carry:
            res = '1' + res
        return res

在这里插入图片描述
这个看题目,除了C之外,其他的都可以看做是数,需要一个栈,每次的字母都对栈的最后元素操作,最后计算栈内数字的总和即可

class Solution:
    def calPoints(self, ops) -> int:
        a=[]
        for i in range(len(ops)):
            if ops[i]=='C':
                a.pop()
            elif ops[i]=='D':
                a.append(int(a[-1])*2)
            elif ops[i]=='+':
                a.append(int(a[-1])+int(a[-2]))
            else:
                a.append(int(ops[i]))
        return sum(a)

在这里插入图片描述
思路:
从头到尾,如果没有遇到这个指定字符,就给前边元素+1,具体实现可以用数组下标减去前边的指定字符位置
然后再从后往前,来这么一遍,然后最后输出每个位置的最小值的数组,这种方法的时间复杂度和空间复杂度都为O(N)
还有一种思路,先找出指定字符位置,然后做一个表来标记,然后遍历,用当前的数组下标减去表中位置的绝对值最小即是

class Solution(object):
    def shortestToChar(self, S, C):
        prev = float('-inf')
        ans = []
        for i, x in enumerate(S):
            if x == C: prev = i
            ans.append(i - prev)

        prev = float('inf')
        for i in range(len(S) - 1, -1, -1):
            if S[i] == C: prev = i
            ans[i] = min(ans[i], prev - i)
        return ans
class Solution1:
    def shortestToChar(self, S: str, C: str) :
        c_idx = [i for i in range(len(S)) if S[i]==C ]
        return [min(abs(i- j)  for j in c_idx) for i in range(len(S))]

在这里插入图片描述
经典岛屿问题,之后碰到了好多,这种围成圈圈或者咋滴咋滴的。可以使用数组来简化代码,思路就正常思路

class Solution:
    def islandPerimeter(self, grid: List[List[int]]) -> int:
        res=0
        for i in range(len(grid)):
            for j in range(len(grid[0])):
                if grid[i][j]==1:
                    res+=4
                    if i-1>=0 and grid[i-1][j]==1:
                        res-=2
                    if j-1>=0 and grid[i][j-1]==1:
                        res-=2
        return res

其中中间的元素并不对周长产生影响,如果这个元素周围连着元素,只能多两个边,而且是从上往下数,所以只要考虑元素的前边和上边是否连着即可

8.7

在这里插入图片描述
可以用深度遍历和广度遍历来进行判断:

class Solution:#BFS
    def isSameTree(self, p: TreeNode, q: TreeNode) -> bool:
        root1=[p]
        root2=[q]
        while root1 and root2:
            w=root1.pop()
            e=root2.pop()
            if isinstance(w,TreeNode) and isinstance(e,TreeNode):
                if w.val!=e.val:
                    return False 
                root1.extend([w.left,w.right])
                root2.extend([e.left,e.right])
            elif isinstance(w,TreeNode) or isinstance(e,TreeNode):
                return False
        #if root1 or root2:
            #return False
       # else:
            #return True  这个本身是判断两个栈是否为空,但实际不用判断,因为最后的元素如果是null对整个树没有影响,前边如果有位置不一样的话在上边就已经判断完毕了
        return True
class Solution1:#DFS
    def isSameTree(self, p: TreeNode, q: TreeNode) -> bool:
        if not q and not p:
            return True
        elif not p or not q:
            return False
        elif p.val != q.val:
            return False
        else:
            return self.isSameTree(p.left,q.left) and self.isSameTree(p.right,q.right)

实际测试,这两个算法的复杂度一样
在这里插入图片描述
先把数组转化为集合,然后看集合内元素种类,如果大于长度二分之一就是长度二分之一,否则就是集合大小

class Solution:
    def distributeCandies(self, candies: List[int]) -> int:
        a=collections.Counter(candies)#集合a=set(candies)
        if len(a)<=len(candies)//2:
            return len(a)
        else:
            return len(candies)//2

在这里插入图片描述
每个数都是由数位上的数字乘以权重,比如abcdef这个数,其实是a * 105 + b * 104 + c * 10 3 + d * 102 + e * 101+f
例如 a * 105 可以化成a * (99999+1),所以,模除9即可变成a+b+c+d+e+f,直到结果小于等于9

class Solution:
    def addDigits(self, num: int) -> int:
        if num == 0:
            return 0
        if num % 9 == 0:
            return 9
        else:
            return num % 9

在这里插入图片描述
这个题就是看两个字符串中元素是否相同,内容大小。使用collections类即可,通过哈希表

class Solution:
    def CheckPermutation(self, s1: str, s2: str) -> bool:
        if collections.Counter(s1)==collections.Counter(s2):
            return True
        else:
            return False

在这里插入图片描述
杨辉三角就是从第三行开始,每个元素都是上边两个元素的和,最边上的元素一直是1后边的是[i-1][j-1]+[i-1][j]

class Solution:
    def generate(self, numRows: int) :
        a = [[1], [1, 1]]
        if numRows==0:
            return []
        elif numRows==1:
            return [[1]]
        elif numRows==2:
            return a
        else:
            tem=0
            for i in range(2,numRows):
                a.append([1,1])
                for j in range(1,i):
                    tem=a[i-1][j-1]+a[i-1][j]
                    a[i].insert(j,tem)
            return a

在这里插入图片描述
sorted函数本身自带很多种排序方式,所以只要设置sorted排序方式即可

class Solution:
    def relativeSortArray(self, arr1, arr2) :
        return sorted(arr1, key=lambda x: (0, arr2.index(x)) if x in arr2 else (1, x))

Python sort函数:
https://blog.csdn.net/lyy14011305/article/details/76148512
sort sorted
a.sort函数定义:sort(cmp=None, key=None, reverse=False)
sorted函数定义:sorted(iterable, cmp=None, key=None, reverse=False)
1、函数sorted()不改变原来的list,而是返回一个新的排好序的list。

2、cmp与key均可以采用lambda表达式

3、采用cmp是确定排序方式(如:从大到小还是从小到大),排序的key是函数自己选择;采用key是确定排序的key,排序方式是函数自己选择。

8.10

在这里插入图片描述
先数第一个出现的元素个数,如果变了就计数第二个元素个数,输出最小值,然后再变再数,直到结尾

class Solution:
    def countBinarySubstrings(self, s: str) -> int:
        tem = s[0]
        a = 0
        c = []
        d=0
        for i in s:
            if i == tem:
                a += 1
            else:
                c.append(a)
                tem = i
                a = 1
        c.append(a)
        for i in range(1,len(c)):
            d+=min(c[i-1],c[i])
        return d
class Solution1:
    def countBinarySubstrings(self, s: str) -> int:
        pre, cur, res, prec = 0, 1, 0, s[0]
        for c in s[1:]:
            if c != prec: pre, cur = cur, 1
            else: cur += 1
            if cur <= pre: res += 1
            prec = c
        return res

在这里插入图片描述
简单方法:变成字符串,双指针,前后指针同时移动,直到后大于等于前。
如果是整数操作,则先判断长度,然后每次原数除以10,新数乘10+原数模10,最后判断是否相等

class Solution:
    def isPalindrome(self, x: int) -> bool:
        n=0
        while x>n or (x%10==0 and x!=0):
            n = n * 10 + x % 10
            x = x // 10
        if n == x or n//10==x:
            return True
        return False

在这里插入图片描述
弄俩头,然后每次接上一个最小的,如果有一个空了,直接全续后边

class Solution:
    def mergeTwoLists(self, l1, l2):
        prehead = ListNode(-1)
        prev = prehead
        while l1 and l2:
            if l1.val <= l2.val:
                prev.next = l1
                l1 = l1.next
            else:
                prev.next = l2
                l2 = l2.next            
            prev = prev.next
        # 合并后 l1 和 l2 最多只有一个还未被合并完,我们直接将链表末尾指向未合并完的链表即可
        prev.next = l1 if l1 is not None else l2
        return prehead.next

8.11

在这里插入图片描述
两种方法:动态规划,或者中心扩展,每次都找一个中心,然后两边判断,直到不一样或者到了边界停止

class Solution:#中心扩展法1
    def expandAroundCenter(self, s, left, right):
        while left >= 0 and right < len(s) and s[left] == s[right]:
            left -= 1
            right += 1
        return left + 1, right - 1
    def longestPalindrome(self, s: str) -> str:
        start, end = 0, 0
        for i in range(len(s)):
            left1, right1 = self.expandAroundCenter(s, i, i)
            left2, right2 = self.expandAroundCenter(s, i, i + 1)
            if right1 - left1 > end - start:
                start, end = left1, right1
            if right2 - left2 > end - start:
                start, end = left2, right2
        return s[start: end + 1]
class Solution:#中心扩展法2
    def longestPalindrome(self, s: str) -> str:
        n=len(s)
        start=0
        end=0
        def find(left,right):
            while left>=0 and right<n and s[left]==s[right]:
                left-=1
                right+=1
            return left+1,right-1
        for i in range(n-1):
            a1,b1=find(i,i)
            a2,b2=find(i,i+1)
            if b1-a1>end-start:
                start,end=a1,b1
            if b2-a2>end-start:
                start,end=a2,b2
        return s[start:end+1]
函数如果设定在函数里边,调用会省时间,如果两个函数处于同一等级,调用可能更费时间  在此题中,21快200ms左右
class Solution:#动态规划
    def longestPalindrome(self, s: str) -> str:
        n = len(s)
        dp = [[False] * n for _ in range(n)]
        ans = ""
        # 枚举子串的长度 l+1
        for l in range(n):
            # 枚举子串的起始位置 i,这样可以通过 j=i+l 得到子串的结束位置
            for i in range(n):
                j = i + l
                if j >= len(s):
                    break
                if l == 0:
                    dp[i][j] = True
                elif l == 1:
                    dp[i][j] = (s[i] == s[j])
                else:
                    dp[i][j] = (dp[i + 1][j - 1] and s[i] == s[j])
                if dp[i][j] and l + 1 > len(ans):
                    ans = s[i:j+1]
        return ans

在这里插入图片描述
此题可以看做是找到所有未被围绕的区域,可以从边上出发,将所有连起来的‘0’改为‘T’,等到最后将所有0改为’X’,再将‘T’改为‘0’,如果中间部分小当然从头遍历,找内部‘0’好,但是中间如果特别大,则这样寻找费时费力,不如找到边界方便,边界的话复杂度为O(N),中间为O((N-1)2).

class Solution:
    def solve(self, board):
        if board == []:
            return []
        n1 = len(board)
        n2 = len(board[0]) 
        def find(x,y):
            board[x][y]='T'
            q=[-1,1]
            for i in q:
                if 0<=x+i<n1:
                    if board[x+i][y]=='O':
                        find(x+i,y)
                if 0<=y+i<n2:
                    if board[x][y+i]=='O':
                        find(x,y+i)
        for i in range(n2):
            if board[0][i]=='O':
                find(0,i)
            if board[n1-1][i]=='O':
                find(n1-1,i)
        for i in range(n1):
            if board[i][0]=='O':
                find(i,0)
            if board[i][n2-1]=='O':
                find(i,n2-1)
        for i in range(n1):
            for j in range(n2):
                if board[i][j]=='O':
                    board[i][j]='X'
                if board[i][j]=='T':
                    board[i][j]='O'
        return board

8.12

在这里插入图片描述
这个题是图的遍历,图的遍历分为深度优先和广度优先,其中要注意的是要保存遍历过的节点,不再进行遍历,防止死锁,然后拷贝

class Solution(object):

    def __init__(self):
        self.visited = {}

    def cloneGraph(self, node):
        """
        :type node: Node
        :rtype: Node
        """
        if not node:
            return node

        # 如果该节点已经被访问过了,则直接从哈希表中取出对应的克隆节点返回
        if node in self.visited:
            return self.visited[node]

        # 克隆节点,注意到为了深拷贝我们不会克隆它的邻居的列表
        clone_node = Node(node.val, [])

        # 哈希表存储
        self.visited[node] = clone_node

        # 遍历该节点的邻居并更新克隆节点的邻居列表
        if node.neighbors:
            clone_node.neighbors = [self.cloneGraph(n) for n in node.neighbors]

        return clone_node
深度优先:每次都进去一个节点都要把邻居都遍历完
from collections import deque
class Solution(object):

    def cloneGraph(self, node):
        """
        :type node: Node
        :rtype: Node
        """

        if not node:
            return node

        visited = {}

        # 将题目给定的节点添加到队列
        queue = deque([node])
        # 克隆第一个节点并存储到哈希表中
        visited[node] = Node(node.val, [])

        # 广度优先搜索
        while queue:
            # 取出队列的头节点
            n = queue.popleft()
            # 遍历该节点的邻居
            for neighbor in n.neighbors:
                if neighbor not in visited:
                    # 如果没有被访问过,就克隆并存储在哈希表中
                    visited[neighbor] = Node(neighbor.val, [])
                    # 将邻居节点加入队列中
                    queue.append(neighbor)
                # 更新当前节点的邻居列表
                visited[n].neighbors.append(visited[neighbor])

        return visited[node]
广度优先:
一个一个节点接着复制

在这里插入图片描述
从后向前进行删除操作,就不会越界了

class Solution:
    def removeElement(self, nums, val: int) -> int:
        n=len(nums)
        for i in range(n-1,-1,-1):
            if nums[i]==val:
                nums.pop(i)
        return len(nums)

在这里插入图片描述

class Solution:
    def countAndSay(self, n: int) -> str:
        def find(s):
            a = []
            b = s[0]
            c = 0
            for i in range(len(s)):
                if s[i] == b:
                    c += 1
                else:
                    a.append(str(c))
                    a.append(b)
                    c=1
                    b=s[i]
            a.append(str(c))
            a.append(b)
            d = "".join(a)
            return d
        fin = find("1")
        if n==1:
            return "1"
        for i in range(n-2):
            fin = find(fin)
        return fin

在这里插入图片描述
简单加法计算,和大数加法一样,关键是进位,先补全长度,然后再进行加减,最后看进位

class Solution:
    def addBinary(self, a: str, b: str) -> str:
        a=list(a)
        b=list(b)
        a.reverse()
        b.reverse()
        n1 = len(a)
        n2 = len(b)
        c = []
        d = 0
        if n2 > n1:
            a, b, n1, n2 = b, a, n2, n1
        for i in range(n2):
            tem = (int(a[i]) + int(b[i]) + d) % 2
            d = (int(a[i]) + int(b[i]) + d) // 2
            c.append(str(tem))
        for i in range(n2,n1,1):
            tem = (int(a[i]) + d) % 2
            d = (int(a[i]) + d) // 2
            c.append(str(tem))
        if d == 1:
            c.append(str(d))
        c.reverse()
        return "".join(c)
a = "100"
b="110010"
print(Solution().addBinary(a,b))

或者通过移位(上课讲的方法)

class Solution:
    def addBinary(self, a, b) -> str:
        x, y = int(a, 2), int(b, 2)
        while y:
            answer = x ^ y
            carry = (x & y) << 1
            x, y = answer, carry
        return bin(x)[2:]

在这里插入图片描述
这个题想了好久,用了一个巧妙的方法。
这种不固定范围,需要消耗一些东西来进行改变(横坐标变小),需要遍历找到最大的题一般都要变自己的最差的一边,把两个运动拆解为一个不动一个运动,这个题木桶理论,最差的一边决定自己的上限,而且有消耗的移动
动差的对于动好的一边来说,收益更好 ,

class Solution:
    def maxArea(self, height: List[int]) -> int:
        l, r = 0, len(height) - 1
        ans = 0
        while l < r:
            area = min(height[l], height[r]) * (r - l)
            ans = max(ans, area)
            if height[l] <= height[r]: 
                l += 1
            else:
                r -= 1
        return ans

8.13

在这里插入图片描述
大数的乘法,先计算每个位的乘积,然后最后再统一加减进位

class Solution:
    def multiply(self, num1: str, num2: str) -> str:
        if num1=="0"or num2=="0":
            return "0"
        n1=len(num1)
        n2=len(num2)
        d=0
        a=[0]*(n1+n2-1)
        for i in range(n1-1,-1,-1):
            for j in range(n2-1,-1,-1):
                a[i+j]+=int(num1[i])*int(num2[j])
        for i in range(n1+n2-2,-1,-1):
            tem=(a[i]+d)
            a[i]=str(tem%10)
            d=tem//10                        
        if d>0:
            d=str(d)
            return d+"".join(a)
        return "".join(a)

在这里插入图片描述
可以使用双指针,因为结果固定,所以只要把a+b+c=0转化为a+b=-c
然后再使用双指针来判断

class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:        
        n=len(nums)
        res=[]
        if(not nums or n<3):
            return []
        nums.sort()
        res=[]
        for i in range(n):
            if(nums[i]>0):
                return res
            if(i>0 and nums[i]==nums[i-1]):
                continue
            L=i+1
            R=n-1
            while(L<R):
                if(nums[i]+nums[L]+nums[R]==0):
                    res.append([nums[i],nums[L],nums[R]])
                    while(L<R and nums[L]==nums[L+1]):
                        L=L+1
                    while(L<R and nums[R]==nums[R-1]):
                        R=R-1
                    L=L+1
                    R=R-1
                elif(nums[i]+nums[L]+nums[R]>0):
                    R=R-1
                else:
                    L=L+1
        return res

还可以尝试使用记忆化搜索,也可以叫做回溯

class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        if not nums: return []
        # 先排序,关键!
        nums.sort()     
        ans = set()
        N, target = 3, 0
        self._find_sum(nums, 0, N, target, [], ans)
        return list(ans)
    def _find_sum(self, nums, start, N, target, path, ans):
        # terminator
        if len(nums) < N or N < 2: return
        # process
        if N == 2:
            # 两数求和
            d = set()
            for j in range(start, len(nums)):
                if target - nums[j] in d:
                    ans.add(tuple(path + [target - nums[j], nums[j]]))
                else:
                    d.add(nums[j])
        else:
            for i in range(start, len(nums)):
                # 剪枝1: target比剩余数字能组成的最小值还要小 或 比能组成的最大值还要大,就可以停止循环了
                if target < nums[i] * N or target > nums[-1] * N: break
                # 剪枝2: 去重
                if i > start and nums[i] == nums[i - 1]: continue
                # drill down
                self._find_sum(nums, i + 1, N - 1, target - nums[i], path + [nums[i]], ans)
        return

class Solution:#普通记忆化搜索,会超时,并未剪枝
    def threeSum(self, nums) :
        nums=sorted(nums)
        res=[]
        n=len(nums)
        def find(nums,use):
            if sum(use) == 0 and len(use)==3:
                res.append(use)
                return
            if sum(use) > 0 or len(use)>2: return
            for i in range(len(nums)):
                if i > 0 and nums[i] == nums[i - 1]:
                    continue
                find(nums[i + 1:], use + [nums[i]])
            return res
        return  find(nums,[])

记忆化搜索题都是这一种套路
套路总结2
在这里插入图片描述
nums1安排一个指针,如果比二大就换,nums2也安排一个指针, 每次上下交换之后对二重新排序定位,直到m次之后,将2附在后边即可
修改:
可以从后往前来加,这样就不用对2进行排序,因为后边的大

class Solution:
    def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None:
        a=n+m-1
        n-=1
        m-=1
        while n>=0:
            if m>=0 and nums1[m]>nums2[n]:
                nums1[a]=nums1[m]
                m-=1
            else:
                nums1[a]=nums2[n]
                n-=1
            a-=1

在这里插入图片描述
在这里插入图片描述

class Solution:
    def mySqrt(self, x: int) -> int:
        if x == 0:
            return 0
        ans = int(math.exp(0.5 * math.log(x)))
        return ans + 1 if (ans + 1) ** 2 <= x else ans

剩下的有二分法,高中课本学过,设定上界下界,然后每次减小一半

class Solution:
    def mySqrt(self, x: int) -> int:
        l, r, ans = 0, x, -1
        while l <= r:
            mid = (l + r) // 2
            if mid * mid <= x:
                ans = mid
                l = mid + 1
            else:
                r = mid - 1
        return ans

在这里插入图片描述
一看最大序列啥的,肯定dp没跑了,刚总结了这类的题怎么做,正好试试手

class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        n=len(nums)
        dp=[0 for _ in range(n)]
        dp[0]=nums[0]
        for i in range(1,n):
            if nums[i]>nums[i]+dp[i-1]:
                dp[i]=nums[i]
            else:
                dp[i]=nums[i]+dp[i-1]
        return max(dp)

8.19

在这里插入图片描述
这个也是动态规划,没跑了写呗

class Solution:
    def countSubstrings(self, s: str) -> int:
        # dp[i][j] 代表 子串[i, j] 是否是一个 回文串
        n = len(s)
        dp = [[False] * n for _ in range(n)]
        count = 0
        # 枚举所有可能 因为代表子串 所以 i <= j
        for j in range(n):
            for i in range(0, j + 1):
                # 子串长度
                length = j - i + 1
                # 只有一个字符 直接就是一个回文串
                if length == 1:
                    dp[i][j] = True
                    count += 1
                # 两个字符 只有相等才是回文串
                if length == 2 and s[i] == s[j]:
                    dp[i][j] = True
                    count += 1
                # 超过两个字符 首位相同 且除去首尾的子串是回文串 才是回文串
                if length > 2 and s[i] == s[j] and dp[i+1][j-1] is True:
                    dp[i][j] = True
                    count += 1
        return count
class Solution:#中心扩展法
    def countSubstrings(self, s: str) -> int:
        def check(s,l,r):
            num=0
            while l>=0 and r<len(s) and s[l]==s[r]:
                l-=1
                r+=1
                num+=1
            return num
        num=0
        for i in range(len(s)):
            num+=check(s,i,i)#以本节点为中心
            if i==len(s)-1:
                continue
            num+=check(s,i,i+1)#以本节点和下一个节点为中心
        return num

在这里插入图片描述

class Solution:
    def getRow(self, rowIndex: int) :
        dp=[[0]*i  for i in range(3,rowIndex+4)]
        dp[0][1]=1
        if rowIndex>0:
            dp[1][1],dp[1][2]=1,1
        for i in range(2,rowIndex+1):
            for j in range(1,i+2):
                dp[i][j]=dp[i-1][j-1]+dp[i-1][j]
        return dp[-1][1:-1]
print(Solution().getRow(0))

在这里插入图片描述
链表的环操作一般都用快慢指针,为空弹出则没环,如果快慢指针碰到了,就是有环

class Solution:
    def hasCycle(self, head: ListNode) -> bool:
        slow = fast = head
        # if not head:  # 没必要这样写可以加入while循环判断更简洁
        #     return False
        while fast and fast.next:  # 防止head为空和出现空指针的next的情况
            slow = slow.next
            fast = fast.next.next
            if slow is fast:
                return True
        return False

8.20

在这里插入图片描述
扫雷老游戏了,这个题是说其中的一步会造成什么样的后果,而不是扫完,扫完的话,有些地方不能判断,不多BB,
看题意,第一个选项,挖到雷,直接炸就完事了
第二个选项和第三个选项意味着挖到空需要递归,如果旁边有雷,则显示数字,如果没有则变为‘B’

class Solution:
    def updateBoard(self, board, click):
        n1=len(board)
        n2=len(board[0])
        a = click[0]
        b = click[1]
        if board[a][b] == "M":
            board[a][b] = "X"
            return board
        def confirm(i,j):
            res = 0
            for x in [1, -1, 0]:
                for y in [1, -1, 0]:
                    if x == 0 and y == 0: continue
                    if 0 <= i + x < n1 and 0 <= j + y < n2 and board[i + x][j + y] == "M": res += 1
            return res
        def find(i,j):
            num = confirm(i, j)
            if num > 0:
                board[i][j] = str(num)
                return
            board[i][j] = "B"
            for x in [1, -1, 0]:
                for y in [1, -1, 0]:
                    if x == 0 and y == 0: continue
                    nxt_i, nxt_j = i + x, j + y
                    if 0 <= nxt_i < n1 and 0 <= nxt_j < n2 and board[nxt_i][nxt_j] == "E": find(nxt_i, nxt_j)
        find(a, b)
        return board

在这里插入图片描述
简单方式:集合和的二倍减去数组和
或者二进制异或,遍历开始异或,最后输出的就是一个的

class Solution:
    def singleNumber(self, nums: List[int]) -> int:
        t=0
        for i in nums:
            t=t^i
        return t

在这里插入图片描述
记录遍历到的最低值,如果下个值大,就计算差值,下个值比最小的小,就重新标记最小值

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        inf = int(1e9)
        minprice = inf
        maxprofit = 0
        for price in prices:
            maxprofit = max(price - minprice, maxprofit)
            minprice = min(price, minprice)
        return maxprofit

在这里插入图片描述
可以这么看,如果下一个要降价,那么铁卖了。如果下一个涨价,那么今天的就买。
所以需要两个指针,然后判断

class Solution:
    def maxProfit(self, prices) -> int:
        n = len(prices)
        sum = 0
        for i in range(1, n):
            if prices[i]>prices[i-1]:
                sum+=prices[i]-prices[i-1]
        return sum

8.21

在这里插入图片描述
先深度遍历到最底,每次返回值,最后取最小即可

class Solution:
    def minDepth(self, root: TreeNode) -> int:
        if not root:
            return 0
        if not root.left and not root.right:
            return 1
        min_depth = 10**9
        if root.left:
            min_depth = min(self.minDepth(root.left), min_depth)
        if root.right:
            min_depth = min(self.minDepth(root.right), min_depth)
        return min_depth + 1

在这里插入图片描述
先取中间的值,然后再建立二叉树,每次都建立左孩子节点,如果想要让高度最小的话,可以递归调用,每次都建立左右节点(用数组中间的值当做根节点)

class Solution:
    def sortedArrayToBST(self, nums: List[int]) -> TreeNode:
        def helper(left, right):
            if left > right:
                return None
            # 总是选择中间位置左边的数字作为根节点
            mid = (left + right) // 2
            root = TreeNode(nums[mid])
            root.left = helper(left, mid - 1)
            root.right = helper(mid + 1, right)
            return root
        return helper(0, len(nums) - 1)

8.30

在这里插入图片描述
先用空格分块,最后再组合

class Solution:
    def reverseWords(self, s: str) -> str:
        a=s.split( )
        t=""
        for i in a:
            for j in range(len(i)-1,-1,-1):
                t=t+i[j]
            t=t+" "
        return t[:-1]

在这里插入图片描述
将两个字符串连在一起,去掉头尾,如果还在里边,则肯定是重复的

class Solution:
    def repeatedSubstringPattern(self, s: str) -> bool:
        return (s + s).find(s, 1) != len(s)

或者使用KPM算法(带有固定表格的判断字符串相同)

class Solution:
    def repeatedSubstringPattern(self, s: str) -> bool:
        def kmp(pattern: str) -> bool:
            n = len(pattern)
            fail = [-1] * n
            for i in range(1, n):
                j = fail[i - 1]
                while j != -1 and pattern[j + 1] != pattern[i]:
                    j = fail[j]
                if pattern[j + 1] == pattern[i]:
                    fail[i] = j + 1
            return fail[n - 1] != -1 and n % (n - fail[n - 1] - 1) == 0        
        return kmp(s)

8.31

在这里插入图片描述
这个就是图的遍历,类似一笔画完啥啥啥
思路:使用两个集合,一个作为访问集合,一个作为访问过集合,然后每次先将访问到的数据加入访问集合,访问过的加入访问过集合,访问新的要在访问集合,但不能在访问过集合,访问完集合所有内容后,判断访问集合元素个数是否与数组长度(有0这个元素)相同,相同则true,不同则False

class Solution:
    def canVisitAllRooms(self, rooms) -> bool:
        n=len(rooms)
        past=set()
        now=set()
        now.add(0)
        t=0
        while t>=0:
            tem=set(rooms[t])
            now.update(tem.symmetric_difference(past))
            past.add(t)
            tem1=now.symmetric_difference(past)
            if len(tem1)==0:
                if len(now)==n:
                    return True
                else:
                    return False
            for i in tem1:
                t=i
                break

在这里插入图片描述
先删去其他不是字母和数字的字符,然后全变成小写,最后再判断是否是回文串

class Solution:
    def isPalindrome(self, s: str) -> bool:
        a=""
        if s.isspace():
            return True
        s=s.lower()
        for i in s:
            if 'a'<=i<='z' or '0'<=i<='9':
                a=a+i
        n=len(a)
        if n % 2==1:
            b=a[0:n//2]
            c=a[n//2+1:n]
        else:
            b=a[0:n//2]
            c=a[n//2:n]
        d=b[::-1]
        if d==c:
            return True
        else:
            return False

使用字符串的isalnum函数,可以直接判断选中的字符是否是数字或字符,然后遍历判断

class Solution:
    def isPalindrome(self, s: str) -> bool:
        sgood = "".join(ch.lower() for ch in s if ch.isalnum())
        return sgood == sgood[::-1]

还可以使用双指针

class Solution:
    def isPalindrome(self, s: str) -> bool:
        n = len(s)
        left, right = 0, n - 1
        
        while left < right:
            while left < right and not s[left].isalnum():
                left += 1
            while left < right and not s[right].isalnum():
                right -= 1
            if left < right:
                if s[left].lower() != s[right].lower():
                    return False
                left, right = left + 1, right - 1
        return True

在这里插入图片描述
这种带循环的题都可以使用快慢指针,追击问题,慢指针运行一遍之前都可以追到(步进的问题)步进一定要互质
这个题有特殊解法,如果循环的话,只有一个序列
{4, 16, 37, 58, 89, 145, 42, 20}
所以数能不能到这个序列,决定这个数之后是1还是循环

class Solution:
    def isHappy(self, n: int) -> bool:
        a=[]
        while n!=1:
            s=str(n)
            n=0
            for i in s:
                n+=int(i)*int(i)
            if s in a:
                return False
            a.append(s)
        return True

总结

八月的力扣就这些,现在反回去看还有好多不是很熟,之前刷题为了尽快熟悉代码,熟悉思路,有些东西并没有总结,而是采用原始思路或者不规范,不合理的思路,现在看,有好多题都是固定套路,所以,等到再刷多一点,就要总结一下题的类型和套路,然后再用这些总结的结果来做题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Enginer静态力

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

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

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

打赏作者

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

抵扣说明:

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

余额充值