[Python筆記] 劍指offer刷題記錄——進度75/75

劍指offer刷題記錄
LeetCode上的劍指offer題
刷題ing

49. 醜數

#1.dp_轉化成數組合並問題,用上個狀態的數
class Solution:
    def nthUglyNumber(self, n: int) -> int:
        #醜數的規律,從前向後看,每個數都是前邊的乘以2,3,5得到的
        #三指針:三個有序數組(*2,*3,*5)無重複元素合併
        if not n:
            return 0
        ugly=[1]*n
        i,j,k=0,0,0
        for idx in range(1,n):
            tmp = min(ugly[i]*2,ugly[j]*3,ugly[k]*5)
            if tmp==ugly[i]*2:
                i+=1
            if tmp==ugly[j]*3:
                j+=1
            if tmp==ugly[k]*5:
                k+=1
            ugly[idx]=tmp
        return ugly[-1]
#2.堆&優先隊列_每加一個元素自動排序_用set去重
from queue import PriorityQueue
class Solution:
    def nthUglyNumber(self, n: int) -> int:
        # 集合+優先隊列
        pq = PriorityQueue()#其實就是堆啦
        rec = set()#set去重

        pq.put(1)
        rec.add(1)

        i = 1
        while True:
            x = pq.get()
            if i == n:
                return x
            i+=1
            for k in 2*x, 3*x, 5*x:
                if k not in rec:
                    rec.add(k)
                    pq.put(k)#自動排序

        return -1

50.第一個只出現一次的字符

#1.一般計數器
#Python 3.6 後,默認字典就是有序的,因此無需使用 OrderedDict()
class Solution:
    def firstUniqChar(self, s: str) -> str:
        memo = {}
        for l in s:
            if l in memo:
                memo[l]+=1
            else:
                memo[l]=1
        for key in memo:
            if memo[key]==1:
                return key
        else:
            return ' '

51.數組中的逆序對

#1.merge
class Solution:
    def mergeSort(self, nums, tmp, l, r):
        #用merge利用數組的部分有序性
        if l >= r:
            return 0
        mid = (l + r) // 2
        #l,mid//mid+1,r,子數組的逆序對個數
        inv_count = self.mergeSort(nums, tmp, l, mid) + self.mergeSort(nums, tmp, mid + 1, r)
        i, j, pos = l, mid + 1, l
        #i是左半個有序序列指針,j是右半有序序列的指針,tmp是新創建數組,pos是其指針
        while i <= mid and j <= r:
            if nums[i] <= nums[j]:
                tmp[pos] = nums[i]#左邊的小,放進去
                i += 1
                inv_count += (j - (mid + 1))#其實是在r的元素被加進去下一步到加l元素的時候纔會記錄下來,本來是看j的元素小,放進去的時候i這邊還有幾個沒放,理解成放i的時候看已經放了幾個j進去了也是okk的
            else:
                #逆序對出現了
                tmp[pos] = nums[j]
                j += 1
            pos += 1
        #剩下的清掉
        for k in range(i, mid + 1):
            tmp[pos] = nums[k]
            inv_count += (j - (mid + 1))
            pos += 1
        for k in range(j, r + 1):
            tmp[pos] = nums[k]
            pos += 1
        nums[l:r+1] = tmp[l:r+1] #這樣nums就部分排好序了
        return inv_count

    def reversePairs(self, nums: List[int]) -> int:
        n = len(nums)
        tmp = [0] * n
        return self.mergeSort(nums, tmp, 0, n - 1)
#2.樹狀數組
from typing import List


class Solution:

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

        class FenwickTree:
        	#樹狀數組搭建
            def __init__(self, n):
                self.size = n
                self.tree = [0 for _ in range(n + 1)]

            def __lowbit(self, index):
                return index & (-index)

            # 單點更新:從下到上,最多到 len,可以取等
            def update(self, index, delta):
                while index <= self.size:
                    self.tree[index] += delta
                    index += self.__lowbit(index)

            # 區間查詢:從上到下,最少到 1,可以取等
            def query(self, index):
                res = 0
                while index > 0:
                    res += self.tree[index]
                    index -= self.__lowbit(index)
                return res

        # 特判
        size = len(nums)
        if size < 2:
            return 0

        # 原始數組去除重複以後從小到大排序,這一步叫做離散化
        s = list(set(nums))

        # 構建最小堆,因爲從小到大一個一個拿出來,用堆比較合適
        import heapq
        heapq.heapify(s)

        # 由數字查排名
        rank_map = dict()
        rank = 1
        # 不重複數字的個數
        rank_map_size = len(s)
        for _ in range(rank_map_size):
            num = heapq.heappop(s)
            rank_map[num] = rank
            rank += 1

        res = 0
        # 樹狀數組只要不重複數字個數這麼多空間就夠了
        ft = FenwickTree(rank_map_size)
        # 從後向前看,拿出一個數字來,就更新一下,然後向前查詢比它小的個數
        for i in range(size - 1, -1, -1):
            rank = rank_map[nums[i]]
            ft.update(rank, 1)
            res += ft.query(rank - 1)
        return res

