[Python笔记] 剑指offer刷题记录——进度50/75

剑指offer刷题记录

LeetCode上的剑指offer题
刷题ing

26.树的子结构

#1.双递归
class Solution:
    def isSubStructure(self, A: TreeNode, B: TreeNode) -> bool:
    	'''
    	先序遍历树A中的每个节点An,对应函数isSubStructure(A, B)
    	判断树A中以An为根节点的子树是否包含树B,对应函数helper(A, B)
    	'''
        #有点双递归的意思
        #是在说子结构,没说子树,B遍历空之后还可以有A的子节点
        if not A or not B:
            #空树不是任意一个树的子结构
            return False
        def helper(A,B):
            if not B:
                return True
            if not A:
                return False
            if A.val==B.val:
                return helper(A.left,B.left) and helper(A.right,B.right)
            else:
                return False
        return helper(A,B) or self.isSubStructure(A.left,B) or self.isSubStructure(A.right,B)#找起始点

27.二叉树的镜像

#1.递归
class Solution:
    def mirrorTree(self, root: TreeNode) -> TreeNode:
        if not root:
            return None
        root1 = root.left #存一下左节点之后调用
        root.left = self.mirrorTree(root.right)
        root.right = self.mirrorTree(root1)
        return root
#2.队列
import collections
class Solution:
    def mirrorTree(self, root: TreeNode) -> TreeNode:
        q = collections.deque()
        q.append(root)
        while q:
            node = q.popleft()
            if not node:
                continue
            #子树直接交换
            tmp = node.left
            node.left=node.right
            node.right = tmp
            if node.left:
                q.append(node.left)
            if node.right:
                q.append(node.right)
        return root
#3.栈模拟队列
class Solution:
    def mirrorTree(self, root: TreeNode) -> TreeNode:
        s = []
        s.append(root)
        while s:
            node = s[0]
            s.pop(0)
            if not node:
                continue
            #子树直接交换
            tmp = node.left
            node.left=node.right
            node.right = tmp
            if node.left:
                s.append(node.left)
            if node.right:
                s.append(node.right)
        return root

28.nn对称的二叉树

class Solution:
    def isSymmetric(self, root: TreeNode) -> bool:
        #要看子树想不相同
        def helper(left,right):
            if not left and not right:
                return True
            if not left and right or left and not right:
                return False
            if left.val!=right.val:
                #比当前节点的值
                return False
            return helper(left.left,right.right) and helper(left.right,right.left)
        return helper(root,root)

29.顺时针打印矩阵

#1.老实人写法,左-右-下-左-上,保持每一行列移动的边界更新
class Solution:
    def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
        if not matrix:
            return []
        ans = []
        m = len(matrix)
        n = len(matrix[0])
        l = 0
        r = n-1
        up = 0
        down = m-1
        while True:
            #左→右
            ans+=[matrix[up][i] for i in range(l,r+1)]
            up += 1
            if up>down:
                break
            #上↓下
            ans+=[matrix[i][r] for i in range(up,down+1)]
            r -= 1
            if r<l:
                break
            #右←左
            ans+=[matrix[down][i] for i in range(r,l-1,-1)]
            down -= 1
            if up>down:
                break
            #下↑上
            ans+=[matrix[i][l] for i in range(down,up-1,-1)]
            l += 1
            if l>r:
                break
        return ans
#2.Py
class Solution:
    def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
        ans = []
        while matrix:
            ans += list(matrix.pop(0))#取出第一行
            matrix = list(zip(*matrix))[::-1]#逆时针rot90度
            # A[::-1]这个操作对于行向量可以左右翻转;对于二维矩阵可以实现上下翻转
        return ans

30.包含min函数的栈

class MinStack:
    def __init__(self):
        """
        initialize your data structure here.
        """
    #这就是那个吧,设置最小栈的那个
        self.minstack = [float('inf')]
        self.stack = []


    def push(self, x: int) -> None:
        self.stack.append(x)
        self.minstack.append(min(x,self.minstack[-1]))#只加入当前最小值

    def pop(self) -> None:
        self.stack.pop()
        self.minstack.pop()

    def top(self) -> int:
        return self.stack[-1]

    def min(self) -> int:
        return self.minstack[-1]
