[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,子串可以延長,tmp+1
            2.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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章