52.兩個鏈表的第一個公共節點

#1.浪漫相遇
class Solution:
    def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
    	'''
		1:有交集:相交鏈表連着到尾端,A與B交長度c,剩下a和b,有a+c+b = b+c+a,總能在交叉第一個相遇
		2:無交集,a+b=b+a,兩個一起null,返回null
    	'''
        p1 = headA
        p2 = headB
        while p1!=p2:
            p1 = headB if not p1 else p1.next
            p2 = headA if not p2 else p2.next
        return p1
#2.hashmap看相同節點有無
#不滿足空間O(1)
class Solution:
    def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
        memo = {}
        p1 = headA
        p2 = headB
        while p1:
            memo[p1]=1
            p1 = p1.next
        while p2:
            if p2 in memo:
                return p2
            p2 = p2.next
        return None

53-1.在排序數組中查找數字 I

#1.二分,注意and的前後判斷順序,邊界判斷在先
lass Solution:
    def search(self, nums: List[int], target: int) -> int:
        if not nums or len(nums)==0:
            return 0
        #暴力不可,二分
        i = 0
        j = len(nums)-1
        cnt = 0
        while i<j:
            mid = (j-i)//2+i
            if nums[mid]<target:
                i = mid+1
            else:
                j = mid
        #找到了左起點
        cnt = 0
        while i<len(nums) and nums[i]==target:
            cnt+=1
            i+=1
        return cnt

53-2.0~n-1中缺失的數字

#1.暴力遍歷
class Solution:
    def missingNumber(self, nums: List[int]) -> int:
        if nums == [] or nums[0]!=0:
            return 0
        for i in range(1,len(nums)):
            if nums[i]!=i:
                return i
        return len(nums)
#2.二分(有序就想到二分,畢竟時間複雜度低)
class Solution:
    def missingNumber(self, nums: List[int]) -> int:
        i=0
        j = len(nums)
        while i<j:
            mid = (i-j)//2+j
            if nums[mid]==mid:
                i=mid+1
            else:
                j=mid
        return i
#3.異或(更加適用於無序的數組)
class Solution:
    def missingNumber(self, nums: List[int]) -> int:
        ans = len(nums)
        for i in range(0,len(nums)):
            #其實還是看i和numsi等不等
            #如果中間缺了一個,那麼肯定其他的到最後的連續異或了都能抵消成0(ans初始是len(nums)剛好是最後一個元素))
            #而剩下一個i,就是最後返回的缺失值
            #如果是末尾缺失,那麼剛好就是len(nums)
            ans^=nums[i]
            ans^=i
        return ans

54.二叉搜索樹的第k大節點

#1.一般迭代中序
class Solution:
    def kthLargest(self, root: TreeNode, k: int) -> int:
        #中序遍歷,返回-k
        stack = [(0,root)]
        inorder = []
        while stack:
            opt,node = stack.pop()
            if not node:
                continue
            if opt==1:
                inorder.append(node.val)
            else:
                stack.append((0,node.right))
                stack.append((1,node))
                stack.append((0,node.left))
        return inorder[-k]
#2.遞歸下加個計數器也可以
class Solution:
    def kthLargest(self, root: TreeNode, k: int) -> int:
        #中序-計數k,右-中-左逆着記
        self.ans = 0
        self.cnt=0
        def helper(root,k):
            if root.right:
                helper(root.right,k)
            self.cnt+=1
            if self.cnt==k:
                self.ans = root.val
                return
            if root.left:
                helper(root.left,k)
        helper(root,k)
        return self.ans