# Your MinStack object will be instantiated and called as such:
# obj = MinStack()
# obj.push(x)
# obj.pop()
# param_3 = obj.top()
# param_4 = obj.min()

31.栈的压入、弹出序列

class Solution:
    def validateStackSequences(self, pushed: List[int], popped: List[int]) -> bool:
        stack = []
        #压栈试试
        cnt=0
        for i in range(len(pushed)):
            stack.append(pushed[i])
            while stack and stack[-1]==popped[cnt]:
                stack.pop()
                cnt+=1
        return cnt==len(popped)

32-1.从上到下打印二叉树

#1.队列
import collections
class Solution:
    def levelOrder(self, root: TreeNode) -> List[int]:
        #层序遍历
        q = collections.deque()
        q.append(root)
        ans = []
        while q:
            for i in range(len(q)):
                node = q.popleft()
                if not node:
                    continue
                ans.append(node.val)
                if node.left:
                    q.append(node.left)
                if node.right:
                    q.append(node.right)
        return ans
#2.递归
from typing import List
from functools import reduce
from operator import iconcat

class Solution:
    def levelOrder(self, root: TreeNode) -> List:
        #递归,记录深度,并将同层节点放到同一个数组中
        trees = []
        self.levelOrderWithDepth(root, 0, trees)
        return reduce(iconcat, trees, [])#reduce() 函数会对参数序列中元素进行累积。

    def levelOrderWithDepth(self, root: TreeNode, depth: int, trees: [[int]]):
        if not root or depth < 0:
            return
        while len(trees) <= depth:
            trees.append([])
        trees[depth].append(root.val)
        self.levelOrderWithDepth(root.left, depth + 1, trees)
        self.levelOrderWithDepth(root.right, depth + 1, trees)
#3.普通递归也
from operator import iconcat
class Solution:
    def levelOrder(self, root: TreeNode) -> List[int]:
        ans = []
        def helper(node,depth,ans):
            if not node:
                return 
            if len(ans)<=depth:
                ans.append([])
            ans[depth].append(node.val)
            helper(node.left,depth+1,ans)
            helper(node.right,depth+1,ans)
        helper(root,0,ans)
        return reduce(iconcat, ans, [])

32-2.从上到下打印二叉树 II

#1.dfs
class Solution:
    def levelOrder(self, root: TreeNode) -> List[List[int]]:
        ans = []
        def helper(node,depth,ans):
            if not node:
                return 
            if len(ans)<=depth:
                ans.append([])
            ans[depth].append(node.val)
            helper(node.left,depth+1,ans)
            helper(node.right,depth+1,ans)
        helper(root,0,ans)
        return ans
#2.bfs
import collections
class Solution:
    def levelOrder(self, root: TreeNode) -> List[List[int]]:
        ans = []
        if not root:
            return ans
        q = collections.deque()
        q.append(root)
        while q:
            tmp = []
            for i in range(len(q)):
                node = q.popleft()
                if not node:
                    continue
                tmp.append(node.val)
                if node.left:
                    q.append(node.left)
                if node.right:
                    q.append(node.right)
            ans.append(tmp)
        return ans

32-3.从上到下打印二叉树 III

#1.dfs_换汤不换药
class Solution:
    def levelOrder(self, root: TreeNode) -> List[List[int]]:
        ans = []
        def helper(node,depth,ans):
            if not node:
                return 
            if len(ans)<=depth:
                ans.append([])
            ans[depth].append(node.val)
            helper(node.left,depth+1,ans)
            helper(node.right,depth+1,ans)
        helper(root,0,ans)
        for depth in range(len(ans)):
            if depth%2==1:
            	#奇数行反过来输出就okk
                ans[depth] = ans[depth][::-1]
        return ans
#2.bfs_同理
import collections
class Solution:
    def levelOrder(self, root: TreeNode) -> List[List[int]]:
        ans = []
        if not root:
            return ans
        q = collections.deque()
        q.append(root)
        now_depth=0
        while q:
            tmp = []
            for i in range(len(q)):
                node = q.popleft()
                if not node:
                    continue
                tmp.append(node.val)
                if node.left:
                    q.append(node.left)
                if node.right:
                    q.append(node.right)
            if now_depth%2==1:
                ans.append(tmp[::-1])
            else:
                ans.append(tmp)
            now_depth+=1
        return ans

