LeetCode每日一题6月记录

LeetCode每日一题6月记录

  • 6.1 拥有最多糖果的孩子
  • 6.2 求1+2+3......+n之和
  • 6.3 新21点
  • 6.4 除自身以外数组的乘积
  • 6.5 螺旋打印矩阵
  • 6.6 最长连续序列
  • 6.7单词接龙 II
  • 6.8 等式方程的可满足性
  • 6.9把数字翻译成字符串
  • 6.10回文数
  • 6.11 每日温度
  • 6.12 三数之和
  • 6.13 爬楼梯
  • 6.14 转变数组后最接近目标值得数组和
  • 6.15最长公共前缀
  • 6.16 二叉树的序列化与反序列化
  • 6.17 最佳观光组合
  • 6.18 从先序遍历还原二叉树
  • 6.19 验证回文串
  • 6.20 正则表达式匹配
  • 6.21 二叉树中的最大路径和
  • 6.22 模式匹配
  • 6.23 二进制求和
  • 6.24 最接近的3数之和
  • 6.25 单词拆分
  • 6.26 移除重复节点
  • 6.27 缺失的第一个正数
  • 6.28 长度最小的子数组
  • 6.29数组中的第K个最大元素
  • 6.30用两个栈模拟队列

6.1 拥有最多糖果的孩子

原题链接.

找出原糖果数里最大的,随后遍历数组,判断元素加上候选糖果之后是否大于原来的最大值。

6.2 求1+2+3…+n之和

原题链接.
首先发现递归或者迭代方法的话都要考虑一下终止条件问题。
所以可以利用逻辑操作里面的短路操作

n > 1 and self.sumNums(n - 1)

如果n是1就不会调用下一个n-1了,就终止了迭代。

6.3 新21点

原题链接.
采用dp思想,dp[i]为当前分数为i时,比赛最后胜利的概率。
当分数在[K,min(N,K+W-1)]中时已经获胜,所以概率dp取值1,在其之后的取值要么大于N失败要么大于K+W是无法达到的取值,所以概率dp为0。
接下来状态转移函数可以很清晰的得到
在这里插入图片描述
但是这样的话时间效率太慢了,可以继续优化一下转移函数
在这里插入图片描述
这样就可以用常数时间算出每一个dp值了

6.4 除自身以外数组的乘积

原题链接.
1.典型用空间换时间的类型,分两趟遍历用数组L和R分别记录从数组第一个元素到i和从最后一个元素到i的乘积,对应L和R元素相乘就是所求结果。
2.空间复用,从右向左第二次遍历时,直接在L数组对应位置乘上一个保存右边之积变量,然后把L作为输出。

6.5 螺旋打印矩阵

原题链接.
用4个变量记录已经还没打印部分的上下左右四个边界,一个循环打印一圈,并每部分输出时判断是否越界了。

class Solution:
    def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
        if not matrix: return []
        l, r, t, b, res = 0, len(matrix[0]) - 1, 0, len(matrix) - 1, []
        while True:
            for i in range(l, r + 1): res.append(matrix[t][i]) # left to right
            t += 1
            if t > b: break
            for i in range(t, b + 1): res.append(matrix[i][r]) # top to bottom
            r -= 1
            if l > r: break
            for i in range(r, l - 1, -1): res.append(matrix[b][i]) # right to left
            b -= 1
            if t > b: break
            for i in range(b, t - 1, -1): res.append(matrix[i][l]) # bottom to top
            l += 1
            if l > r: break
        return res

来自LeetCode题解: Krahets.

6.6 最长连续序列

原题链接.
题目要求o(n)时间那么访问数据的操作必须是按数组地址或者hash表这种O(1)时间的。因为乱序,所以使用hash表。
然后每个数据数据必须访问常数次数,考虑判断的起始条件,如果一个数字是一个连续序列的起始元素我们才进入遍历阶段,那么每个元素只要访问一次。
用变量保存历史最长连续序列的长度,最后返回即可。

6.7单词接龙 II

原题链接.
把字典里每个单词看作一个节点,可以转换的单词之间建立一条边。
因为是找最短路径,采用bfs搜索,把达到目的节点所花费轮数最少的路径加入节点即可。
记录每个节点的前序节点,输出路径时从最后节点逆序输出。

6.8 等式方程的可满足性