#3.老實人做法:借用BST結構特性
'''
先計算右子樹的節點數爲 r_num,那麼根節點是第 r_num+1  大的節點。如果r_num+1=k,則返回root的val值;如果r_num+1<k,則在左子樹中找第 k-r_num-1 大的節點;如果r_num+1>k,則在右子樹找第k大的節點。遞歸查找。
'''
class Solution:
    def kthLargest(self, root: TreeNode, k: int) -> int:
        if not root:
            return 0
        def helper(root):
            if not root:
                return 0
            return helper(root.left)+helper(root.right)+1
        r_num = helper(root.right)
        if r_num+1==k:
            return root.val
        elif r_num+1<k:
            return self.kthLargest(root.left,k-r_num-1)
        else:
            return self.kthLargest(root.right,k)
        return 0

55-1.二叉樹的深度

#1.一般遞歸
class Solution:
    def maxDepth(self, root: TreeNode) -> int:
        def helper(node):
            if not node:
                return 0
            max_left=helper(node.left)
            max_right=helper(node.right)
            return  max(max_left,max_right)+1
        return helper(root)
#2.dfs
class Solution:
    def maxDepth(self, root: TreeNode) -> int:
        self.depth=0
        def dfs(node,level):
            if not node:
                return 0
            if self.depth<level:
                self.depth+=1
            dfs(node.left,level+1)
            dfs(node.right,level+1)
        dfs(root,1)
        return self.depth
#3.bfs_就是層序遍歷下
import collections
class Solution:
    def maxDepth(self, root: TreeNode) -> int:
        if not root:
            return 0
        depth = 0
        q = collections.deque()
        q.append(root)
        while q:
            depth+=1
            for i in range(len(q)):
                node = q.popleft()
                if not node:
                    continue
                if node.left:
                    q.append(node.left)
                if node.right:
                    q.append(node.right)
        return depth

55-2.平衡二叉樹

#1.一般遞歸,需返回高度
class Solution:
    def isBalanced(self, root: TreeNode) -> bool:
        #在記錄TF的時候還得記錄高度,之後對比用
        def helper(root):
            if not root:
                return True,-1
            left_balance,left_height = helper(root.left)
            if not left_balance:
                return False,0
            right_balance,right_height = helper(root.right)
            if not right_balance:
                return False,0
            if abs(left_height-right_height)>1:
                return False,0
            else:
                return True,max(left_height,right_height)+1
        return helper(root)[0]
#2.後序_直接用height判斷
class Solution:
    def isBalanced(self, root: TreeNode) -> bool:
        return self.treeHeight(root) >= 0
    def treeHeight(self, root):
        if not root:
            return 0
        leftHeight = self.treeHeight(root.left)
        rightHeight = self.treeHeight(root.right)
        if leftHeight >= 0 and rightHeight >= 0 and abs(leftHeight - rightHeight) <= 1:
            return max(leftHeight, rightHeight) + 1
        else:
            return -1 # -1表示不平衡

56-1.數組中數字出現的次數

#1.一般位運算
class Solution:
    def singleNumbers(self, nums: List[int]) -> List[int]:
        #hash計數兩次遍歷空間不滿足
        #排序再遍歷估計行
        #果然是異或了嗎
        ans = 0
        for i in range(len(nums)):
            ans ^= nums[i]
        #這時候得到的是那兩個數的異或
        #1,6就是7;2,10就是8,爲1的某一位就可以區分這兩個數
        idx=0
        while ans&1==0:
            #找到異或裏第一個爲1的位數
            idx+=1
            ans>>=1
        left = 0
        right = 0
        for i in range(len(nums)):
            if (nums[i]>>idx)&1==0:
                #這一位上是0
                left^=nums[i]
            else:
                right^=nums[i]
        return [left,right]
'''
找到異或裏第一個爲1的位數那一步其實是lowbit
用ans&(-ans)就行
'''

56-2.數組中數字出現的次數 II

#1.hash計數
class Solution:
    def singleNumber(self, nums: List[int]) -> int:
        memo = {}
        for i in range(len(nums)):
            if nums[i] not in memo:
                memo[nums[i]]=1
            else:
                memo[nums[i]]+=1
        for num in memo:
            if memo[num]==1:
            	return num
#2.Py_去重_數學
class Solution:
    def singleNumber(self, nums: List[int]) -> int:
        return (sum(set(nums))*3-sum(nums))//2
