[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+1k,则在右子树找第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

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