原题链接.
使用并查集结构把等式遍历一边,构造出几个联通分量。
再遍历不等式,如果不等式两个节点在一个联通分量里面则报错。

6.9把数字翻译成字符串

原题链接.
利用dp思想,利用dp[i]储存从左到i位该数字可以翻译的不同数目,则转移方程可以写成
dp[i] = dp[i-2] + dp[i-1] if nums[i-1]*10 + nums[i] 属于10到25之间,否则
dp[i] = dp[i-1]

6.10回文数

原题链接.
1.转换为字符串或者数字,双指针处理
2.按int型处理,获取x后一半数字的反转,跳出循环时,half_rever和剩余x位数相等或等多一位(原x为奇数),返回结果时需要判断两下。

        half_rever = 0
        while x > half_rever:
            half_rever = half_rever *10 + x%10
            x //= 10

6.11 每日温度

原题链接.
利用单调栈思想,维持一个递减栈,正序遍历每次不符合条件出栈时,把出栈元素对应结果即为cur-s[-1];逆序遍历时则是设置进栈元素对应结果为s[-1]-cur.

class Solution:
    def dailyTemperatures(self, T: List[int]) -> List[int]:
        s = []
        out = [0] * len(T)
        for i in range(len(T)-1,-1,-1):
            temperature = T[i]
            while s and T[s[-1]] <= temperature:
                s.pop()
            if s:
                out[i] = s[-1] - i
            s.append(i)
        return out

6.12 三数之和

原题链接.
1.排序原数组
2.遍历数组,对于每个小于等于0的元素,在它之后的数组里用双指针从头尾向中间寻找和为0的元素.
3.要求不能出现重复元素,所以在开始遍历数组时要过滤相等元素,只处理每个数字第一次出现;后面双指针操作时,也要过滤。

class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        n=len(nums)
        res=[]
        if(not nums or n<3):
            return []
        nums.sort()
        for i in range(n):
            if nums[i] > 0:
                return res
            #重复元素只要第一个
            if(i>0 and nums[i]==nums[i-1]):
                continue
            l = i + 1
            r = n - 1
            while l<r:
                if nums[i] + nums[l] +nums[r] == 0:
                    res.append( [nums[i] , nums[l] , nums[r]])
                    while(l<r and nums[l]==nums[l+1]):
                        l += 1
                    while(l<r and nums[r]==nums[r-1]):
                        r -= 1
                    l -= 1
                    r -= 1
                elif(nums[i]+nums[l]+nums[r]>0):
                    r-=1
                else:
                    l+=1
        return res

6.13 爬楼梯

原题链接.
经典dp问题,但是边界处理可以简化一点,视为从0开始的斐波那契数列

class Solution:
    def climbStairs(self, n: int) -> int:
        dp1 = 0
        dp2 = 1
        for i in range(n):
            dp1,dp2 = dp2,dp1+dp2
        return dp2

6.14 转变数组后最接近目标值得数组和

原题链接.
把数组排序后,利用二分法枚举所有可能的值。
在这个基础上可以再用一次二分法,寻找第一个大于target得value值。

6.15最长公共前缀

原题链接.
横向和纵向扫描都可,既对比所有字符串同一位置元素是否相等或者逐次比较两个字符串的公共前缀。

class Solution:
    def longestCommonPrefix(self, strs: List[str]) -> str:
        if not strs:
            return ''
        s = strs[0]
        for str in strs:
            while str.find(s) != 0:
                s = s[:-1]
        return s

6.16 二叉树的序列化与反序列化

原题链接.
两者需要以一个共同的树遍历模式进行,这里以先序遍历为例子。
1.序列化
用同一个变量保存传递序列化的值

def serialize(self, root):
        """ Encodes a tree to a single string.
        :type root: TreeNode
        :rtype: str
        """
        def rserialize(root, string):
            """ a recursive helper function for the serialize() function."""
            # check base case
            if root is None:
                string += 'None,'
            else:
                string += str(root.val) + ','
                string = rserialize(root.left, string)
                string = rserialize(root.right, string)
            return string
        
        return rserialize(root, '')

2.反序列化
先把序列化结果预处理一下再调用递归程序还原成树