#3.數電法1,32位計位規律
'''
通過對數組中各個數的二進制表示形式逐位進行觀察,我們可以發現,當數組中只出現一次的那個數字(用k表示)
在二進制的對應位爲0時,該對應位爲1在數組各個數字中出現的總次數應當爲3^n
當k的對應位爲1時,該對應位爲1在數組各個數字中出現的總次數應當爲3^n+1,
爲此,我們可以統計數字中的各個位中1出現的次數,當爲3^n 次時,只出現一次的數字的對應位應當爲0,
當爲3^n + 1次時,只出現一次的數字的對應位應當爲1。
'''
class Solution:
    def singleNumber(self, nums: List[int]) -> int:
        stack = [0]*32
        for num in nums:
            for i in range(32):
                stack[i] += 1 if (num&(1<<i))!=0 else 0
        ans = 0
        for i in range(32):
            ans+=(1<<i)*(stack[i]%3)
        return ans
#4.數電法2,位運算優化
'''
實際上,我們只需要記錄對應位出現的次數爲0、1、2次的情況,當對應位出現次數爲3的時候,
我們便可以將該位出現的次數置爲0,重新開始進行計數。由於int型中的各個二進制位出現的次數爲3進制的,
爲此我們需要兩個位來記錄各個位出現的次數,由此我們需要引入兩個變量a,b來統計對應位出現的次數。由ab兩個變量組合起來來記錄各個二進制位出現爲1的情況。變量a表示高位的情況,變量b表示低位的情況,而在遍歷數組運算完成之後,遍歷b的值便是答案。
a’=1,b‘=0,新位=1,此時a=0,b=0,的這樣一種三進製表示。
真值表推邏輯表達式:輸出1對應行看輸入,0是非,1是真
'''
class Solution:
    def singleNumber(self, nums: List[int]) -> int:
        a = 0
        b = 0
        for num in nums:
            a = (a^num)&~b
            b = (b^num)&~a
        return a

57.和爲s的兩個數字

#1.雙指針 O(N) O(1)
class Solution:
    def twoSum(self, nums: List[int], target: int) -> List[int]:
        #雙指針
        i = 0
        j = len(nums)-1
        while i<j:
            summ = nums[i]+nums[j]
            if summ == target:
                return [nums[i],nums[j]]
            if summ<target:
                i+=1
            else:
                j-=1
        return None
#2.二分_超慢
class Solution:
    def twoSum(self, nums: List[int], target: int) -> List[int]:
        #二分
        for i in range(len(nums)):
            out = target-nums[i]
            l = 0
            r = len(nums)-1
            while l<r: 
                mid = (l-r)//2+r
                if nums[mid] == out:
                    return [nums[i],nums[mid]]
                if nums[mid]<out:
                    l = mid+1
                else:
                    r = mid
                    

57-2.和爲s的連續正數序列

#1.前綴和_遍歷
class Solution:
    def findContinuousSequence(self, target: int) -> List[List[int]]:
        #連續正整數序列,前綴和???
        ans = []
        end = target//2+1  #9--5;15--8
        presum = [0]*(end+1)
        nums = list(range(end+1))
        for i in range(len(nums)):
            presum[i] = presum[i-1]+nums[i]
        #print(presum)
        for i in range(len(nums)):
            j = i+1
            while j<len(nums) and presum[j]-presum[i]<target:
                j+=1
            if j>=len(nums) or presum[j]-presum[i]>target:
                continue
            if presum[j]-presum[i]==target:
                #print(i,j)
                ans.append(list(range(i+1,j+1)))
        return ans
#2.數學優化前綴和-滑窗
class Solution:
    def findContinuousSequence(self, target: int) -> List[List[int]]:
        #數學優化前綴和+滑窗
        ans = []
        i = 1
        j = 2
        while i<j:
            summ = (i+j)*(j-i+1)//2 #首項末項項數除2
            if summ<target:
                j+=1
            elif summ>target:
                i+=1
            else:
                ans.append(list(range(i,j+1)))
                i+=1
        return ans
