103. leetcode笔记(1~60)

d1

leet1: 两数之和

https://leetcode-cn.com/problems/two-sum
给定 nums = [2, 7, 11, 15], target = 9
因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]
【hashmap存储】

class Solution:
    def twoSum(self, nums: List[int], target: int) -> List[int]:
        # 边界值判断  
        if not nums or target == None:
            return []

        has = {} # hash型map存值对应的"索引"
        for i, num in enumerate(nums):
            if target - num in has:
                return [has[target - num], i]
            
            has[num] = i
        return []

leet4: 两个数组中的中位数

给定两个大小为 m 和 n 的有序数组 nums1 和 nums2。
请你找出这两个有序数组的中位数,并且要求算法的时间复杂度为 O(log(m + n))。

nums1 = [1, 2]
nums2 = [3, 4]
则中位数是 (2 + 3)/2 = 2.5
【奇偶判断】

class Solution:
    def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float:
        if not nums1 and not nums2:
            return None

        nums = []
        nums.extend(nums1)
        nums.extend(nums2)
        nums.sort() 
        # 偶数个数情况,求均值;"//"获取整数索引
        return nums[len(nums)//2] if len(nums) % 2 else (nums[len(nums)//2] + nums[len(nums)//2 - 1]) / 2

leet5: 最长回文子串

给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。
输入: "babad"
输出: "bab"
注意: "aba" 也是一个有效答案。
【双循环贪心】

class Solution:
    def longestPalindrome(self, s: str) -> str:
        if len(s) <= 1:
            return s
        # 最大长度开始判别,符合条件的字符串即最大回文子串
        for length in range(len(s), 0, -1):  #  len(s)次,倒序
            for i in range(0, len(s) - length + 1): 
                newS = s[i : i + length]
                if newS == newS[::-1]: # '反转'判别
                    return newS

d2

leet9: 回文数

判断一个整数是否是回文数。回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。
输入: 121
输出: true
【折半比较】

class Solution:
    def isPalindrome(self, x: int) -> bool:
        if x == None:
            return False
        if len(str(x)) <= 1:
            return True
        
        # 折半搜索
        for i in range(len(str(x)) // 2):
            if str(x)[i] != str(x)[-i - 1]:
                return False
        return True

leet409 最长回文串

给定一个包含大写字母和小写字母的字符串,找到通过这些字母构造成的最长的回文串。
在构造过程中,请注意区分大小写。比如 "Aa" 不能当做一个回文字符串。
注意:
假设字符串的长度不会超过 1010。
示例:
输入:
"abccccdd"
输出:
7
【集合更新与判空】(add remove, 放入可回文的字符,无重复)

class Solution:
    def longestPalindrome(self, s: str) -> int:
        if len(s) < 2:
            return len(s)

        num = 0 
        set0 = set()
        for i in s:
            if i in set0: # 有两个字符则能组成回文
                set0.remove(i)
                num += 2
            else: 
                set0.add(i) # 更新可回文元素
            
        # 只要还有没有移除的set0元素,就更新元素个数+1
        return num + 1 if len(set0)!=0  else num 

leet10: 正则表达式匹配

给你一个字符串 s 和一个字符规律 p,请你来实现一个支持 '.''*' 的正则表达式匹配。
'.' 匹配任意单个字符
'*' 匹配零个或多个前面的那一个元素
所谓匹配,是要涵盖 **整个 **字符串 s的,而不是部分字符串。
说明:

  • s 可能为空,且只包含从 a-z 的小写字母。
  • p 可能为空,且只包含从 a-z 的小写字母,以及字符 .*
    示例 1:
    输入:
    s = "aa"
    p = "a"
    输出: false
    解释: "a" 无法匹配 "aa" 整个字符串。

【正则包与结果项】

import re
class Solution:
    def isMatch(self, s:str, p:str)->bool:
        if re.match(p, s):
            return re.match(p, s).group() == s
        return False

d3

leet26: 删除排序数组的重复项

给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。
不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。
示例:
给定数组 nums = [1,1,2],
函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 1, 2。
你不需要考虑数组中超出新长度后面的元素。
【新计数索引】

class Solution:
    def removeDuplicates(self, nums: List[int]) -> int:
        if not nums:
            return 0
        
        count = 0
        # 修改数组,同时计数
        for i in range(1, len(nums)): # 第0个一定不排除
            if nums[i] != nums[count]:
                count += 1
                nums[count] = nums[i] # 仅需修改前若干个元素为关不重复元素即可,后面元素不改变
        
        # 返回排序数组中不重复的元素个数
        return count + 1 

leet27: 移除元素

给定一个数组 nums 和一个值 val,你需要原地移除所有数值等于 val 的元素,返回移除后数组的新长度。
不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
示例:
给定 nums = [3,2,2,3], val = 3,
函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。
你不需要考虑数组中超出新长度后面的元素。
【while val存在】

class Solution:
    def removeElement(self, nums: List[int], val: int) -> int:
        if not nums:
            return None

        # while循环存有目标的情况,不断删除
        while val in nums:
            nums.remove(val)
        return len(nums)
            

leet28: strStr()

实现 strStr() 函数:
给定一个 haystack 字符串和一个 needle 字符串,在 haystack 字符串中找出 needle 字符串出现的第一个位置 (从0开始)。如果不存在,则返回 -1。
示例:
输入: haystack = "hello", needle = "ll"
输出: 2

【str.index(p)】

class Solution:
    def strStr(self, haystack: str, needle: str) -> int:
        # 判断needle的存在性,后检测初始出现位置
        return haystack.index(needle) if needle in haystack else -1

d4

leet50: pow(x, n)

【分支递归乘数】

class Solution:
    def myPow(self, x: float, n: int) -> float: 
        if n == 0:
            return 1
        elif n < 0:
            return 1 / self.myPow(x, -n) # 调用自身
        # 奇数情况
        elif n % 2: 
            return x * self.myPow(x, n - 1)
        # 减少为一般乘数
        return self.myPow(x * x, n / 2) 

leet303: 区域和检索-数组不变情况【线段树】

给定一个整数数组 nums,求出数组从索引 i 到 j (i ≤ j) 范围内元素的总和,包含 i, j 两点。
给定 nums = [-2, 0, 3, -5, 2, -1],求和函数为 sumRange()
sumRange(0, 2) -> 1
sumRange(2, 5) -> -1
sumRange(0, 5) -> -3
【缓存和】

class NumArray:
    
    # 缓存机制便于快速查找区间和,不超时
    def __init__(self, nums: List[int]):
        self.nums = [0] + nums  # 前加入0便于防止0越界:self.nums[j] - self.nums[i - 1]
        # 从1开始求和, 更新self.nums
        for i in range(1, len(self.nums)): # 更新的数组长度不变
            self.nums[i] = self.nums[i - 1] + self.nums[i]

    def sumRange(self, i: int, j: int) -> int:
        if not self.nums:
            return 0
        return self.nums[j + 1] - self.nums[i]  # 索引向前+1


# Your NumArray object will be instantiated and called as such:
# obj = NumArray(nums)
# param_1 = obj.sumRange(i,j)

leet487:最大连续1的个数

给定一个二进制数组,你可以最多将 1 个 0 翻转为 1,找出其中最大连续 1 的个数。
输入:[1,0,1,1,0]
输出:4
解释:翻转第一个 0 可以得到最长的连续 1。
当翻转以后,最大连续 1 的个数为 4。
【最近0的出现位置】

class Solution:
    def findMaxConsecutiveOnes(self, nums: List[int]) -> int:
        if nums == None:
            return 0
        
        maxNum, num = 0,0
        lastZero = -1
        # 遍历更新0的最近出现位置,与更新后的1计数num,与更改后的1个数maxNum
        for i in range(len(nums)):
            if nums[i] == 1:
                num += 1
            else:
                maxNum = max(maxNum, num)
                num = i - lastZero # 更新最近出现的0的位置
                lastZero = i
                 
        return max(maxNum, num)

d5

leet507: 完美数

对于一个 正整数,如果它和除了它自身以外的所有正因子之和相等,我们称它为“完美数”
输出: True
解释: 28 = 1 + 2 + 4 + 7 + 14
【折平根比较】

class Solution:
    def checkPerfectNumber(self, num: int) -> bool:
        if num == None or num == 1:   
            return False

        i = 2
        sum0 = 1 # 任何的数都有因数1
        # 折平方根搜索
        while i * i <= num:
            if num % i == 0:
                sum0 += num / i + i 
            i += 1
        return sum0 == num 
              

leet665: 非递减数列

修改一个数,则变成非递减数列,输出True
【嵌套条件分支】

class Solution:
    def checkPossibility(self, nums: List[int]) -> bool:
        if nums == None:
            return False

        count = 0
        for i in range(len(nums)-1):
            if nums[i] > nums[i + 1]:  # 两种情况只能出现一遍
                # 大数(i位置)变小(i+1位置)
                if i == 0 or nums[i + 1] > nums[i - 1]: # 4(i=0时) 2 1 or 2 4(i) 3
                    nums[i] = nums[i + 1]
                # 小数(i+1位置)变大(i位置)
                else: # 2 2(i) 1 3
                    nums[i + 1] = nums[i]
                count += 1
        return count <= 1

leet400: 第N个数字

123456789101112...求出第N个数字对应的单字符数值
【while True中的数量级t和求解条件m】

class Solution:
    def findNthDigit(self, n: int) -> int:
        if n == None or n <= 0:
            return None
        
        # 9: t=1 m=9 9≤9:n=8 return '9'(1+8)[0]即0
        # 19: t=1 m=9 n=10 i=2;t=10 m=180 n≤180 n=9 return '14'(10+9//2)[9%2]即4
        i = 1
        while True:
            # 数量级递增
            t = 10 ** (i - 1)
            # 求解终止条件,9 180 
            m = t * 9 * i

            # m作为求解出口t条件
            if n <= m: 
                n -= 1
                return int(str(t + n // i)[n % i])
            n -= m
            i += 1

d6

leet219: 存在重复元素ii

列表中存在最大长度为k的两个相等的数,则返True
给定一个整数数组和一个整数 k,判断数组中是否存在两个不同的索引 i 和 j,使得 nums [i] = nums [j],并且 i 和 j 的差的绝对值最大为 k。如输入: nums = [1,2,3,1], k = 3, 输出: true

【集合表示滑动窗口】

class Solution:
    def containsNearbyDuplicate(self, nums: List[int], k: int) -> bool:
        if not nums or k == None:
            return False

        set0 = set() # set存储两个规定距离的相等值
        for i in range(len(nums)):
            if nums[i] in set0: # 唯一的判别条件
                return True
            set0.add(nums[i])  # 添加元素【add】
            # 只允许k长度内的元素存在集合内,否则删除最先进入集合中的第i-k个元素。
            # 如:1 2 3(i处) 2 k=2
            # 集合中元素数超过k !
            if len(set0) > k:  
                set0.remove(nums[i - k])
        return False

leet面试题22: 链表中倒数第k个节点

返回链表中倒数第k个节点,如1->2->3->4->5,  k = 2:
q先走2步,之后p/q一共走4步p指向4=>输出4->5

【双指针追踪】

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def getKthFromEnd(self, head: ListNode, k: int) -> ListNode:
        if not head or k <= 0:
            return None

        p = head
        q = head
        for _ in range(k):
            q = q.next
            
        # p最后倒数的位置与k循环的次数(q之前走过的长度)相同
        while q:
            q = q.next
            p = p.next
        return p

leet1346: 整数及其两倍数是否存在

两个数不是同一个数,存在某数2倍数的一个数则返回True
【判双零】

class Solution:
    def checkIfExist(self, arr: List[int]) -> bool:
        if not arr:
            return False
        if 0 in arr:
            arr.remove(0)
        if 0 in arr: # 两个零存在才满足条件
            return True
            
        for i in range(len(arr)):
            if 2 * arr[i] in arr:
                return True
        return False

d7

leet415: 字符串相加

两个数值型字符串,相加后返回结果字符串
【while中低位进位】

class Solution:
    def addStrings(self, num1: str, num2: str) -> str:
        if not num1: 
            return num2
        elif not num2:
            return num1
        
        i, j = len(num1)-1, len(num2)-1
        carry = 0 # 进位数
        res = ''

        # 只要还有没有相加完成的位, 9 + 99 = 108
        while i >= 0 or j >= 0:
            n1 = int(num1[i]) if i >= 0 else 0 # 没有位参加运算则赋0
            n2 = int(num2[j]) if j >= 0 else 0
            tmp = n1 + n2 + carry
            carry = tmp // 10 # 高位
            res = str(tmp % 10) + res # 低位
            i -= 1
            j -= 1
        
        # 最终含有进位则添加结果项中的进位
        return '1' + res if carry else res
        # -----or 直接简化的int()转换--------
        return str(int(num1) + int(num2))

leet1026: 节点与其祖先之间的最大差

求节点与其祖先之间的最大差,如:


输入:[8,3,10,1,6,null,14,null,null,4,7,13]
输出:7
解释: 
我们有大量的节点与其祖先的差值,其中一些如下:
|8 - 3| = 5
|3 - 7| = 4
|8 - 1| = 7
|10 - 13| = 3
在所有可能的差值中,最大值 7 由 |8 - 1| = 7 得出 

【左右递归dfs返回最大差】

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def maxAncestorDiff(self, root: TreeNode) -> int:
        def dfs(node, ma = -999999, mi = 999999):
            if not node:
                return ma - mi
            
            # 递归
            l = dfs(node.left, max(ma, node.val), min(mi, node.val))#祖先与左孩子的最大差
            r = dfs(node.right, max(ma, node.val), min(mi, node.val))
            return max(l, r)

        return dfs(root)

leet135:分发糖果

一排人领糖果, 相邻的人rating高的获得较多糖,每人都有糖果。

老师想给孩子们分发糖果,有 N 个孩子站成了一条直线,老师会根据每个孩子的表现,预先给他们评分。
你需要按照以下要求,帮助老师给这些孩子分发糖果:
每个孩子至少分配到 1 个糖果。
相邻的孩子中,评分高的孩子必须获得更多的糖果。
那么这样下来,老师至少需要准备多少颗糖果呢?如输入: [1,0,2], 输出: 5
解释: 你可以分别给这三个孩子分发 2、1、2 颗糖果。

【左右规则】

class Solution:
    def candy(self, ratings: List[int]) -> int:
        if not ratings:
            return 0

        left = [1 for _ in range(len(ratings))] # 每人一颗糖
        right = left[:]  # 复制一份left列表,【非引用】

        # 左右规则均从(倒数)第二个数开始与之前比较
        # 左规则: 初始每个人都有糖,从左向右遍历1~len(ratings)-1索引的元素
        for i in range(1, len(ratings)):
            if ratings[i] > ratings[i - 1]:
                left[i] = left[i - 1] + 1
    
        # 右规则: 初始count为最右边的元素值,从右到左, 0~len(ratings)-2索引的元素
        count = left[-1] 
        for i in range(len(ratings) - 2, -1, -1):
            if ratings[i] > ratings[i + 1]:
                right[i] = right[i + 1] + 1
            # 取左右规则中最大的糖果数累加
            count += max(left[i], right[i])

        return count 

d8

leet面试02.02: 倒数第k个节点值

返回链表中倒数第k个节点值
【双指针追踪】

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def kthToLast(self, head: ListNode, k: int) -> int:
        if not head or k<=0:
            return None
        
        # 双指针测量路径长
        p = head
        q = head
        for _ in range(k):
            q = q.next
        while q:
            q = q.next
            p = p.next
        return p.val

leet面试04.04/ 55: 平衡二叉树

是否平衡二叉树,即任意一个节点其两颗子树的高度差不超过一。
【递归返回是否平衡和树高】

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def isBalanced(self, root: TreeNode) -> bool:
        
        # 返回当前是否平衡树与高度差
        def check(node):
            if not node:
                return True, 0 # 空树是平衡树,高度差为0 
            lres, lheight = check(node.left)
            rres, rheight = check(node.right)
            # 分别返回是否当前为平衡树、当前树高(在子树高度基础上加一)
            return lres and rres and abs(lheight - rheight) <= 1, max(lheight, rheight) + 1

        # 返回结果中第一个值
        return check(root)[0]

leet2/leet面试02.05: 链表求和

【三重while中低位进位】

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:
        res = ''
        carry = 0 
        
        # 链表的首位对应数值的低位,即从链表收为开始计数、进位
        while l1 and l2:
            n1 = l1.val  
            n2 = l2.val  
            tmp = n1 + n2 + carry
            carry = tmp // 10 
            res = str(tmp % 10) + res
            l1 = l1.next  
            l2 = l2.next 
        while l1:
            n1 = l1.val  
            tmp = n1 + carry
            carry = tmp // 10 
            res = str(tmp % 10) + res
            l1 = l1.next  
        while l2: 
            n2 = l2.val  
            tmp = n2 + carry
            carry = tmp // 10 
            res = str(tmp % 10) + res
            l2 = l2.next
        res = '1' + res if carry else res
  
        # 反向输出
        head = ListNode(0)
        p = head
        for i in range(len(res)-1, -1, -1):
            p.next = ListNode(res[i])
            p = p.next
        return head.next

d9

leet面试18: 删除链表中节点

删除链表中的节点
【制造伪头结点】

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def deleteNode(self, head: ListNode, val: int) -> ListNode:
        if not head:
            return None

        # 情况一:删除头结点
        if head.val == val:
            return head.next

        # 情况二:非头结点 
        dummy = head # 制造伪头结点
        while head and head.next:
            if head.next.val == val:  # 与head.next.val比较
                head.next = head.next.next
            head = head.next   # 逐次向后
        
        return dummy

        # --------------------or 类似【leet203的移除链表中所有元素,递归】------------
        if not head:
            return None

        head.next = self.deleteNode(head.next, val)
        return head.next if head.val == val else head

leet面试32: 从上到下之字形打印二叉树

之字形层次遍历二叉树,如:

    3
   / \
  9  20
    /  \
   15   7 
返回其层次遍历结果: 
[
  [3],
  [20,9],
  [15,7]
]

【递归含层数的层次遍历与折返判断】

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def levelOrder(self, root: TreeNode) -> List[List[int]]:

        # 层次遍历
        def output(node, level):            
            if not root:
                return 
            list0 = [[]] # 二维数组

            # 奇偶行赋值
            if level % 2: 
                list0[level - 1].append(node.val) # 奇数行索引从0开始
            else:
                list0[level - 1].insert(0, node.val) # 偶数行索引为奇数,头插法放入列表中
            # 本行初始为空列表
            if len(list0) == level:
                list0.append([])

            output(node.left, level + 1)
            output(node.right, level + 1)
        
        output(root, 1)
        return list0[:-1] # 列表元素集中除去最后一个

leet724: 寻找数组的中心索引

如果一个数的左边数组和与右边数组和相等,则返回此中间数的索引。
【从左累加与总和比较】

class Solution:
    def pivotIndex(self, nums: List[int]) -> int:
        if not nums:
            return -1

        lsum, sum0 = 0, 0
        for i in range(len(nums)):
            sum0 += nums[i]
        
        # 左规则: 左边累计和加上下一个元素的和等于总和,则下一个索引是所求
        for i in range(len(nums)):
            if sum0 == lsum * 2 + nums[i]:
                return i
            lsum += nums[i]
        return -1

d10

leet面试27: 二叉树的镜像

输出二叉树的镜像, 如:

输入:
     4
   /   \
  2     7
 / \    / \
1   3   6  9

镜像输出: 
     4
   /   \
  7     2
 / \   /  \
9   6  3   1 

【制造临时节点的自身递归】

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def mirrorTree(self, root: TreeNode) -> TreeNode:
        if not root:
            return 

        # 递归自身 
        node0 = root.left # 创建临时节点,防止左节点被覆盖
        root.left = self.mirrorTree(root.right)
        root.right = self.mirrorTree(node0) # 防止左节点被覆盖
        return root 

leet107: 层次遍历二叉树

自底向上遍历二叉树,如:

给定二叉树 [3,9,20,null,null,15,7],

    3
   / \
  9  20
    /  \
   15   7
返回其自底向上的层次遍历为:

[
  [15,7],
  [9,20],
  [3]
]
 

【递归层次遍历】

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def levelOrderBottom(self, root: TreeNode) -> List[List[int]]:
        list0 = [[]] # 新建二维数组

        def output(node, level):
            if not node:
                return 
            
            # 赋值节点进入二维数组
            list0[level - 1].append(node.val)
            # 当前层已有赋值,则新增下一层的节点赋值空间
            if len(list0) == level:
                list0.append([])
            output(node.left, level + 1)
            output(node.right, level + 1) 

        output(root, 1)
        # list0[:-1]后反转列表
        return list0[:-1][::-1]

leet662: 二叉树的最大宽度

给定一个二叉树,编写一个函数来获取这个树的最大宽度。树的宽度是所有层中的最大宽度。
这个二叉树与满二叉树(full binary tree)结构相同,但一些节点为空。
每一层的宽度被定义为两个端点(该层最左和最右的非空节点,两端点间的null节点也计入长度)之间的长度。如输入: 

           1
         /   \
        3     2
       / \     \  
      5   3     9 

输出: 4
解释: 最大值出现在树的第 3 层,宽度为 4 (5,3,null,9)。 

【含hashmap[depth]的dfs递归】

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def widthOfBinaryTree(self, root: TreeNode) -> int:
        hashmap = {} # hashmap初始, 外部变量记录每层的宽度

        def dfs(node, width, depth):
            if not node:
                # 没有节点则宽度返回0
                return 0
            
            if depth not in hashmap:
                hashmap[depth] = width # 深度depth对应的宽度width
            # 递归
            l = dfs(node.left, 2 * width, depth +1)
            # 即使没有左节点,右分支还要加null的一个节点数
            r = dfs(node.right, 2 * width + 1, depth + 1)
            return max(l, r, width - hashmap[depth] + 1)

        return dfs(root, 0, 0) # 初始深度与宽度从1开始

d11

leet29: 两数相除

返回除法结果,不越界[-2**31, 2**31-1]
【正负/零划分】

class Solution:
    def divide(self, dividend: int, divisor: int) -> int:
        if divisor == 0:
            return None

        # 被除数为0
        if dividend == 0:
            return 0
        # 同正同负
        elif (dividend < 0 and divisor < 0) or (dividend > 0 and divisor > 0):
            # 同为负数时,最小负数//(-1)结果超越最大正数2**31-1
            return (dividend // divisor) if (dividend // divisor) <= 2 ** 31 - 1 else 2 ** 31 - 1
        # 一正一负
        else:
            return -(abs(dividend) // abs(divisor))

leet191: 位1的个数

一个数的二进制表示中1的个数
【带奇偶判断的逐右移位】

class Solution:
    def hammingWeight(self, n: int) -> int:
        if not n:
            return 0

        # 位依次右移通过n的奇偶性计数,奇数则加一
        res = 0
        while n:
            res += (n % 2)
            n >>= 1 # 右移1 or 【n //= 2】
        return res
        # --------or------
        # n = n&(n-1)位运算依次加一
        res = 0
        while n:
            n &= n - 1
            res += 1
        return res

leet203: 移除链表元素

移除所有值为val的节点,返回链表。如输入: 1->2->6->3->4->5->6, val = 6
输出: 1->2->3->4->5
【含有目标比较的自身递归】

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def removeElements(self, head: ListNode, val: int) -> ListNode:
        if not head or val == None:
            return None
        # 自身递归, 下一个节点指代的链表移除目标后的指向,是head.next
        head.next = self.removeElements(head.next, val)
        # head是目标则返回head.next, 否则返回当前head
        return head.next if head.val == val else head
            
        # --------or 不简洁方法--------------
        # 存入列表,后逐次移除,再建立链表
        if not head or val == None:
            return None
        list0 = []
        while head:
            list0.append(head.val) 
            head = head.next

        while val in list0:
            list0.remove(val)
        dummy = ListNode(0)
        p = dummy
        for i in list0:
            p.next = ListNode(i)
            p = p.next
        return dummy.next

            

d12

leet69: x的平方根

实现int sqrt(int x)函数
【牛顿迭代】

class Solution:
    def mySqrt(self, x: int) -> int:

        # 牛顿迭代法
        if x <= 1:
            return x # 条件中非负整数x

        res = x
        while res > x / res:
            res = (res + x / res) // 2
        return int(res)
        # -----------or 直接的库函数------------
        return int(math.sqrt(x))

leet617: 合并二叉树

合并的规则是如果两个节点重叠,那么将他们的值相加作为节点合并后的新值,否则不为 NULL 的节点将直接作为新二叉树的节点。如输入: 
    Tree 1                     Tree 2                  
          1                         2                             
         / \                       / \                            
        3   2                     1   3                        
       /                           \   \                      
      5                             4   7                  
输出: 
合并后的树:
         3
        / \
       4   5
      / \   \ 
     5   4   7

【两次判空与全存在的各自相加】

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def mergeTrees(self, t1: TreeNode, t2: TreeNode) -> TreeNode:
       # 返回其中仅一个的有值节点
       if not t1: return t2
       if not t2: return t1

       # 在两者均存在情况下同一位置节点值相加
       t1.val += t2.val
       # 递归
       t1.left = self.mergeTrees(t1.left, t2.left) # 合并左节点为新的t1节点
       t1.right = self.mergeTrees(t1.right, t2.right) # 合并右节点为新的t1节点
       # 返回新的节点
       return t1


leet113: 路径总和

求出根到叶子节点路径总和为固定值的所有路径集

给定一个二叉树和一个目标和,找到所有从根节点到叶子节点路径总和等于给定目标和的路径。

说明: 叶子节点是指没有子节点的节点。

示例:
给定如下二叉树,以及目标和 sum = 22,

            5
           / \
          4   8
         /   / \
        11  13  4
       /  \    / \
      7    2  5   1
返回:

[
 [5,4,11,2],
 [5,8,4,5]
]

【目标递减下叶子和当前更新的路径和与目标值的比较】

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def pathSum(self, root: TreeNode, sum: int) -> List[List[int]]:
        res = []

        def dfs(node, tmp, sum0):
            if not node:
                return 
            
            # 叶节点且是等于最后减下来的差值
            if not node.left and not node.right and node.val == sum0:
                tmp.append(node.val) # 放入当前路径中的叶子节点
                res.append(tmp)  # 存储当前完整的路径
            # 左右子树依次遍历,目标长度值逐次减去当前值
            dfs(node.left, tmp + [node.val], sum0 - node.val)  # 节点逐次放入候选路径集
            dfs(node.right, tmp + [node.val], sum0 - node.val)

       
        dfs(root, [], sum) # []: 当前符合条件的一条路径中的节点
        return res 
            

d13

leet1207: 独一无二的出现次数

数组中是否有独一无二的出现次数
【hashmap计数与值数组判重】

class Solution:
    def uniqueOccurrences(self, arr: List[int]) -> bool:

        if not arr: 
            return True
        count = {} # hashmap存储每个值出现的个数

        # 初始化每个数的长度count,逐次加一
        for i in arr: count[i] = 0
        for i in arr: count[i] += 1

        # hashmap中的值如果有重复则返回False, 若重则set后长度减少
        return len(set(count.values())) == len(count.values())

leet300: 最长上升子序列

给定一个无序的整数数组,找到其中最长上升子序列的长度。如输入: [10,9,2,5,3,7,101,18]
输出: 4 ,解释: 最长的上升子序列是 [2,3,7,101],它的长度是 4。

【双循环动态规划】

class Solution:
    def lengthOfLIS(self, nums: List[int]) -> int:
        if not nums:
            return 0

        # 动态规划,初始为长度1的【len(nums)】长序列 
        dp = [1] * len(nums)
        for i in range(len(nums)):  # (1, len(nums)):
            for j in range(i):
                # i与j相比较,逐次:【仅当】组成长为2的上升序列才更新dp[i]
                if nums[j] < nums[i]: 
                    dp[i] = max(dp[i], dp[j] + 1) # 取当前原始长度值与更新后的长度值中较大者
        
        # 返回【最长者】
        return max(dp)

leet669: 修剪二叉搜索树

【递归判断】

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def trimBST(self, root: TreeNode, L: int, R: int) -> TreeNode:
        if not root:
            return None
        # 当前节点值>R, 则修剪后的二叉数在当前节点左侧, 即"返回"修剪左侧后的二叉树; 返回修剪右侧后的二叉树同理。
        if root.val > R: return self.trimBST(root.left, L, R)
        if root.val < L: return self.trimBST(root.right, L, R)
        
        # 否则(root.val处于L,R中间), 分别修剪二叉树左右分支
        root.left = self.trimBST(root.left, L, R)
        root.right = self.trimBST(root.right, L, R)

        # 最终返回修改完成的二叉树
        return root

d14

leet121: 买卖股票的最佳时机

给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
如果你最多只允许完成一笔交易(即买入和卖出一支股票),设计一个算法来计算你所能获取的最大利润。
注意你不能在买入股票前卖出股票。如输入: [7,1,5,3,6,4]
输出: 5
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格。

【最低股价与最大利润同时更新】

class Solution:
    def maxProfit(self, prices: List[int]) -> int:
        if not prices:
            return 0 # 没有则获利0

        min0 = 999999
        maxp = -999999
        # 逐次更新
        for i in prices:
            min0 = min(min0, i) # 最低股价
            maxp = max(maxp, i - min0) # 最大利润
        return maxp

leet面试42: 连续子数组的最大和

输入一个整型数组,数组里有正数也有负数。数组中的一个或连续多个整数组成一个子数组。求所有子数组的和的最大值。
要求时间复杂度为O(n)。如输入: nums = [-2,1,-3,4,-1,2,1,-5,4]
输出: 6, 解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。

【max0 sum0初始索引0赋值,与sum0>0判断】

class Solution:
    def maxSubArray(self, nums: List[int]) -> int:
        if not nums:
            return 0

        max0 = nums[0]
        sum0 = nums[0]
        # 从第二个数开始判断累加
        for i in range(1, len(nums)):  
            # 上次累计和小于0则sum0置当前值
            if sum0 < 0: 
                sum0 = nums[i]
            else:
                sum0 += nums[i] # 否则累加
            max0 = max(max0, sum0) # 逐次更新连续子数组的最大和
        return max0 

leet279: 完全平方数

给定正整数 n,找到若干个完全平方数(比如 1, 4, 9, 16, ...)使得它们的和等于 n。你需要让组成和的完全平方数的个数最少。如输入: n = 12
输出: 3 ,解释: 12 = 4 + 4 + 4
【动态规划双循环解背包问题】

class Solution:
    def numSquares(self, n: int) -> int:
        if n == None:
            return 0
        dp = [0] * (n + 1)

        # 从1开始循环n遍
        for i in range(1, n + 1):
            dp[i] = i # 最坏情况每次加1 
            for j in range(1, int(math.sqrt(i)) + 1):
                # 最少完全平方数的个数
                dp[i] = min(dp[i], dp[i - j * j] + 1) # 动态转移方程
        return dp[-1]
        

d15

leet204: 计数质数

统计所有小于非负整数 n 的质数的数量。如输入: 10
输出: 4,解释: 小于 10 的质数一共有 4 个, 它们是 2, 3, 5, 7 。
【筛去法】

class Solution:
    def countPrimes(self, n: int) -> int: 
        if n == None or n <= 2:
            return 0

        count = 0
        isPrime = [1] * n # 假设初始全部是素数
        for i in range(2, n):
            if isPrime[i]: 
                count += 1

                # 筛去i的倍数
                j = 2 * i # 从2倍数开始
                # 在是素数的数的位置上置其为非素数
                while j < n:
                    isPrime[j] = 0 # I的倍数的值置非素数
                    j += i 
        return count
        

leet264: 丑数ii

找出第 n 个丑数。
丑数就是只包含质因数 2, 3, 5 的正整数。如输入: n = 10
输出: 12, 解释: 1, 2, 3, 4, 5, 6, 8, 9, 10, 12 是前 10 个丑数。
【三指针动态规划】

class Solution:
    def nthUglyNumber(self, n: int) -> int:
        if n == None:
            return None
        
        # 第0个丑数为1, 求出前n个丑数
        # 0r: dp = [1 for _ in range(n)]
        dp = [1] * n
        i2 = 0 
        i3 = 0
        i5 = 0
        # 三指针在取最小的丑数为对应索引上的数,逐次动态更新, 遍历n-1次即可
        for i in range(1, n):
            dp[i] = min(2 * dp[i2], 3 * dp[i3], 5 * dp[i5])
            if 2 * dp[i2] == dp[i]:
                i2 += 1
            if 3 * dp[i3] == dp[i]:
                i3 += 1
            if 5 * dp[i5] == dp[i]:
                i5 += 1

        # 最后一个数字为第n个丑数
        return dp[-1]

leet714: 买卖股票的最佳时机含手续费

给定一个整数数组 prices,其中第 i 个元素代表了第 i 天的股票价格 ;非负整数 fee 代表了交易股票的手续费用。
你可以无限次地完成交易,但是你每次交易都需要付手续费。如果你已经购买了一个股票,在卖出它之前你就不能再继续购买股票了。

返回获得利润的最大值。如输入: prices = [1, 3, 2, 8, 4, 9], fee = 2
输出: 8,解释: 能够达到的最大利润:
在此处买入 prices[0] = 1
在此处卖出 prices[3] = 8
在此处买入 prices[4] = 4
在此处卖出 prices[5] = 9
总利润: ((8 - 1) - 2) + ((9 - 4) - 2) = 8.
【买入卖出的动态规划】

class Solution:
    def maxProfit(self, prices: List[int], fee: int) -> int:
        if not prices or fee < 0:
            return None

        cash = 0 # 不持股票时的最大利
        hold = -prices[0] # 持有股票时的最大利
        
        # 比较两种操作的收益现金cash/hold
        for i in range(1, len(prices)):
            # 同一天卖出再买入(亏手续费)不比不进行操作好
            cash = max(cash, hold + prices[i] - fee) # 卖出时出手续费, cash在持有股票的利润的基础上卖出
            hold = max(hold, cash - prices[i]) # 买入
            
        return cash 

d16

leet122: 多次买卖股票的最大利

给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
如输入: [7,1,5,3,6,4]
输出: 7
解释:
在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出,
这笔交易所能获得利润 = 5-1 = 4 。
随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,
这笔交易所能获得利润 = 6-3 = 3 。

【同leet714 多次买卖的动态规划】

class Solution:
   def maxProfit(self, prices: List[int]) -> int:
       if not prices:
           return 0
           
       cash = 0
       hold = -prices[0] # 初始买入的第一支股票
       for i in range(1, len(prices)):
           cash = max(cash, hold + prices[i]) # 卖出
           hold = max(hold, cash - prices[i]) # 买入
       return cash

leet100: 相同的树

是否是相同的树
【同空、不同空判断后的自身递归】

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def isSameTree(self, p: TreeNode, q: TreeNode) -> bool:
        # 同时存在则是True
        if not p and not q: return True
        # 有一个不存在则False
        if not q or not q: return False
        if p.val != q.val:
            return False
        return self.isSameTree(p.left, q.left) and self.isSameTree(p.right, q.right)
         

leet437: 路径总和iii

不特定根节点和叶子节点,返回路径总和为固定值的路径数
【自身递归与dfs递归】

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def pathSum(self, root: TreeNode, sum: int) -> int:

        def dfs(node, sum0):
            if not node:
                return 0 # 无解
                
            count = 0 
            if sum0 == node.val: count = 1
            # 当前节点是一个解, 递归求解左右两边,均从给定数值中减去当前节点值
            return count + dfs(node.left, sum0 - node.val) + dfs(node.right, sum0 - node.val)
        
        if not root:
            return 0
        # 从自身左右边、根节点开始,寻找到符合条件的路径则计数,最终返回总数
        return self.pathSum(root.left, sum) + self.pathSum(root.right, sum) + dfs(root,   sum)

d17

lee315线段树

找出右边的值小于当前值的数的个数:
使用线段树构建左右子树,并从右到左计算前面的数小于当前值的数的个数,最终返回依次进入结果数组的list的倒置值。

class Node:
    def __init__(self, begin, end):
        self.begin = begin
        self.end = end
        self.mid = (begin + end) // 2
        self.left = None
        self.right = None
        self.count = 0 

    # 要是当前值比最小值都小就返回0,否则比较最小值大则说明有一个比较当前值小的值了。
    # 线段树递归自身的过程即是计算前方比当前值小的数的个数
    def add(self, num):
        self.count += 1
        if self.begin == self.end:
            return 0
        self.left = Node(self.begin, self.mid) if not self.left else self.left
        self.right = Node(self.mid + 1, self.end) if not self.right else self.right
        # 分情况提交左右子树结果
        if num <= self.mid: 
            return self.left.add(num)
        else:
            return self.left.count + self.right.add(num) # 左子树结果+右递归结果
        
class Solution:
    def countSmaller(self, nums: List[int]) -> List[int]:
        if not nums:
            return []

        # 使用自定义类初始化根节点
        root = Node(min(nums), max(nums))
        res = []
        # 依次返回倒叙数组中前面的数比较当前数小的值的个数
        for i in range(len(nums)-1, -1, -1):
            res.append(root.add(nums[i]))
        # 返回倒过来的正确数组
        return res[::-1]
        

leet1013: 划分为和的三个部分:

【list复制法】
将遍历到的前两个和数都去掉,双重遍历T(n)= O(n)

class Solution:
    def canThreePartsEqualSum(self, A: List[int]) -> bool:
        if not A:
            return False
        
        for i in range(len(A)-2):
            list0 = A[:]
            sum0 = sum(A[:i+1])
            for ii in range(0, i+1):
                list0.remove(A[ii])

            for k in range(i+1, len(A)-1):
                list1 = list0[:]
                sum1 = sum(A[i+1 : k+1]) 
                for kk in range(i+1, k+1):
                    list1.remove(A[kk]) 

                if sum1 == sum0 == sum(list1):
                    return True
        return False

leet71:简化路径

规范的路径表示写法转换为最简路径
【栈进栈出,判断'' '.' '..'()】

class Solution:
    def simplifyPath(self, path: str) -> str:
        st = []
        for i in path.split('/'):
            if i not in ['','.','..']:
               st.append(i)
            elif i == '..' and st:
                st.pop()
        return '/' + '/'.join(st)

d18

leet792 匹配子序列的单词数

【复制字符库,逐次后移匹配库起始点】

class Solution:
    def numMatchingSubseq(self, S: str, words: List[str]) -> int:
        if not S or not words:
           return 0

        resnum = 0
        for i in words:
            list0 = S[:] # 每次都要还原字符库
            point = 0# 匹配成功数

            # 遍历每个元素中的每个字母
            for ii in i:
                if ii in list0:
                    list0 = list0[list0.index(ii)+1 : ] # 在字符库中则从命中索引往后(子"序列"),更新作剩余字符库
                    point += 1
            if point == len(i):
                resnum += 1
        return resnum

leet410 分割数组的最大值

按要求分一个数组为m份,求其中子数组的最大值中最小一个
【二分查找、设定子数组最大的和mid作划分节点】

class Solution:
    def splitArray(self, nums: List[int], m: int) -> int:
        if not nums or not m:
            return None

        # 最小的最大值一定在l,r中徘徊,在终止条件l mid:
                    subsum = i
                    count += 1
                
            if count > m: # 划分子区间太多了,要提高mid
                l = mid + 1
            else:
                r = mid
        return r

leet108 有序数组转换为平衡二叉搜索树

【二分思想,拆分数组递归】(不用判断平衡)

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def sortedArrayToBST(self, nums: List[int]) -> TreeNode:
        if not nums:
            return None

        # 二分处为root,构建平衡搜索树; 其他递归求左右
        root = TreeNode(nums[len(nums) // 2])
        lnums = nums[ : len(nums) // 2]
        rnums = nums[len(nums) // 2 + 1 : ]
        root.left = self.sortedArrayToBST(lnums)
        root.right = self.sortedArrayToBST(rnums)
        return root

d19(52%)

leet 1208 尽可能使字符串相等

两个字符串对应位置移动总次数在指定步数内(ascii值的差),求字符串的最大可变换长
【滑窗记录更新最初位置、不断更新当前滑窗内移动步数最小值】

class Solution:
    def equalSubstring(self, s: str, t: str, maxCost: int) -> int:
        if not s or not t:
            return 0
        
        # 经典滑动窗口,每一次一动一个格子
        l = 0 # 记录好滑窗起始判断的位置
        cost = 0
        maxlen = 0
        for i in range(len(s)):
            cost += abs(ord(s[i]) - ord(t[i])) 
            
            while cost > maxCost: # 该窗口不可用,代价减掉旧的初始点的代价
                cost -= abs(ord(s[l]) - ord(t[l])) 
                l += 1 # 窗口起始位置递增
            maxlen = max(maxlen, i - l + 1) # 更新最大长,为当前走到的滑窗内的最大长
        
        return maxlen

leet374:猜数字大小

原本的数字比猜出的数字小则返回1,求最后的正确数字
【二分法压缩区间】

# The guess API is already defined for you.
# @param num, your guess
# @return -1 if my number is lower, 1 if my number is higher, otherwise return 0
# def guess(num: int) -> int:

class Solution:
    def guessNumber(self, n: int) -> int:
        if not n:
            return None

        l,r = 1, n
        
        # 二分思想收缩区间
        while l < r:
            m = (l + r) // 2
            if guess(m) == 1:
                l = m + 1
            elif guess(m) == -1:
                r = m
            else:
                return m
        return l # 总有对的时候

leet949 给定数字能组成的最大时间

如:1 2 3 4组成的最大数字为'23:41'
【判断分钟数、三重遍历后求index4】

class Solution:
    def largestTimeFromDigits(self, A: List[int]) -> str:
        if not A or min(A) >2 or len(A) != 4:
            return ''

        hour = 0
        minu = 0
        max0 = -1
        for i in range(4):
            
            for j in range(4):
                if j != i:
                
                    for k in range(4):
                        if k != j and k != i:
                            l = 6 - i - j - k # 计算没有用到的数索引index4
                            hour = A[i] * 10 + A[j]
                            minu = A[k] * 10 + A[l]
                            if hour < 24 and minu < 60:
                                max0 = max(max0, hour * 60 + minu) # 最大分钟数更新

        # 返回整数串;没有满足条件的结果返回'';不足的格式用0填充
        return '%02d:%02d'% (max0//60,max0%60) if max0 >= 0 else '' 

d20(51%)

leet997 找到小镇的法官

法官不trust人任何人,任何人(除法官外)都trust法官
【图遍历每个人,出度为0入度N-者】

class Solution:
    def findJudge(self, N: int, trust: List[List[int]]) -> int:
        if not N: # 至少一个人
            return -1
        if not trust:
            return N if N == 1 else -1
        indegree = [0] * (N+1) # 因为人的编号从1开始
        outdegree = [0] * (N+1)
        for i in trust:
            outdegree[i[0]] += 1
            indegree[i[1]] += 1

        for i in range(1, N+1): # 第0个值是空
            # 其他N-1个人都相信他,自己不相信任何人(出度为0入度
            if outdegree[i] == 0 and indegree[i] == N - 1:
                return i
        return -1 

leet225 队列进出

模拟queue;peak返回值不弹出
【list只pop(0)作移除元素或pop()】

class MyQueue:

    def __init__(self):
        """
        Initialize your data structure here.
        """
        self.queue = []

    def push(self, x: int) -> None:
        """
        Push element x to the back of queue.
        """
        self.queue.append(x)

    def pop(self) -> int:
        """
        Removes the element from in front of queue and returns that element.
        """
        if not self.empty():
            val = self.queue.pop(0)
            return val

    def peek(self) -> int:
        """
        Get the front element.
        """
        if not self.empty():
            return self.queue[0] # 与pop()均返回第一个元素即可

    def empty(self) -> bool:
        """
        Returns whether the queue is empty.
        """
        return not self.queue  


# Your MyQueue object will be instantiated and called as such:
# obj = MyQueue()
# obj.push(x)
# param_2 = obj.pop()
# param_3 = obj.peek()
# param_4 = obj.empty()

leet423 从英文中重建数字

【s.count计数、独特性】

class Solution:
    def originalDigits(self, s: str) -> str:
        if not s:
            return ''

        # list0 =['zero', 'one','two','three','four','five','six','seven','eight','nine']
        # 默认是被打乱的英文字母,所以只要知道特有字母对应的数字格式即可,如'w'只在2中出现
        n0 = s.count('z')
        n2 = s.count('w') 
        n6 = s.count('x')
        n8 = s.count('g')

        # 依次可以逐步确定对应的数字个数,不能使用多次出现的字符如nine one的n
        n3 = s.count('t') - n2 - n8
        n7 = s.count('s') - n6  
        n5 = s.count('v') - n7
        n4 = s.count('f') - n5
        n9 = s.count('i') - n5 - n6 - n8
        n1 = s.count('o') - n0 - n2 - n4

        list0 = [n0, n1, n2, n3, n4,  n5, n6, n7, n8, n9]
        return ''.join( [str(i) * n for i,n in enumerate(list0)] ) # 从小到大

d21(100%)

leet7 整数反转

如-321转换为-123
【两种情况判断、str逆转】

class Solution:
    def reverse(self, x: int) -> int:
        if x == None:
            return None

        newx = int(str(x)[::-1]) if x >=0 else int('-' + str(x)[1:][::-1])
        return 0 if newx > 2**31 - 1 or newx < -2**31 else newx

leet165 比较版本号

如输入: version1 = "7.5.2.4", version2 = "7.5.3"
输出: -1,即v1 【递进字符串、循环终止条件为有值比较、‘55’.split(‘.’)返回[] 】

class Solution:
    def compareVersion(self, version1: str, version2: str) -> int:
        
        if not version1 or not version2:
            return None

        while version1 or version2: 
            # 两个都没有点
            if '.' not in version1 and '.' not in version2:
                if int(version1) > int(version2):
                    return 1
                elif int(version1) < int(version2):
                    return -1
                else:
                    return 0
                    
            # return ('222'.split('.')[1:]) # true:[] 如果有一个不存在.也没有关系
            if int(version1.split('.')[0]) > int(version2.split('.')[0]):
                return 1
            elif int(version1.split('.')[0]) < int(version2.split('.')[0]):
                return -1
            else:
                # 比较下层
                version1 = '.'.join(str(int(i)) for i in version1.split('.')[1:] ) if '.' in version1 else '0' # 消除01这样的版本表示为1
                version2 = '.'.join(str(int(i)) for i in version2.split('.')[1:] ) if '.' in version2 else '0' 
             
        return None

leet515 找出二叉树每行中的最大值

输入:
1
/
3 2
/ \ \
5 3 9
输出: [1, 3, 9]

【有层次号标识的层次遍历】

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def largestValues(self, root: TreeNode) -> List[int]:
        if not root:
            return []
        
        list0 = [] 
        l = 0
        def l_visit(node, l):
            if node:
                if len(list0)

d22 (67%)

leet141 环形链表

链表有环则返回True
【重置已遍历的值】

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def hasCycle(self, head: ListNode) -> bool:
        if not head:
           return False

        while head: # 只要之前遍历过的就是有环
            if head.val =='findme':
                return True
            head.val = 'findme'
            head = head.next
        return False

leet142 环形链表II

链表有环则返回环的起始节点
【遍历返回重置后的节点】

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def detectCycle(self, head: ListNode) -> ListNode:
        if not head:
            return None
        
        while head: # 遍历,如果有环则返回当前节点
            if head.val == 'visited':
                return head
            head.val = 'visited'
            head = head.next

        return None
            

leet491 递增子序列

找到数组的所有递增子序列,len>2、不用连续
【保存当前起始位置、逐次选后面满足条件元素的位置加入,递归生成有效集】

class Solution:
    def findSubsequences(self, nums: List[int]) -> List[List[int]]:
        if not nums:
            return []

        set0 = set()
        def gen(curloc, tmp):
            # 元组长大于2才能加入结果集合
            if len(tmp) > 1 and tmp not in set0:
                set0.add(tmp)

            # 在当前值集合tmp的条件下,依次加入后面大于该tmp最大值的元素
            for i in range(curloc + 1, len(nums)):
                if tmp[-1] <= nums[i]:
                    gen(i, tmp + (nums[i],) ) # 更新tmp后满足"不递减"条件的起点位置

        # 遍历列表的每个位置,生成当前位置为起点满足条件的子集
        for i, e in enumerate(nums):
            gen(i, (e, )) # i放入元组中

        return [list(i) for i in set0]

d23 (60%)

leet102 二叉树层序遍历

层序遍历输出各个层次的节点值,分别用list表示
【bfs递归、层次值更新】

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def levelOrder(self, root: TreeNode) -> List[List[int]]:
        if not root:
            return []
        
        list0 = []
        def bfs(node, l):
            if node:
                if len(list0) < l+1:
                    list0.append([])
                list0[l].append(node.val)
                bfs(node.left, l+1)
                bfs(node.right, l+1)

        bfs(root, 0)
        return list0

leet662 二叉树最大宽度

【bfs(层次遍历)非递归与dfs递归】
【bfs+队列的非递归方式:左右节点标识、依次遍历队列更新最大宽】

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def widthOfBinaryTree(self, root: TreeNode) -> int:
        if not root:
            return 0

        # bfs非递归
        queue = [(root, 0, 0)] # 元列表, 分别表示当前节点、depth、左右节点宽度位置loc(分别两倍、两倍+1个宽度)
        curdepth = 0 # 当前层次
        start, loc = 0, 0
        max0 = 0 # 最大宽度
        # 逐渐变多的元组集(左、右)
        for node, depth, loc in queue:
            if node:
                queue.append([node.left, depth+1, 2*loc]) # 逐增层次的同时,将左右节点原先宽度位置更新:2*x和2x+1
                queue.append([node.right, depth+1, 2*loc+1])
                # 逐增当前的深度值,并更新当前深度、起始位置
                if curdepth != depth: 
                    curdepth = depth
                    start = loc
                max0 = max(max0, loc - start + 1)
        
        return max0
                

【dfs递归:默认每层起始位置、更新最大宽、hash0.setdefault()】


class Solution:
   def widthOfBinaryTree(self, root: TreeNode) -> int:

       if not root:
           return 0

       self.max0 = 0 # 最大宽
       start = {}
       def dfs(node, depth=0, loc=0):
           if node:
               start.setdefault(depth, loc) # 就是当前层depth的value不会再变了
               # 更新现在的最大宽
               self.max0 = max(self.max0, loc - start[depth] + 1)
               # dfs,每增加一层depth,左边的就要翻倍,右边的就要翻倍加一;
               # 即只要右边有节点,就是"2*loc+1 - 起始位置 + 1"的宽度,如例中的9:其宽度为2*(2*0+1)+1 - 0 + 1 = 4
               # 如果是例2的对称例,start则与右子树的loc相等
               dfs(node.left, depth+1, 2*loc) 
               dfs(node.right, depth+1, 2*loc+1)
           
       dfs(root)
       return self.max0 # 闭包外用到,故self方式
               

leet453 最小移动次数使数组元素相等

输入:
[1,2,3]
输出:
3, 解释: 只需要3次移动(注意每次移动会增加两个元素的值):
[1,2,3] => [2,3,3] => [3,4,3] => [4,4,4]

【数学题,移动后总和sum'(nums)与最小值的变化幅度m】

class Solution:
    def minMoves(self, nums: List[int]) -> int:
        # m次移动后,sum(nums)+m(n-1) = len(nums)*mean,且min(nums) + m = mean即原本"最小值"m次加一最终变mean!
        # sum(nums) + m * (len(nums)-1) = len(nums)* (min(nums)+m)
        # 以上求m : m = sunsum(nums) - len(nums) * min(nums)
        if not nums:
            return 0

        return sum(nums) - len(nums) * min(nums)

d24

leet22 括号生成

【初始'()'后循环n-1次、对所有上一次结果遍历更新、找打所有空位进行set0.update】

class Solution:
    def generateParenthesis(self, n: int) -> List[str]:
        if not n:
           return []
        
        res = set(['()'])
        for _ in range(n-1): # n个括号
            tmp = set()
            for r in res: # 对之前结果所有都更新
                # 每个结果string有len(string)个空位;update支持迭代
                tmp.update( set(r[:j]+'()'+r[j:] for j in range(len(r))) ) # 
            res = tmp
        return list(res)

leet13 罗马数字转整数

输入: "LVIII"
输出: 58, 解释: L = 50, V= 5, III = 3.
【hash、小值字符在大值之前】

class Solution:
    def romanToInt(self, s: str) -> int:
        if not s:
            return 0

        has = {'M':1000, 'D':500, 'C':100, 'L':50, 'X':10, 'V':5, 'I':1 }
        res = 0
        for i in range(len(s)):
            # 当前字符是最后一个,当前字符对应值<后一个
            if i < len(s) - 1 and has[s[i]] < has[s[i+1]]: # 减法
                res -= has[s[i]]
            else:
                res += has[s[i]] # 加法
        return res

leet227 基本运算器2
实现一个基本的计算器来计算一个简单的字符串表达式的值。
字符串表达式仅包含非负整数,+, - ,*,/ 四种运算符和空格 。 整数除法仅保留整数部分。
输入: " 3+5 / 2 "
输出: 5
【stack、has_ops[op]对上个运算符和之后数字运算、转为stack和的运算】

import operator
class Solution:
    def calculate(self, s: str) -> int:
        if not s:
            return None

        stack = []
        has_ops = {'+':lambda x: stack.append(x),
                   '-':lambda x: stack.append(-x),
                   '*':lambda x: stack.append(stack.pop()*x),
                   '/':lambda x: stack.append( int( operator.truediv(stack.pop(), x) ) ) } # 仅保留整数即可
        op = '+'
        num = 0
        for i in s+'+':# 为了对于最后的操作符和数字进行计算
            if i.isdigit():
                num = num*10 + int(i) # 整个数字
            elif i !=' ': # 非空字符' '
                # 上上次的操作符对应上次数字,"*"、"/"时与上上上次数字运算后放入加运算栈,加减法时独自放入
                has_ops[op](num)

                op = i
                num = 0
        
        return sum(stack)        

d25

leet373 查找和最小的k对数字

输入: nums1 = [1,1,2], nums2 = [1,2,3], k = 2
输出: [1,1],[1,1],解释: 返回序列中的前 2 对数:
[1,1],[1,1],[1,2],[2,1],[1,2],[2,2],[1,3],[1,3],[2,3]
【sorted()中用带有lambda的key】

class Solution:
    def kSmallestPairs(self, nums1: List[int], nums2: List[int], k: int) -> List[List[int]]:
        if not nums1 or not nums2 or not k:
            return []
        list0 = []
        for i in nums1:
            for j in nums2:
                list0.append([i,j])
        # 对数字对按和升序
        list0 = sorted(list0, key = lambda x: x[0]+ x[1] )   # 和为升序条件
        return list0[:k] # 返前k个

leet945 使数组唯一的最小增量

输入:[3,2,1,2,1,7]
输出:6,解释:经过 6 次 move 操作,数组将变为 [3, 4, 1, 2, 5, 7],可看出 5 次或 5 次以下的 move 操作是不能让数组的每个值唯一的。
【记录累积的步数、当前可用值】

class Solution:
    def minIncrementForUnique(self, A: List[int]) -> int:
        if not A:
            return 0
        A.sort()
        moves = 0 
        cur_avalible = 0
        # 升序后逐次遍历,记录步数、可用值
        for i in A:
            moves += max(cur_avalible - i, 0) # 步数一定大于零,第一个数moves=0
            cur_avalible = max(cur_avalible,i) + 1 # 当前可用值=当前值与当前可用值的较大值+1
        return moves # sum(set0) - sum(A) # moves
        #   如                1   1     2     2       3     7
        # moves:          0   1  1+1   2+2  4+2 6+max(6-7,0)=6
        # cur_avalible: 2   3   4   5   6   

leet2 两数相加

【当前进位、之前进位+分配空间,头建链表】

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:
        if not l1 and not l2:
            return None
        
        list0 = []
        i = 0
        while l1 or l2: 
            # 只有一个数存在,有进位则两者相加,否则sum0=当前值
            if not l1:
                sum0 = l2.val if len(list0) 9: 
                if len(list0)

d26
1/3

leet 912 排序数组

快速排序

class Solution:
    def sortArray(self, nums: List[int]) -> List[int]:
        if not nums:
            return []
        
        def quick(l, r):
            if l > r:
                return nums
            i,j,pivot = l, r, l
            while i < j:
                while i < j and nums[j] > nums[pivot]:
                    j -= 1
                while i < j and nums[i] <= nums[pivot]:
                    i += 1
                nums[i], nums[j] = nums[j], nums[i]
            nums[j], nums[pivot] = nums[pivot], nums[j] # 与J交换
            quick(l, j - 1) # 分治左排序
            quick(j + 1, r) # 分治右排序
            return nums

        return quick(0, len(nums) - 1)


2/3

leet 1143 最长公共子序列

【dp pre动态规划】

class Solution:
    def longestCommonSubsequence(self, text1: str, text2: str) -> int:
        if not text1 or not text2:
            return 0

        # pre与dp动态规划
        pre = [0 for _ in range(len(text2) + 1)]
        dp = [0 for _ in range(len(text2) + 1)]

        for i in range(len(text1)):
            j = 0
            for j in range(1, len(text2) + 1):
                if text1[i] == text2[j-1]: # 之前最优解+1的条件
                    dp[j] = pre[j-1] + 1
                else:
                    dp[j] = max(pre[j], dp[j-1]) # 取最优
                pre[j-1] = dp[j-1] # 更新pre
            pre[j] = dp[j] # 更新pre至len(text2))
        return dp[-1]

你可能感兴趣的:(103. leetcode笔记(1~60))