33.二叉搜索树的后序遍历序列

#1.递归
class Solution:
    def verifyPostorder(self, postorder: List[int]) -> bool:
        #后序的话,最后一个是root,没法建立唯一的树,不知道左右子树的长度
        #往回找,比root大就在右子树,比root小就在左子树,前边开始遍历即可
        def helper(pos):
            if len(pos)<=1:
                #到了子节点了
                return True
            root = pos[-1]
            for i in range(len(pos)):
                if pos[i]>root:
                    break
            for j in range(i,len(pos)-1):
                if pos[j]<root:
                    return False
            return helper(pos[:i]) and helper(pos[i:-1])
        if not postorder:
            return True
        return helper(postorder)
#2.单调栈辅助迭代
#越往右越大,这样,就可以构造一个单调递增的栈,来记录遍历的元素。
#往右子树遍历的过程,value是越来越大的,一旦出现了value小于栈顶元素value的时候,
#就表示要开始进入左子树了(如果不是,就应该继续进入右子树,否则不满足二叉搜索树的定义
class Solution:
    def verifyPostorder(self, postorder: [int]) -> bool:
        stack, root = [], float("+inf")
        for i in range(len(postorder) - 1, -1, -1):
            if postorder[i] > root: return False
            while(stack and postorder[i] < stack[-1]):
                #找到左子树了,右子树的节点和root都丢出来
                root = stack.pop()#查看左子树,当前为root
            stack.append(postorder[i])
        return True

34.二叉树中和为某一值的路径

#1.标准dfs
'''
用dfs的时候注意记录path的数组变化,应该在判断前添加val,到底时判断,到底且不属于的话记得pop不然下一个if的时候
数组值会变,path添加到ans里的时候记得copy,只是变量名的话是贴标签,之后会变化的!!!
'''
class Solution:
    def pathSum(self, root: TreeNode, target: int) -> List[List[int]]:
        #不是BST,dfs
        if not root:
            return []
        ans = []
        def helper(root,nums):
            nums.append(root.val)
            if not root.left and not root.right:
                #到底
                summ=sum(nums)
                #print(nums)
                if summ==target:
                    ans.append(nums[:])#nums要拷贝,不能是贴着变量名标签不然会随整体变化最后清空
            if root.left:
                helper(root.left,nums)
            if root.right:
                helper(root.right,nums)
            nums.pop()#走到底且不满足的话要清理nums防止进入下一个if时nums变化了
        helper(root,[])
        return ans

35.复杂链表的复制

浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存。但深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。

#1.Hash
#On,On
class Solution:
    def copyRandomList(self, head: 'Node') -> 'Node':
        #hash
        if not head:
            return head
        memo = {None:None}#不能用dict()这样初始化,不然需要判断q.random!=None,毕竟dict的key一般不能为None
        cur = head
        #hashmap里存储(原节点,copy节点)的映射
        while cur:
            memo[cur] = Node(cur.val)
            cur = cur.next
        cur = head
        #映射的值与值连接,节点组成新的链表
        while cur:
            memo[cur].next = memo[cur.next]
            memo[cur].random = memo[cur.random]
            cur = cur.next
        return memo[head]
#2.原地修改(间隔节点法)
#On,O1
class Solution:
    def copyRandomList(self, head: 'Node') -> 'Node':
        #原地修改
        if not head:
            return head
        cur = head
        copy = None
        #将拷贝节点放到原节点后面,例如1->2->3这样的链表就变成了这样1->1'->2->2'->3->3'
        while cur:
            copy = Node(cur.val)
            copy.next = cur.next
            cur.next = copy
            cur = cur.next.next
        cur = head
        #把拷贝节点的random指针安排上
        #复制的random就是上一个节点的random的next
        while cur:
            if cur.random:
                cur.next.random = cur.random.next#此时cur.next就都是copy节点了
            cur = cur.next.next
        newhead = head.next#copy第一个节点位置
        #分离拷贝节点和原节点,变成1->2->3和1'->2'->3'两个链表,后者就是答案
        #想拉拉链一样,咔咔咔咔咔咔
        cur = head
        tmp = None
        while cur and cur.next:
            tmp = cur.next
            cur.next = tmp.next
            cur = tmp
        return newhead