#3.純數學法
'''
(x+y)∗(y−x+1)//2=target  解方程
y^2+y−x^2+x−2∗target=0 視y爲變量
a=1,b=1,c=-x^2+x-2*target 套公式
判斷是否整數解需要滿足兩個條件:
判別式 b^2-4ac 開根需要爲整數
最後的求根公式的分子需要爲偶數,因爲分母爲2
​	
'''
class Solution:
    def findContinuousSequence(self, target: int):
        # 創建輸出列表
        res = []

        # y不能超過target的中值,即y<=target//2 + 1,range函數左開右閉,所以這裏是+2
        for y in range(1,target//2 + 2):
            # 應用我們的求根公式
            x = (1/4 + y**2 + y - 2 * target) ** (1/2) + 0.5
            # 我們要確保x不能是複數,且x必須是整數
            if type(x) != complex and x - int(x) == 0:
                res.append(list(range(int(x),y+1)))
        
        return res
#4.間隔法
#複雜度O(√target)
'''
首項x末項x+i間隔i
2xi+2x+i^2+i=2t 
t=x(i+1)+i(i+1)/2
x = (t-(i*(i+1)/2))/(i+1)
條件1: x必須是正整數,所以i(i+1)/2 要小於t,否則就會出現負數。
條件2: (t-(i*(i+1)/2))/(i+1)必須是整數
'''
class Solution:
    def findContinuousSequence(self, target: int) -> List[List[int]]:
        # 我們的間隔從1開始
        i, res = 1, []
        
        # 根據上面的條件1,限定i的大小,即間隔的範圍
        while i*(i+1)/2 < target:
            # 根據條件2,如果x不爲整數則擴大間隔
            if not (target - i*(i+1)/2) % (i+1):
                # 如果兩個條件都滿足,代入公式求出x即可,地板除//會把數改成float形式,用int()改回來
                x = int((target - i*(i+1)/2) // (i+1))
                # 反推出y,將列表填入輸出列表即可
                res.append(list(range(x,x+i+1)))
            # 當前間隔判斷完畢,檢查下一個間隔
            i += 1

        # 由於間隔是從小到大,意味着[x,y]列表是從大到小的順序放入輸出列表res的,所以反轉之
        return res[::-1]
                

58-1.翻轉單詞順序

#1.Py_str法
class Solution:
    def reverseWords(self, s: str) -> str:
        s1 = s.strip().split(' ')
        s2 = []
        for i in range(len(s1)-1,-1,-1):
            if s1[i]=="" or s1[i]==" ":
                continue
            s2.append(s1[i].strip())
        return " ".join(s2)
#或者
class Solution:
    def reverseWords(self, s: str) -> str:
        return ' '.join(s.strip().split()[::-1])

#2.雙指針_後向前遍歷
class Solution:
    def reverseWords(self, s: str) -> str:
        s = s.strip() # 刪除首尾空格
        i = j = len(s) - 1 #從結尾開始遍歷
        res = []
        while i >= 0:
            while i >= 0 and s[i] != ' ': i -= 1 # 搜索首個空格
            res.append(s[i + 1: j + 1]) # 添加單詞
            while s[i] == ' ': i -= 1 # 跳過單詞間空格
            j = i # j 指向下個單詞的尾字符
        return ' '.join(res) # 拼接並返回

58-2.左旋轉字符串

#1.Py切片
class Solution:
    def reverseLeftWords(self, s: str, n: int) -> str:
        return s[n:]+s[:n]
#2.騷操作_取餘
class Solution:
    def reverseLeftWords(self, s: str, n: int) -> str:
        res = []
        for i in range(n, len(s)):
            res.append(s[i])
        for i in range(n):
            res.append(s[i])
        return ''.join(res)
#3.三次翻轉
class Solution:
    def reverseLeftWords(self, s: str, n: int) -> str:
        if n > len(s) or not s:
            return ''
        s = list(s)
        def reverse(start, end):
            while start < end:
                s[start], s[end] = s[end], s[start]
                start += 1
                end -= 1
        length = len(s) - 1
        reverse(0, n-1)
        reverse(n,length)
        reverse(0, length)
        return ''.join(s)
'''
Py筆記
1.列表的一個內置方法,直接使用返回值爲None
2.reversed()的作用之後,返回的是一個把序列值經過反轉之後的迭代器,
  所以,需要通過遍歷,或者List,或者next()等方法,獲取作用後的值;
3.reverse和reversed的區別是有沒有返回值
4.不管reverse哪個局部,最好的方法是a[a:b:c]=reversed(a[a:b:c])
'''
class Solution:
    def reverseLeftWords(self, s: str, n: int) -> str:
        if n > len(s) or not s:
            return ''
        s = list(s)
        s[:n] = reversed(s[:n])
        s[n:] = reversed(s[n:])
        s = reversed(s)
        return ''.join(s)

59-1.滑動窗口的最大值

class Solution:
    def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
        #有麼有那樣的單調隊列--滑窗--單調
        '''
        棧模擬隊列
        遞減隊,最左側元素一定爲當前滑窗內最大;
        若遞減隊列大於等於滑窗大小,彈出最左側也就是最大元素;
        若遇到非遞減元素,則把升序部分都pop,新入隊列
        '''
        q = []
        ans = []
        for i in range(len(nums)):
            while q and nums[i]>nums[q[-1]]:
                #單調隊列記錄索引
                q.pop()
            q.append(i)
            while q[-1]-q[0]>=k:
                q.pop(0)
            ans.append(nums[q[0]])
        return ans[k-1:] #從第一個滑窗開始記
#雙端隊列也一樣
from collections import deque
class Solution:
    def maxSlidingWindow(self, nums: 'List[int]', k: 'int') -> 'List[int]':
        queue, res = [], []
        for i in range(len(nums)):
            if len(queue) > 0 and i - queue[0] + 1 > k: del queue[0]
            while len(queue) > 0 and nums[i] > nums[queue[-1]]: del queue[-1]
            queue.append(i)
            if i >= k - 1: 
                res.append(nums[queue[0]])
                
        return res

59-2.隊列的最大值

'''
時間複雜度:O(1)(插入,刪除,求最大值)
刪除操作求最大值操作顯然只需要O(1) 的時間。
而插入操作雖然看起來有循環,做一個插入操作時最多可能會有n次出隊操作。
但要注意,由於每個數字只會出隊一次,因此對於所有的n個數字的插入過程,對應的所有出隊操作也不會大於 
n次。因此將出隊的時間均攤到每個插入操作上,時間複雜度爲 O(1)。
空間複雜度:O(n),需要用隊列存儲所有插入的元素。

'''
#1.滑動窗口維護遞減隊列的方法,純用隊列
import queue
class MaxQueue:

    def __init__(self):
        self.deque = queue.deque()#雙端,主helper
        self.queue = queue.Queue()#單向隊列

    def max_value(self) -> int:
        return self.deque[0] if self.deque else -1


    def push_back(self, value: int) -> None:
        while self.deque and self.deque[-1] < value:
            self.deque.pop()
        self.deque.append(value)
        self.queue.put(value)

    def pop_front(self) -> int:
        if not self.deque:
            return -1
        ans = self.queue.get()
        if ans == self.deque[0]:
            self.deque.popleft()
        return ans
#2.棧實現
import queue
class MaxQueue:

    def __init__(self):
        self.q = []
        self.helper = []

    def max_value(self) -> int:
        return self.helper[0] if self.q else -1


    def push_back(self, value: int) -> None:
        while self.helper and self.helper[-1] < value:
            self.helper.pop()
        self.helper.append(value)
        self.q.append(value)

    def pop_front(self) -> int:
        if not self.q:
            return -1
        ans = self.q.pop(0)
        if ans == self.helper[0]:
            self.helper.pop(0)
        return ans

60.n個骰子的點數

#痛苦找規律
class Solution:
    def twoSum(self, n: int) -> List[float]:
        #n爲骰子個數
        #找了一下規律,這題有點dpdp
        start = (1/6)**n
        '''
        n=1:  0 1 1 1 1 1 1
        n=2:  0 0 1 2 3 4 5 6 5 4 3 2 1
        n=3:  0 0 0 1 3 6 10 ...
        規律準確來講是:dp[n][s] = sum(dp[n-1][s-1] to dp[n-1][s-6])
        '''
        dp = [[0]*(n*6+1) for _ in range(n)]
        dp[0][:7]=[0,1,1,1,1,1,1]
        for j in range(1,n):
            for s in range(j,n*6+1):
                k = 1
                while k<=6 and s>=k:
                    dp[j][s] += dp[j-1][s-k] 
                    k+=1
        ans = []
        for num in dp[-1]:
            if num>0:
                ans.append(num*start)
        return ans
#優化空間to單維
class Solution:
    def twoSum(self, n: int) -> List[float]:
        #n爲骰子個數
        #找了一下規律,這題有點dpdp
        start = (1/6)**n
        dp = [0]*(n*6+1)
        dp[:7]=[0,1,1,1,1,1,1]
        for j in range(1,n):
            for s in range(n*6,j-1,-1):
                #單維優化得逆序
                k = 1
                dp[s] = 0#這一步注意,是從自己的0開始累加纔對
                while k<=6 and s>=k:
                    dp[s] += dp[s-k] 
                    k+=1
        ans = []
        for num in dp:
            if num>0:
                ans.append(num*start)
        return ans

61.撲克牌中的順子

#1.排序遍歷_統計空位和0補位
class Solution:
    def isStraight(self, nums: List[int]) -> bool:
        #這個數組中0可以當任何數用,所以當牌不連續的時候,它就可以替補一下
        nums.sort()
        cnt0 = 1 if nums[0]==0 else 0
        gap = 0
        for i in range(1,len(nums)):
            if nums[i]==0:
                cnt0+=1
            if nums[i]!=0 and nums[i]==nums[i-1]:
            	#順子中不能有0以外的重複牌
                return False
            if nums[i]==nums[i-1]+1 or nums[i-1]==0:
                continue
            else:
            	#非順子計算空位
                gap += nums[i]-nums[i-1]-1
        return cnt0>=gap
#2.排序遍歷
class Solution:
    def isStraight(self, nums: List[int]) -> bool:
        joker = 0
        nums.sort() # 數組排序
        for i in range(4):
            if nums[i] == 0: joker += 1 # 統計大小王數量
            elif nums[i] == nums[i + 1]: return False # 若有重複,提前返回 false
        return nums[4] - nums[joker] < 5 # 最大牌 - 最小牌 < 5 則可構成順子

#3.set+遍歷
class Solution:
    def isStraight(self, nums: List[int]) -> bool:
        repeat = set()
        ma, mi = 0, 14
        for num in nums:
            if num == 0: continue # 跳過大小王
            ma = max(ma, num) # 最大牌
            mi = min(mi, num) # 最小牌
            if num in repeat: return False # 若有重複,提前返回 false
            repeat.add(num) # 添加牌至 Set
        return ma - mi < 5 # 最大牌 - 最小牌 < 5 則可構成順子 

62.圓圈中最後剩下的數字

#1.照題意操作一遍就有了
class Solution:
    def lastRemaining(self, n: int, m: int) -> int:
        nums = list(range(n))
        flag = m-1
        while len(nums)>1:
            flag = flag%len(nums)
            nums.pop(flag)
            #print(su)
            flag += m-1
        return nums[0]
#2.數學法--約瑟夫環問題
'''
f(n,m)=[(m-1)%n+x+1]%n 其中x=f(n-1,m)
f(n,m)=[(m-1)%n+x+1]%n
      =[(m-1)%n%n+(x+1)%n]%n
      =[(m-1)%n+(x+1)%n]%n
      =(m-1+x+1)%n
      =(m+x)%n
'''
class Solution:
    def lastRemaining(self, n: int, m: int) -> int:
        #約瑟夫環問題
        ans = 0
        for i in range(2,n+1):
            ans = (ans+m)%i
        return ans

63.股票的最大利潤

#1.一般do
class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        if len(prices)<=1:
            return 0
        dp = 0
        for i in range(1,len(prices)):
            dp = max(dp,prices[i]-min(prices[:i]))
        return dp
#2.維護一個最小值有效提速
class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        if len(prices)<=1:
            return 0
        dp = 0
        minmin = prices[0]
        for i in range(1,len(prices)):
            if prices[i]<minmin:
                minmin = prices[i]
            dp = max(dp,prices[i]-minmin)
        return dp

64.求1+2+…+n

#不讓用乘除法、for、while、if、else、switch、case等關鍵字及條件判斷語句(A?B:C)
#1.遞歸,終點設爲0——遞歸短路
class Solution:
    def sumNums(self, n: int) -> int:
        return n and (n+self.sumNums(n-1))
## 這個特性實際叫做“驟死性評估”,是一種語言特性,即左側的表達式爲假時整個表達式後續將不再進行評估。
## 考察and特性,前者false就跳過後判斷條件

65. 不用加減乘除做加法

a, b 均可能是負數或 0;結果不會溢出 32 位整數;

'''
^ 亦或 ----相當於 無進位的求和, 想象10進制下的模擬情況:(
如:19+1=20;無進位求和就是10,而非20;因爲它不管進位情況)

& 與 ----相當於求每位的進位數, 先看定義:1&1=1;1&0=0;0&0=0;
即都爲1的時候才爲1,正好可以模擬進位數的情況,還是想象10進制下模擬情況:(
9+1=10,如果是用&的思路來處理,則9+1得到的進位數爲1,而不是10,所以要用<<1向左再移動一位,這樣就變爲10了);

這樣公式就是:(a^b) ^ ((a&b)<<1) 即:每次無進位求 + 每次得到的進位數
我們需要不斷重複這個過程,直到進位數爲0爲止;
'''
class Solution:
    def add(self, a: int, b: int) -> int:
        #位運算經典考察題目
        return a if b==0 else add(a^b,(a&b)<<1)
#有關python存儲格式的考察
class Solution:
    def add(self, a: int, b: int) -> int:
        x = 0xffffffff
        #Python中bin一個負數(十進制表示),輸出的是它的原碼的二進制表示加上個負號
        a, b = a & x, b & x #獲取負數的補碼,捨去此數字32位以上的數字,從無限長度變爲一個32位整數。
        while b != 0:
            a, b = (a ^ b), (a & b) << 1 & x #補碼運算減也是加
        return a if a <= 0x7fffffff else ~(a ^ x)
        #如果 a 的補碼是負數(第32位是1),需要把這個補碼恢復到 python 存儲負數的形式
        #若補碼a爲負數( 0x7fffffff 是最大的正數的補碼 ),需執行 ~(a ^ x) 操作,將補碼還原至 Python 的存儲格式。 # a ^ x運算將1至32位按位取反;~ 運算是將整個數字取反;因此, ~(a ^ x) 是將32位以上的位取反,由0變爲1,
        #1至32位不變。

'''
因爲c java等是有位數限制的,所以可以直接做。但是python沒有位數限制,
可能一個數字用了大於32位去存儲。所以先把a,b都搞到32位範圍內,做運算。
最後結果a如果是負數,那麼可能超過了32位存儲,要給a恢復到超過32位的python中的存儲方式。
'''

66.構建乘積數組

#1.兩次遍歷
class Solution:
    def constructArr(self, a: List[int]) -> List[int]:
        ans = [1]*len(a)
        mul = 1
        for i in range(len(a)):
            #左向右
            ans[i]=mul
            mul*=a[i]
        mul = 1
        for i in range(len(a)-1,-1,-1):
            #右向左
            ans[i]*=mul
            mul*=a[i]
        return ans

67.把字符串轉換成整數

#1.正則
class Solution:
    def strToInt(self, str: str) -> int:
        #正則一發
        return max(min(int(*re.findall("^[\+\-]?\d+",str.lstrip())),2**31 - 1),-2**31)
#2.ifelse
class Solution:
    def strToInt(self, str: str) -> int:
        num_max = pow(2,31)-1
        num_min = -pow(2,31)
        if str=="":
            return 0
        s = list(str)
        nums = ['1','2','3','4','5','6','7','8','9','0']
        num_out = []
        for i in range(len(str)):
            if s[i] in nums:
                num_out.append(s[i])
                continue
            if s[i]=="-" and num_out == []:
                num_out.append(s[i])
                continue
            if s[i]=="+" and num_out ==[]:
                num_out.append(s[i])
                continue
            if s[i]==" " and num_out ==[]:
                    continue
            else:
                break
        if num_out == [] or num_out == ["-"] or num_out == ["+"]:
            return 0
        num_out = "".join(num_out)
        print(num_out)
        num_out = int(num_out)
        if num_out<num_min:
            return num_min
        elif num_out>num_max:
            return num_max
        else:
            return num_out

68-1.二叉搜索樹的最近公共祖先

class Solution:
    def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
        if root.val>p.val and q.val<root.val:
            return self.lowestCommonAncestor(root.left,p,q)
        if root.val<p.val and q.val>root.val:
            return self.lowestCommonAncestor(root.right,p,q)
        return root

68-2. 二叉樹的最近公共祖先

class Solution:
    def lowestCommonAncestor(self, root: TreeNode, p: TreeNode, q: TreeNode) -> TreeNode:
        if not root:
            return None
        if root.val == p.val:
            return p
        if root.val == q.val:
            return q
        left = self.lowestCommonAncestor(root.left,p,q)
        right = self.lowestCommonAncestor(root.right,p,q)
        if left and right:
            return root
        if not left and right:
            return right
        if left and not right:
            return left
        else:
            return None
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章