def deserialize(self, data):
        """Decodes your encoded data to tree.
        :type data: str
        :rtype: TreeNode
        """
        def rdeserialize(l):
            """ a recursive helper function for deserialization."""
            if l[0] == 'None':
                l.pop(0)
                return None
                
            root = TreeNode(l[0])
            l.pop(0)
            root.left = rdeserialize(l)
            root.right = rdeserialize(l)
            return root

        data_list = data.split(',')
        root = rdeserialize(data_list)
        return root

6.17 最佳观光组合

原题链接.
对于一个数组,我们要求出A[i] + A[j] + i - j最大的i与j的组合(i O ( n 2 ) O(n^2) O(n2)的,但是我们发现对于一个j而言我们只需要知道在他前面出现的最大 A [ i ] + i A[i]+i A[i]+i即可,所以可以在遍历J的时候一直维护一个变量,存储出现过的最大的 A [ i ] + i A[i]+i A[i]+i

class Solution:
    def maxScoreSightseeingPair(self, A: List[int]) -> int:
        res = 0
        mx1 = A[0]
        for i in range(1,len(A)):
            res = max(res,mx1+A[i]-i)
            mx1 = max(mx1,A[i]+i)
        return res

6.18 从先序遍历还原二叉树

原题链接.
1.遍历字符串,根据‘–’数量判断节点应该在的层数。
2.根据节点的层数信息,考虑是先序遍历,用一个list保存当前访问路径的话,如果层数和list元素个数相等就说明当前节点是末尾元素的左儿子;如果不是,list需要回溯到当前节点层数的长度,然后当前节点作为末尾元素的右儿子。

6.19 验证回文串

原题链接.
双指针,一边过滤非数字字母一边判断即可。

6.20 正则表达式匹配

原题链接.
利用动态规划思想求解。
1.边界条件,p和s为空的判断。
2.设立dp[i][j]表示s[:i]p[:j]匹配与否,然后寻找转移方程,初始值为dp[0][0] = True和dp[0][1] = False
3.当i = 0时,p[j-1] == ‘*’的话,dp[0][j] = dp[0][j-2]因为*可以代表前一元素不出现。
4.随后根据转移方程

                if s[i-1] == p[j-1] or p[j-1] == '.':
                    dp[i][j] = dp[i-1][j-1]
                    #相等则照常
                elif p[j-1] == '*':
                    if p[j-2] == s[i-1] or p[j-2] == '.':
                        #匹配0个或者继续往下匹配
                        dp[i][j] = dp[i-1][j] or dp[i][j-2]
                    else:
                    #出现0个p[j-2]
                        dp[i][j] = dp[i][j-2]
                else:
                    dp[i][j] = False
                    #匹配不了

6.21 二叉树中的最大路径和

原题链接.
1.用一个全局变量保存最大值。
2.对于一个节点来说,包括他的最大路径可能分为以他为根的左右子树中或者单纯的经过他。
3.设置辅助递归函数,寻找节点最大路径。以他为根的左右子树中最大路径为左右子树的最大路径加自身值;被调用的子节点返回自身左右子树中单边最大的路径值。

class Solution:
    def __init__(self):
        self.max_sum = float('-inf')
    def maxPathSum(self, root: TreeNode) -> int:
        self.helper(root)
        return self.max_sum
        
    def helper(self,root):
        if not root:
            return 0
        left_gain = max(self.helper(root.left), 0)
        right_gain = max(self.helper(root.right), 0)

        node_in_path = left_gain + right_gain + root.val
        self.max_sum = max(self.max_sum,node_in_path)
        return root.val + max(left_gain , right_gain)

6.22 模式匹配

原题链接.
根据value总长度以及a,b个数枚举对应单词的长度,然后遍历value检查是否满足,有则返回True,都没有则返回False。

6.23 二进制求和

原题链接.
模拟正常整数加法的进位过程。

class Solution:
    def addBinary(self, a: str, b: str) -> str:
        if not a or not b: return a or b
        
        a, b, ans = a[::-1], b[::-1], []
        # carry: 进位
        i = j = carry = 0   
        while i < len(a) or j < len(b) or carry:
            n1 = int(a[i]) if i < len(a) else 0
            n2 = int(b[j]) if j < len(b) else 0
            carry, cur = divmod(n1 + n2 + carry, 2)
            ans.append(str(cur))
            i, j = i + 1, j + 1
        return ''.join(ans[::-1])

6.24 最接近的3数之和

原题链接.
排序数组后,使用双指针遍历并同时更新结果。

6.25 单词拆分

原题链接.
动态规划,dp[i]标志s[:i]能被划分与否,双重循环遍历。

class Solution:
    def wordBreak(self, s: str, wordDict: List[str]) -> bool:
        n = len(s)
        dp = [False] * (n+1)
        dp[0] = True
        for i in range(1,n+1):
            for j in range(0,i):
                if dp[j] and s[j:i] in wordDict:
                    dp[i] =True
                    #剪枝
                    break
        return dp[-1]

6.26 移除重复节点

原题链接.
1.用一个hash表储存出现过的节点,再遇到相同值,直接跳到下一个节点。
2.进阶,不用缓冲存储区。其实说白了就是暴力法,n次遍历链表,每次消除一个重复值。

6.27 缺失的第一个正数

原题链接.
题目要求只能用常数辅助空间,时间复杂度也要是O(n)的,我们就只能另辟蹊径,利用原数组来帮我们存储一下信息。
1.利用数组的index信息,遍历数组,对于每个值value把nums[value-1]置为相反的负数;之后再遍历一次数组,哪一位的值能是正的,那么index+1就是要找的缺失值;对于数组中原本就有的正数,可以统一标记为len(nums)+1,因为只要出现了负数,缺失值肯定小于len(nums).
2.把数组元素各归其位,把nums[nums[i]-1]和nums[i]对调,让原本的nums[i]待到该待的位置,之后遍历数组,位置不对的index+1就是要返回的缺失值。

class Solution:
    def firstMissingPositive(self, nums: List[int]) -> int:
        n = len(nums)
        for i in range(n):
            while 1 <= nums[i] <=n and nums[nums[i]-1]!= nums[i]:
            #避免重复值出现死循环,加一个不等判断
                nums[nums[i] - 1], nums[i] = nums[i], nums[nums[i] - 1]
            
        for i in range(n):
            if nums[i] != i+1:
                return i+1
        return n+1

6.28 长度最小的子数组

原题链接.
采用双指针或者说滑动窗口的方法,窗口内元素之和大于s则左边收缩,小于s则右边扩展,每次比较窗口大小和结果。

class Solution:
    def minSubArrayLen(self, s: int, nums: List[int]) -> int:
        if not nums:
            return 0
        
        n = len(nums)
        ans = n + 1
        l, r = 0, 0
        total = 0
        while r < n:
            total += nums[r]
            while total >= s:
                ans = min(ans,r-l+1)
                total -= nums[l]
                l += 1
            r += 1
        return 0 if ans == n+1 else ans

6.29数组中的第K个最大元素

原题链接.
借助快排里面的快速划分思想,我们可以再线性时间内找到任意第k个元素。
首先写出快速划分函数

        def partition(l,r,pivot):
        #比较的index
            p = nums[pivot]
            nums[r],nums[pivot] = nums[pivot],nums[r]
            store = l
            for i in range(l,r):
                if nums[i] < p:
                    nums[store],nums[i] = nums[i],nums[store]
                    store += 1
            nums[r],nums[store] = nums[store],nums[r]
           	#最后应在的位置index
            return store

如果快速划分真好找到我们需要的元素就可以直接返回,如果在左或者右则调整划分区间即可。

def select(l,r,k):
            #该函数寻找的k为第k小元素
            if l == r:
                return nums[l]
            pivot = random.randint(l, r)
            pivot = partition(l, r, pivot)

            if k == pivot:
                return nums[k]
            elif k < pivot:
                return select(l, pivot - 1, k)
            else:
                return select(pivot + 1, r, k)

最后题目是K大需要做一个小调整

        return select(0, len(nums) - 1, len(nums) - k)

6.30用两个栈模拟队列

栈是先进后出,而队列是先进先出,那么栈中数据在出栈时进到另外一个栈再出来不久完成了反转变成先进先出了。

class CQueue:

    def __init__(self):
        self.s1 = []
        self.s2 = []

    def appendTail(self, value: int) -> None:
        self.s1.append(value)

    def deleteHead(self) -> int:
        if not self.s2:
            if not self.s1:
                return -1
            else:
                while self.s1:
                    self.s2.append(self.s1.pop())

        return self.s2.pop()

你可能感兴趣的:(LeetCode)