#3.dfs
#本质与hashmap很类似
class Solution:
    def copyRandomList(self, head: 'Node') -> 'Node':
        def dfs(head):
            if not head: return None
            if head in visited:
                return visited[head]#return copy的节点
            # 创建新结点
            copy = Node(head.val, None, None)
            visited[head] = copy
            copy.next = dfs(head.next)#连到dfs的copy节点上
            copy.random = dfs(head.random)
            return copy
        visited = {}
        return dfs(head)

36.二叉搜索树与双向链表

#1.递归中序+头结点设定+最后建立循环
class Solution:
    def treeToDoublyList(self, root: 'Node') -> 'Node':
        def dfs(cur):
            if not cur: return
            dfs(cur.left) # 递归左子树
            if self.pre: # 修改节点引用
                self.pre.right, cur.left = cur, self.pre
            else: # 记录头节点
                self.head = cur
            self.pre = cur # 保存 cur
            dfs(cur.right) # 递归右子树
        
        if not root: return
        self.pre = None
        dfs(root)
        self.head.left, self.pre.right = self.pre, self.head#首尾连接
        return self.head
#2.迭代中序+建立哑结点标记头部,首尾连接跳过dummy
class Solution:
    def treeToDoublyList(self, root: 'Node') -> 'Node':
        #双向链表也和tree一样,是left和right标向
        #升序的话就是前=中序遍历
        if not root:
            return None
        dummy = Node(0)
        last = dummy
        stack = [(0,root)]
        while stack:
            opt,node = stack.pop()
            if not node:
                continue
            if opt==1:
                #print(last.right,dummy.right)#神奇的是第一次last赋值后dummy被挂在了头结点左边,标签行为转移到本体上
                #浅拷贝只是增加了一个指针指向已经存在的内存,而深拷贝就是增加一个指针并且申请一个新的内存
                #按道理来讲用none来init加个判断连到head上会比较好
                last.right = node
                node.left = last
                last = node
            else:#更早的关系会被提前压入栈
                stack.append((0,node.right))
                stack.append((1,node))
                stack.append((0,node.left))
        dummy.right.left = last
        last.right= dummy.right
        return dummy.right

37.序列化二叉树

#1.井号层序遍历隔开+恢复时注意边界,队列的元素对应一左一右俩

class Codec:

    def serialize(self, root):
        """Encodes a tree to a single string.
        
        :type root: TreeNode
        :rtype: str
        """
        q = collections.deque() 
        ans = []
        q.append(root)
        while q:
            for i in range(len(q)):
                node = q.popleft()
                if not node:
                    ans.append('null')
                    continue
                ans.append(str(node.val))
                q.append(node.left)
                q.append(node.right)
        return "#".join(ans)
        

    def deserialize(self, data):
        data = data.split("#")
        #print(data)
        if data[0]=='null':
            return None
        q = collections.deque()
        root = TreeNode(int(data[0]))
        q.append(root)
        i = 1
        while q:
            node = q.popleft()
            if not node:
                continue
            node.left = TreeNode(int(data[i])) if data[i]!='null' else None
            node.right = TreeNode(int(data[i+1])) if data[i+1]!='null' else None
            i += 2
            q.append(node.left)
            q.append(node.right)
        return root


38.字符串的排列

#1.dfs+字符串先排序,重复字符不在同一位置进行dfs
class Solution:
    def permutation(self, s: str) -> List[str]:
        ans = []
        def dfs(comb,s):
            if len(s) == 0:
                #一个单词满了
                ans.append("".join(comb))
                return
            else:
                #一个单词没填满
                for i in range(len(s)):
                    if i==0:
                        dfs(comb+[s[i]],s[:i]+s[i+1:])
                    if i>0 and s[i]!=s[i-1]:
                        dfs(comb+[s[i]],s[:i]+s[i+1:])
            
        s = "".join(sorted(s))
        dfs([],s)
        return ans
#2.dfs用dict、set一类剪枝
class Solution:
    def permutation(self, s: str) -> List[str]:
        c, res = list(s), []
        def dfs(x):
            if x == len(c) - 1:
                res.append(''.join(c)) # 添加排列方案
                return
            dic = set()
            for i in range(x, len(c)):
                if c[i] in dic: continue # 重复,因此剪枝
                dic.add(c[i])
                c[i], c[x] = c[x], c[i] # 交换,将 c[i] 固定在第 x 位
                dfs(x + 1) # 开启固定第 x + 1 位字符
                c[i], c[x] = c[x], c[i] # 恢复交换
        dfs(0)
        return res

39.数组中出现次数超过一半的数字

#1.hashmap
class Solution:
    def majorityElement(self, nums: List[int]) -> int:
        n = len(nums)//2
        memo = {}
        for i in range(len(nums)):
            memo[nums[i]] = memo.get(nums[i],0)+1
            if memo[nums[i]]>n:
                return nums[i]
#2.排序后一定在中间
class Solution:
    def majorityElement(self, nums: List[int]) -> int:
        nums.sort()
        return nums[len(nums)//2]
#3.摩尔投票法
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

40. 最小的k个数

#1.Pysort
class Solution:
    def getLeastNumbers(self, arr: List[int], k: int) -> List[int]:
        arr.sort()
        return arr[:k]
#2.写个快排
class Solution:
    def getLeastNumbers(self, arr: List[int], k: int) -> List[int]:
        #使用 partition 过程找到下标为 k - 1 的那个数即可
        def quicksort(nums,l,r,target):
            if l<r:
                i = l
                j = r
                key = nums[l]
                while i<j:
                    while i<j and nums[j]>=key:
                        j-=1
                    if i<j:
                        nums[i]=nums[j]
                        i+=1
                    while i<j and nums[i]<=key:
                        i+=1
                    if i<j:
                        nums[j]=nums[i]
                        j-=1
                nums[i]=key
                if i<target:
                    quicksort(nums,i+1,r,target)
                else:
                    quicksort(nums,l,i-1,target)
        quicksort(arr,0,len(arr)-1,k-1)
        return arr[:k]
#3.写个堆
class Solution:
    def getLeastNumbers(self, arr: List[int], k: int) -> List[int]:
        if k == 0:
            return list()

        hp = [-x for x in arr[:k]]#因为py是最小堆所以取反数,除掉k以上大的值,之后的堆取反数输出,堆大小为k
        heapq.heapify(hp)
        for i in range(k, len(arr)):
            if -hp[0] > arr[i]:
                heapq.heappop(hp)
                heapq.heappush(hp, -arr[i])
        ans = [-x for x in hp]
        return ans


41.数据流中的中位数

#1.Pysort
class MedianFinder:

    def __init__(self):
        """
        initialize your data structure here.
        """
        self.stack = []

    def addNum(self, num: int) -> None:
        self.stack.append(num)
        self.stack.sort()

    def findMedian(self) -> float:
        if len(self.stack)%2==0:
            mid = len(self.stack)//2
            return (self.stack[mid-1]+self.stack[mid])/2
        else:
            mid = len(self.stack)//2
            return self.stack[mid]



# Your MedianFinder object will be instantiated and called as such:
# obj = MedianFinder()
# obj.addNum(num)
# param_2 = obj.findMedian()

42.连续子数组的最大和

#1.dpdp
		'''
        如果和是负数,那就从里边选个小点的负数:重新计数的环节
        如果和是正数,那就先记录下来在扩大范围看看有没有更大的正数
        '''
class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        #dpdp
        mm = nums[0]
        dp = [0]*(len(nums))
        dp[0]=nums[0]
        for i in range(1,len(nums)):
            dp[i] = max(nums[i],dp[i-1]+nums[i])#dp[i-1]有可能小于0
            #dp[i-1]<0的场合:这里dp存的并不是当前最大和,而是看是否从i重新开始计数
            #dp[i-1]>0的场合:先记下带nums[i]的,无论nums[i]为正负,mm负责记录max值
            if dp[i]>mm:
                mm = dp[i]
        return mm
#2.dpdp_Py空间优化
class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        for i in range(1, len(nums)):
            nums[i] += max(nums[i - 1], 0)#在nums数组上改,O1,到i时,使用的i还没变化,i-1记录dp的
        return max(nums)
#2.大根堆+小根堆
import heapq
class MedianFinder:
    '''
    把数据分为两部分,让左半部分永远元素个数永远大于等于右半部分,
    这样左端大顶堆的堆顶就是左半部分最大值,右端小顶堆的堆顶就是右半部分最小值。
    '''
    
    def __init__(self):
        self.right = [] # 小顶堆,保存较大的一半
        self.left = [] # 大顶堆,保存较小的一半,负的小顶堆

    def addNum(self, num: int) -> None:
        '''
        Push item on the heap, then pop and return the smallest item from the heap. 
        The combined action runs more efficiently than heappush() followed by a separate call to heappop().
        当 m = n(即N为偶数):需向A添加一个元素。实现方法:将新元素num插入至B,再将B堆顶元素插入至A ;
		当 N为奇数:需向B添加一个元素。实现方法:将新元素num插入至A,再将 A堆顶元素插入至B ;
		if len(self.A) != len(self.B):
            heappush(self.A, num)
            heappush(self.B, -heappop(self.A))
        else:
            heappush(self.B, -num)
            heappush(self.A, -heappop(self.B))
        '''
        if len(self.left) != len(self.right):
            #奇数?
            heappush(self.left, -heappushpop(self.right, num))
        else:
            #偶数?
            heappush(self.right, -heappushpop(self.left, -num))

    def findMedian(self) -> float:
        #永远是right多存一点,left是负数注意
        return self.right[0] if len(self.right) != len(self.left) else (self.right[0] - self.left[0]) / 2.0

43.1~n整数中1出现的次数

#1.找规律,位数从低到高
'''
当 cur = 0时: 此位 1 的出现次数只由高位 high和位数决定:high*i
当 cur = 1时: 此位 1 的出现次数由高位 high ,位数和低位 low 决定,计算公式为:high×digit+low+1相当于整数0以下的部分以外多了一个1以及对应低位带来的部分
当 cur=2,3,⋯,9 时: 此位 1 的出现次数只由高位 high 决定,计算公式为:(high+1)×digit,0以上1的位数个肯定是有了
'''
class Solution:
    def countDigitOne(self, n: int) -> int:
        cnt = 0
        i = 1
        while n//i !=0:
            high = n//(10*i)#位数,高位
            cur = (n//i)%10#余数,当前位i
            low = n-(n//i)*i#除法取整残差,低位
            if cur==0:
                cnt+=high*i
            elif cur==1:
                cnt+=high*i+low+1
            else:
                cnt+=(high+1)*i
            i = i*10
        return cnt
#2.递归,位数从高到低
class Solution:
    def countDigitOne(self, n: int) -> int:
        return self.dfs(n)
    def dfs(self,n):
        if n<=0: return 0

        num_s = str(n) 
        high = int(num_s[0])  
        Pow = 10**(len(num_s)-1) 
        last = n - high*Pow
        
        if high == 1:
            return self.dfs(Pow-1)+self.dfs(last)+last+1
        else:
            return Pow+high*self.dfs(Pow-1)+self.dfs(last)

44.数字序列中某一位的数字

class Solution:
    def findNthDigit(self, n: int) -> int:
        i=0
        last = 0
        while n>0:
            last = n
            n-=(10**(i))*9*(i+1)
            i+=1
        #i就是当前所求数的位数,恢复n到循环的位置
        n = last
        start = 10**(i-1)
        end = str(start+(n-1)//i)
        num = end[(n-1)%i]#n-1的话就是从新的数开始计位了,从start开始个数和计位都是
        return int(num)

45.把数组排成最小的数

#本来想用全排列选一下最小值的,但是测试用例比intmaxsize还大,这也就是说需要字符串比较大小
#1.字符串比较大小
#sorted key 不仅可以传lamda , 普通函数 , 还可以传有实现比较的类
class smallnumkey(str):
    def __lt__(x,y):
        return x+y<y+x
class Solution:
    def minNumber(self, nums: List[int]) -> str:
        ans = "".join(sorted(map(str,nums),key=smallnumkey))
        return ans
#2.还是比较大小
#没用class,用了快排
class Solution:
    def minNumber(self, nums: List[int]) -> str:
        def fast_sort(l , r):
            if l >= r: return
            i, j = l, r
            while i < j:
                while strs[j] + strs[l] >= strs[l] + strs[j] and i < j: j -= 1
                while strs[i] + strs[l] <= strs[l] + strs[i] and i < j: i += 1
                strs[i], strs[j] = strs[j], strs[i]
            strs[i], strs[l] = strs[l], strs[i]
            fast_sort(l, i - 1)
            fast_sort(i + 1, r)
        
        strs = [str(num) for num in nums]
        fast_sort(0, len(strs) - 1)
        return ''.join(strs)

46.把数字翻译成字符串

#1.dpdp
#边界条件注目
#小青蛙爬楼梯类问题
#还可以进一步空间优化就是了,-2:prepre,-1:pre,0:now这样三个值存储节省空间
class Solution:
    def translateNum(self, num: int) -> int:
        #分组,检查是否valid,+1
        ans = 1#单数字字母 #双数字字母 #感觉很dpdp
        num = str(num)
        dp = [1]*(len(num))
        if len(num)>=2 and int(num[0]+num[1])<=25:
            dp[1]=2
        for i in range(2,len(num)):
            tmp = int(num[i-1]+num[i])
            if tmp<=25 and tmp>=10:
                dp[i] = dp[i-1]+dp[i-2]#1:和前一个数组成两位的字符,2:单独作为新的一个字符
            else:
                dp[i] = dp[i-1]#没法和前一个数组成两位的字符,拼法没有增多
        return dp[-1]
#2.递归
class Solution:
    def translateNum(self, num: int) -> int:
        self.ans = 0
        def helper(nums):
            if len(nums)==0:
                self.ans+=1
                return
            helper(nums[1:])#1的场合
            if len(nums)>=2:
                if nums[0]=='0' or nums[:2]>'25':
                    return 
                helper(nums[2:])#2的场合
        helper(str(num))
        return self.ans

47.礼物的最大价值

#1.dfs超时了,避免重复搜索de记忆存储法基本上就是dp,那还不如dp
class Solution:
    def maxValue(self, grid: List[List[int]]) -> int:
        #dpdp,或者dfs两方向遍历也行
        #直接用dfs结果超时了,看看剪枝
        self.ans = 0
        def dfs(grid,i,j,tmp):
            tmp+=grid[i][j]
            if i==len(grid)-1 and j==len(grid[0])-1:
                #到角落了
                self.ans = max(self.ans,tmp)
                return
            if i+1<len(grid):
                dfs(grid,i+1,j,tmp)
            if j+1<len(grid[0]):
                dfs(grid,i,j+1,tmp)
        dfs(grid,0,0,0)
        return self.ans
#2.dpdp
class Solution:
    def maxValue(self, grid: List[List[int]]) -> int:
        m =len(grid)
        n =len(grid[0])
        dp = [[0]*n for _ in range(m)]
        dp[0][0]=grid[0][0]
        for j in range(1,n):
            #右
            dp[0][j]=dp[0][j-1]+grid[0][j]
        for i in range(1,m):
            #下
            dp[i][0]=dp[i-1][0]+grid[i][0]
            for j in range(1,n):
                    dp[i][j] = max(dp[i-1][j],dp[i][j-1])+grid[i][j]
        return dp[-1][-1]

48.最长不含重复字符的子字符串

#1.双指针哈希
class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        last_idx=-1
        ans = 0
        memo = dict()
        for i in range(len(s)):
            if s[i] in memo and memo[s[i]]>last_idx:
                #出现重复字符,且字符位置大于当前记录子串的起始idx,得是在现在的子串里重复的字符
                #一换一,ans不用更新
                last_idx = memo[s[i]]
                memo[s[i]]=i
            else:
                memo[s[i]]=i
                ans = max(ans,i-last_idx)#上一个重复字开始的新位置到i长度,abca的话就是b2-a[-1]=3
        return ans
#2.空间优化dp哈希
class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        dic = {}
        res = tmp = 0
        for j in range(len(s)):
            i = dic.get(s[j], -1) # 获取索引 i,没有就返回-1
            dic[s[j]] = j # 更新哈希表
            '''
            dp记录当前子串的长度
            1;dp[j-1]=j-i,子串长度变不动了,遇到相同字符了,当前子串j-i长度
            '''
            tmp = tmp + 1 if tmp < j - i else j - i # dp[j - 1] -> dp[j]
            res = max(res, tmp) # max(dp[j - 1], dp[j])
        return res

你可能感兴趣的:(Python笔记)