2020-07算法刷题集

前言

前段时间一直忙于上课与其它事情,一直都没有抽出时间来刷算法题,现在看来,浪费了许多时间,非常后悔。
错过了昨天,今天将是一个非常好的起点。所以重新开始刷算法题。
(2020-07-25)这段时间每天刷着一道简单难度和一道中等难度的题目,花费的时间也不多。只是总在询问自己刷题的意义在什么地方?今天有了能让自己满意的答案——享受于每一个逻辑和细节处理,享受每一个字符,每一句代码所造成的影响。或许有时会急躁于一个个疏忽,但最终都会明白,急躁并不能解决问题,静下来才能知道问题出在哪里。问题的形成和解决是一个过程,需要经验,需要冷静,同样也需要跨越自己现在的位置。

  • 0715-一年中的第几天

  1. 来源
    力扣(LeetCode)-1154-一年中的第几天
  2. 问题描述
  • 给你一个按 YYYY-MM-DD 格式表示日期的字符串 date,请你计算并返回该日期是当年的第几天。
    通常情况下,我们认为 1 月 1 日是每年的第 1 天,1 月 2 日是每年的第 2 天,依此类推。每个月的天数与现行公元纪年法(格里高利历)一致

  • 示例
    示例 1:
    输入:date = “2019-01-09”
    输出:9
    示例 2:
    输入:date = “2019-02-10”
    输出:41

  1. 算法分析
  1. 获取关键
  • 输入:字符串,以“-”分隔,公元纪年法
  • 返回:第几天
  1. 处理逻辑
  • 这道题都没什么好说的,有些以下常识即可完成
    • 一年有12个月,有两类,一种是平年,二月有28天,一种是闰年,二月有29天
    • 平闰年的区别:闰年可以被4整除。但是,如果是100的倍数的话,必须被400整除,才是闰年。例如1900年可以被4整除,但由于是100的倍数,所以应该被除于400,不能被整除,所以是平年,二月有28天;2000年能被400整除,是闰年。二月有29天
    • 1、3、5、7、8、10、12这些月份有31天
    • 2、4、6、9、11这些月份有30天
  1. 细节处理
  • 100整倍年需要除于400,其它也没啥了
  1. 代码实现
class Date(object):
    def __init__(self,dateStr):
        """ 2010-10-09 """
        self.date = dateStr
    def run(self):
        year = int(self.date[:4])
        month = int(self.date[5:7])
        day = int(self.date[-2:])
        """ 1、3、5、7、8、10、12 """
        daysList = [31,28,31,30,31,30,31,31,30,31,30,31]
        if year % 100 == 0:
            if year % 400 == 0:
                """ 闰年 """
                daysList[1] = 29
        else:
            if year % 4 == 0:
                daysList[1] = 29
        result = 0
        for i in range(month-1):
            result += daysList[i]
        result += day
        return result
if __name__ == "__main__":
    dateObj = Date('2019-02-10')
    print(dateObj.run())
#41

运行结果:

  1. 思考

题目关键词:关于日期的常识
这道题没什么难度,但是如果不具备日期的常识,将无法解题。这是比较关键的。所以,应该多刷一些关于常识的题目,积累逻辑处理的流程之外,更重要的是知道这些常识。

  • 0716-分数加减运算

  1. 来源
    力扣(LeetCode)-592-分数加减运算
  2. 问题描述
  • 给定一个表示分数加减运算表达式的字符串,你需要返回一个字符串形式的计算结果。 这个结果应该是不可约分的分数,即最简分数。 如果最终结果是一个整数,例如 2,你需要将它转换成分数形式,其分母为 1。所以在上述例子中, 2 应该被转换为 2/1。

  • 示例 1:
    输入:"-1/2+1/2"
    输出: “0/1”

  • 示例 2:
    输入:"-1/2+1/2+1/3"
    输出: “1/3”
  1. 算法分析
  1. 获取关键
  • 输入:字符串,加减
  • 返回:最简,字符串,分数
  1. 处理逻辑
  • 从字符串中获取分数对象,并获取对应的运算符,+或者-
  • 对所有对象进行通分,即需要求得所有分数对象的分母的最小公倍数
    经过这一步处理后,所有的分数的分母将会统一,为最小公倍数
  • 对所有的分数对象的分子进行乘积处理,乘积的值是最小公倍数除于对应分母的结果
    求出所有分子的和
    求出分子和与最小公倍数的最大公因数,最大公因数可以通过以下方法获得。
    假 设 存 在 两 个 对 象 a , b a , b 的 最 小 公 倍 数 使 用 [ a , b ] 表 示 , 最 大 公 因 数 使 用 ( a , b ) 表 示 将 会 有 : a ∗ b = ( a , b ) ∗ [ a , b ] 假设存在两个对象a,b\\ a,b的最小公倍数使用[a,b]表示,最大公因数使用(a,b)表示\\ 将会有:a*b = (a,b)*[a,b] a,ba,b使[a,b],使(a,b)ab=(a,b)[a,b]
    将分子和最小公倍数同时除以最大公因数
    返回结果
  1. 细节处理
  • 如果最终的分子和为0时,可以直接返回“0/1”,,不需要求出分子和最小公倍数的最大公因数,否则正常处理
  1. 代码实现
class Solution:
    def beautyResultList(self,worklist):
        resultList = []
        for i in worklist:
            if i != '':
                eleList = i.split('/')
                resultList.append((int(eleList[1]),int(eleList[0])))
        return resultList
    def commonList(self,worklist,commonNum):
        for i in range(len(worklist)):
            value = commonNum/worklist[i][0]*worklist[i][1]
            worklist[i] = value
        return worklist
    def gcd(self,n1,n2):
        """greatest common divisor function """
        return self.gcd(n2, n1 % n2) if n2 > 0 else n1
    def lcm(self,n1,n2):
        """lowest common multiple function"""
        return n1 * n2 // self.gcd(n1, n2)
    def getMinCommonMultiple(self,worklist):
        for i in range(len(worklist)-1):
            n1,n2 = worklist.pop(0),worklist.pop(0)
            worklist.insert(0,self.lcm(n1,n2))
        return worklist[0]
    def fractionAddition(self, expression: str) -> str:
        positive,loseList = [],[]
        if expression[0] != '+' or expression[0] != '-':
            expression = '+'+expression
        for i in range(len(expression)):
            if (expression[i]=='-'):
                ele = ''
                for j in range(i+1,len(expression)):
                    if(expression[j]!='+') and (expression[j]!='-'):
                        ele += expression[j]
                    else:
                        break
                loseList.append(ele)
            elif (expression[i]=='+'):
                ele = ''
                for j in range(i+1,len(expression)):
                    if(expression[j]!='+') and (expression[j]!='-'):
                        ele += expression[j]
                    else:
                        break
                positive.append(ele)
        positive = self.beautyResultList(positive)
        loseList = self.beautyResultList(loseList)
        parentList = []
        for i in positive:
            parentList.append(i[0])
        for i in loseList:
            parentList.append(i[0])
        minCommonNum = self.getMinCommonMultiple(parentList)
        positive = self.commonList(positive,minCommonNum)
        loseList = self.commonList(loseList,minCommonNum)
        posSum = sum(positive)
        loseSum = sum(loseList)
        son = int(posSum-loseSum)
        if son == 0:
            return '0'+'/'+str(1)
        else:
            maxCommonNum = son*minCommonNum/self.getMinCommonMultiple([son,minCommonNum])
            return str(int(son/maxCommonNum))+"/"+str(int(minCommonNum/maxCommonNum))
if __name__ == "__main__":
    obj = Solution()
    print(obj.fractionAddition("-1/2+1/2"))
# 0/1
  1. 思考

题目关键词:
这道题主要侧重于考察数学知识中的最小公倍数最大公因数。另外在解析字符串的过程中,也是需要一些处理逻辑的,但是如果引用正则表达式的话,将会大大减少工作量。这等同于是否造轮子的区别。
另外有一些细节上的处理,也非常关键,在测试过程中遇到了好几个问题,都是因为细节处理上的疏忽。

  • 0717-移动石子直到连续

  1. 来源
    力扣(LeetCode)-1033-移动石子直到连续
  2. 问题描述
  • 三枚石子放置在数轴上,位置分别为 a,b,c。
    每一回合,我们假设这三枚石子当前分别位于位置 x, y, z 且 x < y < z。从位置 x 或者是位置 z 拿起一枚石子,并将该石子移动到某一整数位置 k 处,其中 x < k < z 且 k != y。
    当你无法进行任何移动时,即,这些石子的位置连续时,游戏结束。
    要使游戏结束,你可以执行的最小和最大移动次数分别是多少? 以长度为 2 的数组形式返回答案:answer = [minimum_moves, maximum_moves]

  • 示例
    输入:a = 1, b = 2, c = 5
    输出:[1, 2]
    解释:将石子从 5 移动到 4 再移动到 3,或者我们可以直接将石子移动到 3。

  1. 算法分析
  1. 获取关键
  • 输入:一维数轴,3个整数,移动到某个整数处
  • 返回:一个数组
  1. 处理逻辑
  • 首先有一个数轴,数轴上有3个数,移动3个数,使得连续。但每一回合只能移动两边的数。
    也有几个概念,如果a,b连续,那么就有a+1=b;同理b,c连续则有b+1=c
    因为不按顺序地输入,所以需要进行升序排序;根据大小重新定义为a,b,c
    可以分为以下几种情况:
    • 情况1:3个数是连续的
      返回[0,0]即不需要移动
    • 情况2:左边两个数是连续的
      那么移动最少次数为1,即将右边的数c移动1次,移动(飞)到b+1处;
      最多次数,产生于将c每次移动1个单位,移动到b+1处,则有c-b-1
    • 情况3:右边两数是连续的
      同上,最少次数为1,最多次数为b-a-1
      -情况4:左右两数都不连续 但左右两数相差大于2
      最少次数产生于 将a移动1次,移动(飞)到b-1处,将c移动1次,移动(飞)到b+1处,共2次;
      最多次数产生于 每次一个单位,将a移动到b-1处,将c移动到b+1处,则有
      ( c − b − 1 ) + ( b − 1 − a ) = c − a − 2 (c-b-1)+(b-1-a)=c-a-2 (cb1)+(b1a)=ca2
      -情况5:左右两数不连续,但左右两数有一个相差等于2,例如以下情况:
      在这里插入图片描述
      区别于情况4的最少移动次数,将离得远的数移动到左右相差2的中间即可。例如上面的将1移动到4的位置;也如将7移动到2的位置;
      最多次数还是和情况4一致。
  1. 细节处理
  • 其中情况5没有考虑到,所以算法过不去。看错误示例知道存在这个问题,所以补充了
  1. 代码实现
class Solution:
    def numMovesStones(self, a, b, c):
        a,b,c = sorted([a,b,c])
        #三连续
        if a+1 ==b and b+1 == c:
            return [0,0]
        #左连续
        if a+1 == b and b+1 < c:
            return [1,c-b-1]
        #右连续
        if a+1 <b and b+1 == c:
            return [1,b-a-1]
        if a+2 == b or b+2 == c:
            # return [1,b-a-1+c-b-1]
            return [1,c-a-2]
        #左右不连续
        if a+1 < b and b+1 <c:
            # return [2,b-a-1+c-b-1]
            return [2,c-a-2]
if __name__ == "__main__":
    obj = Solution()
    print('[1,2,5]:',obj.numMovesStones(1,2,5))
    print('[1,3,5]:',obj.numMovesStones(1,3,5))
    print('[1,3,7]:',obj.numMovesStones(1,3,7))

在这里插入图片描述
5. 思考

题目关键词:移动
这道算法的最小值和最大值都产生于移动,只不过产生的过程是不同的。
最少次数产生于跨度比较大的移动,更加确切的说是飞。而最多次数产生于一个单位的移动,每次只移动1个单位,不断缩小3个数的距离。
另外,每一种情况都有特殊性,区别在于最少次数的产生。因为情况不同,最少次数也不同。
最后,这道题也是比较简单,也因为这几天比较忙,所以刷几道简单的。

  • 0719-拼写单词

  1. 来源
    力扣LeetCode-1160
  2. 问题描述
  • 给你一份『词汇表』(字符串数组) words 和一张『字母表』(字符串) chars。
    假如你可以用 chars 中的『字母』(字符)拼写出 words 中的某个『单词』(字符串),那么我们就认为你掌握了这个单词。
    注意:每次拼写(指拼写词汇表中的一个单词)时,chars 中的每个字母都只能用一次。
    返回词汇表 words 中你掌握的所有单词的 长度之和。

  • 示例
    输入:words = [“cat”,“bt”,“hat”,“tree”], chars = “atach”
    输出:6
    解释:
    可以形成字符串 “cat” 和 “hat”,所以答案是 3 + 3 = 6。

  1. 算法分析
  1. 获取关键
  • 输入:一个列表,包含多个字符串,一个字符串
  • 返回:返回列表中满足条件的字符串的总和
  1. 处理逻辑
  • 定义字符串为字典,列表为待验证的字符串的集合
  • 分析字典中每一个字符的出现次数,使用dict(),格式{“x”:counts}
  • 遍历集合,取出每一个待验证的字符串,并分析字符串的每一个字符的出现次数,同上的处理方式
  • 如果待验证字符串字典中的键在字典中,且出现次数小于或等于字典,则表示该键满足条件,记录对应的数值
  • 当遍历完成一个待验证字符串字典时,如果数值等于该字符串的长度,则表示这个待验证字符串满足条件,记录对应的长度。
  • 遍历完成时,返回最终的长度即可。
  1. 细节处理
  • 对象的位置与区别需要分析好,不然很容易出现混淆。
  1. 代码实现
class Solution:
    def counter(self,chars):
        worddict = {}
        for i in chars:
            if(i in worddict):
                worddict[i] = worddict[i]+1
            else:
                worddict[i] = 1 
        return worddict
    def countCharacters(self, words, chars):
        charsdict = self.counter(chars)
        result = 0
        for i in words:
            i_dict = self.counter(i)
            flag = 0
            for j in i_dict:
                if j in charsdict and charsdict[j] >= i_dict[j]:
                    flag += i_dict[j]
            if flag == len(i):
                result += len(i)
        return result
if __name__ == "__main__":
    obj = Solution()
    words = ["cat","bt","hat","tree"]
    chars = "atach"
    # words = ["hello","world","leetcode"]
    # chars = "welldonehoneyr"
    words = ["dyiclysmffuhibgfvapygkorkqllqlvokosagyelotobicwcmebnpznjbirzrzsrtzjxhsfpiwyfhzyonmuabtlwin","ndqeyhhcquplmznwslewjzuyfgklssvkqxmqjpwhrshycmvrb","ulrrbpspyudncdlbkxkrqpivfftrggemkpyjl","boygirdlggnh","xmqohbyqwagkjzpyawsydmdaattthmuvjbzwpyopyafphx","nulvimegcsiwvhwuiyednoxpugfeimnnyeoczuzxgxbqjvegcxeqnjbwnbvowastqhojepisusvsidhqmszbrnynkyop","hiefuovybkpgzygprmndrkyspoiyapdwkxebgsmodhzpx","juldqdzeskpffaoqcyyxiqqowsalqumddcufhouhrskozhlmobiwzxnhdkidr","lnnvsdcrvzfmrvurucrzlfyigcycffpiuoo","oxgaskztzroxuntiwlfyufddl","tfspedteabxatkaypitjfkhkkigdwdkctqbczcugripkgcyfezpuklfqfcsccboarbfbjfrkxp","qnagrpfzlyrouolqquytwnwnsqnmuzphne","eeilfdaookieawrrbvtnqfzcricvhpiv","sisvsjzyrbdsjcwwygdnxcjhzhsxhpceqz","yhouqhjevqxtecomahbwoptzlkyvjexhzcbccusbjjdgcfzlkoqwiwue","hwxxighzvceaplsycajkhynkhzkwkouszwaiuzqcleyflqrxgjsvlegvupzqijbornbfwpefhxekgpuvgiyeudhncv","cpwcjwgbcquirnsazumgjjcltitmeyfaudbnbqhflvecjsupjmgwfbjo","teyygdmmyadppuopvqdodaczob","qaeowuwqsqffvibrtxnjnzvzuuonrkwpysyxvkijemmpdmtnqxwekbpfzs","qqxpxpmemkldghbmbyxpkwgkaykaerhmwwjonrhcsubchs"]
    chars = "usdruypficfbpfbivlrhutcgvyjenlxzeovdyjtgvvfdjzcmikjraspdfp"
    # words = ["hello","world","leetcode"]
    # chars = "welldonehoneyr"
    print(obj.countCharacters(words,chars))

运行截图:在这里插入图片描述
5. 思考

题目关键词:字典
有时需要认真的阅读题目,其实题目中的很多信息,已经告诉我们处理的方式。例如这道题,最开始时,我是准备将每一个字符替换,当完成后,如果字符都被替换完成后,如果替换次数等于待验证字符串的长度,那么就表示符合条件。当然这可以实现,但有许多细节并没有扣好,而且如果字符串的长度过大时,非常消耗内存。
最后,采用了字典记录字符串的字符与出现次数,最终完成,有许多细节是不需要处理的,而且也并无细节。使用合适的数据类型非常关键,这也验证了那句"算法+数据结构=程序"。

  • 0720-有效的回旋镖

  1. 来源
    力扣LeetCode-1037-有效的回旋镖
  2. 问题描述
  • 回旋镖定义为一组三个点,这些点各不相同且不在一条直线上。给出平面上三个点组成的列表,判断这些点是否可以构成回旋镖。

  • 示例
    输入:[[1,1],[2,3],[3,2]]
    输出:true

  1. 算法分析
  1. 获取关键
  • 输入:一个列表,列表中有3个子列表,子列表中有2个元素,分别代表下x,y坐标,则3个子列表表示3个点
  • 返回:判断3个点是否共线。如果共线,则返回Flase,否则True
  1. 处理逻辑
  • 偏向于数学问题。有许多方法可以解决,例如斜率、3点是否构成三角形、点是否在直线上。
  1. 斜率:3个点中随机选取2个点,构成1条直线,获取2个直线,并求得斜率。如果2条直线的斜率不同,那么表示3点不共线。
  2. 三点是否构成三角形:之前有一篇“点在三角形内”,可以用其中的一个面积公式,如果面积为0,则表示构成直线。(代码实现2)
  3. 点是否在直线上,取两个点,构成一条直线方程,判断点是否满足直线方程,如果满足,则表示三点共线
    4.向量法:三点两两组合构成两个向量,判断这两个向量是否共线即可。
    -这一道题使用两种方法,一种是向量法,另一种就是三点是否构成三角形。
  • . 细节处理
    如果需要使用除法的话,可以两两交叉乘,避免分母为0的情况发生。
  1. 代码实现

这里分别使用两个思路三角形面积公式和向量法实现

  • 三角形面积公式
class Solution:
    def isBoomerang(self, points: List[List[int]]) -> bool:
        a = (points[0][0],points[0][1])
        b = (points[1][0],points[1][1])
        c = (points[2][0],points[2][1])
        ##S=(1/2)*(x1y2+x2y3+x3y1-x1y3-x2y1-x3y2)
        s = a[0]*b[1]+b[0]*c[1]+c[0]*a[1]
        y = a[0]*c[1]+b[0]*a[1]+c[0]*b[1]
        if s==y:
            return False
        else:
            return True
  • 向量法
class Solution:
    def diffOfList(self,list1,list2):
        return (list1[0]-list2[0],list1[1]-list2[1])
    def isBoomerang(self, points) -> bool:
        if points[1]!=points[0] and points[1]!=points[2] and points[0]!=points[2]:
            vector_a = self.diffOfList(points[0],points[1])
            vector_b = self.diffOfList(points[0],points[2])
            if vector_a[0] and vector_a[1]:
                if vector_b[0]/vector_a[0] != vector_b[1]/vector_a[1]:
                    return True
                else:
                    return False
            elif not vector_a[0] and vector_a[1]:
                if not vector_b[0]:
                    return False
                else:
                    return True
            elif not vector_a[1] and vector_a[0]:
                if not vector_b[1]:
                    return False
                else:
                    return True
            else:
                return False
        else:
            return False
if __name__ == "__main__":
    obj = Solution()
    points = [[1,1],[2,3],[3,2]]
    points = [[1,1],[2,2],[3,3]]
    points = [[0,0],[1,2],[0,1]]
    points = [[1,1],[2,3],[3,2]]
    print(obj.isBoomerang(points))
  1. 思考

题目关键词:点,直线
这道题更偏向于数学知识上,其实除了上面的那些解法之外,还有许多种方式。
除此之外,也没啥好说的了。

  • 0722-最后一块石头的重量

  1. 来源
    力扣(LeetCode)-1046-最后一块石头的重量
  2. 问题描述
  • 有一堆石头,每块石头的重量都是正整数。
  • 每一回合,从中选出两块 最重的 石头,然后将它们一起粉碎。假设石头的重量分别为 x 和 y,且 x <= y。那么粉碎的可能结果如下:
  1. 如果 x == y,那么两块石头都会被完全粉碎;
  2. 如果 x != y,那么重量为 x 的石头将会完全粉碎,而重量为 y 的石头新重量为 y-x。
  • 最后,最多只会剩下一块石头。返回此石头的重量。如果没有石头剩下,就返回 0。

  • 示例
    输入:[2,7,4,1,8,1]
    输出:1
    解释:
    先选出 7 和 8,得到 1,所以数组转换为 [2,4,1,1,1],
    再选出 2 和 4,得到 2,所以数组转换为 [2,1,1,1],
    接着是 2 和 1,得到 1,所以数组转换为 [1,1,1],
    最后选出 1 和 1,得到 0,最终数组转换为 [1],这就是最后剩下那块石头的重量。

  1. 算法分析
  1. 获取关键
  • 输入:一个列表,列表内元素都是整数
  • 返回:一个整数
  1. 处理逻辑
  • 每一回合选取列表中的最重两块石头,进行碰撞,碰撞后并返回剩余重量到列表。
    碰撞:大减小
    最简单的方法:
  • 对列表进行排序,弹出两个最大的元素,求差。当差不为0时,插入到列表中
  • 当列表长度大于1时重复以上操作
  • 最后将会返回一个空列表或者长度为1的列表
    当列表为空时,返回0即可
    当列表长度为1时,返回列表第一个元素即可
  1. 细节处理
  • 需要考虑到列表为空的条件存在:即列表中最后一回合中,两块石头的重量相等。
  1. 代码实现
class Solution:
    def lastStoneWeight(self, stones: List[int]) -> int:
        while len(stones) > 1:
            stones.sort(reverse=True)
            max = stones.pop(0)
            min = stones.pop(0)
            if max != min:
                stones.append(max-min)
        if stones:
            return stones[0]
        return 0
  1. 思考

题目关键词:贪心,排序
其实这道题挺简单的,但并没有实现得多么优美,也没有参考其它的办法。知识简单的刷题而已。
另外也实现了一种想法:就是只执行一次排序,然后每次将碰撞后的结果插入列表的某个位置,保证列表的元素数值顺序不变。
这里使用了二分插入排序,但是效率非常不稳定,可以由于插入算法并没有写得完美。代码如下:

class Solution:
    def insert(self,ele,ls):
        if not ls:
            return [ele]
        elif len(ls) == 1:
            if ls[0]>ele:
                ls.insert(0,ele)
            else:
                ls.append(ele)
            return ls
        else:
            low = 0
            hight = len(ls)
            while hight > low:
                mid = int((hight-low)/2)+low
                if ls[mid] > ele:
                    if ele > ls[mid-1]:
                        ls.insert(mid,ele)
                        return ls
                    hight = mid-1
                elif ele > ls[mid]:
                    if len(ls)-2>mid and ls[mid+1] > ele:
                        ls.insert(mid+1,ele)
                        return ls
                    low = mid+1
                else:
                    ls.insert(mid,ele)
                    return ls
            ls.insert(low,ele)
            return ls
    def lastStoneWeight(self, stones: List[int]) -> int:
        stones.sort()
        while len(stones) > 1:
            max = stones.pop(-1)
            min = stones.pop(-1)
            if max != min:
                stones = self.insert(max-min,stones)
        if stones:
            return stones[0]
        else:
            return 0
  • 0723-有效三角形的个数

  1. 来源
    力扣(LeetCode)-611-有效三角形的个数
  2. 问题描述
  • 给定一个包含非负整数的数组,你的任务是统计其中可以组成三角形三条边的三元组个数。

  • 示例
    输入: [2,2,3,4]
    输出: 3
    解释:
    有效的组合是:
    2,3,4 (使用第一个 2)
    2,3,4 (使用第二个 2)
    2,2,3

  1. 算法分析
  1. 获取关键
  • 输入:数组,非负整数,取出3个整数组合,判断是否符合三角形
  • 返回:满足条件的三角形个数
  1. 处理逻辑
  • 关键:三条边中,最小的两条边大于第三边,则三条边即可构成三角形
    首先对列表进行排序,难解释,直接看图吧
    在这里插入图片描述>3. 细节处理
  • 根据关键,需要知道边的有效范围和符合条件的边的个数
  1. 代码实现
class Solution:
    def triangleNumber(self, nums: List[int]) -> int:
        nums.sort()
        result,len_nums = 0,len(nums)
        for i in range(len_nums-2):
            if nums[i] == 0 : continue
            current = i+2
            for x in range(i+1,len_nums-1):
                while len_nums > current and nums[i]+nums[x] > nums[current]:
                    current += 1
                result += current -x-1
        return result
  1. 思考

题目关键词:排序,三角形
其实一开始是准备3个for循环,获取满足条件的边的集合,最后返回集合的长度即可
但是最后发现,效率太低了,最后使用了动态规划。效率虽然不是很高,但也跑过去了。

  • 0724-(简单)高度检查器

  1. 来源
    力扣(LeetCode)-1151-高度检查器
  2. 问题描述
  • 学校在拍年度纪念照时,一般要求学生按照 非递减 的高度顺序排列。
    请你返回能让所有学生以 非递减 高度排列的最小必要移动人数。
    注意,当一组学生被选中时,他们之间可以以任何可能的方式重新排序,而未被选中的学生应该保持不动。

  • 示例
    输入:heights = [1,1,4,2,1,3]
    输出:3
    解释:
    当前数组:[1,1,4,2,1,3]
    目标数组:[1,1,1,2,3,4]
    在下标 2 处(从 0 开始计数)出现 4 vs 1 ,所以我们必须移动这名学生。
    在下标 4 处(从 0 开始计数)出现 1 vs 3 ,所以我们必须移动这名学生。
    在下标 5 处(从 0 开始计数)出现 3 vs 4 ,所以我们必须移动这名学生。

  1. 算法分析
  1. 获取关键
  • 输入:一个列表
  • 返回:一个整数
  1. 处理逻辑
  • 这道题官方的问题描述和示例都是有问题的。初看时,一直get不到出题人的意图。甚至觉得是否要写一个排序算法,通过最少的移动次数,完成排序后并返回移动次数。但通过示例的返回值,知道了这道题的意图——对所给集合进行升序排序。并将完成排序之后的集合与原集合进行比较,看有同一位置上有多少个元素不同。
  • 例如原集合[1,1,4,2,1,3]与完成排序的集合[1,1,1,2,3,4],
    有位置2(从0开始),4,5的3个位置上的元素不同,所以返回3.
  • 所以解决的办法就是进行一一比较
  1. 细节处理
  1. 代码实现
class Solution:
    def heightChecker(self, heights):
        copy_heights = heights.copy()
        copy_heights.sort()
        count = 0
        for i in range(len(heights)):
            if heights[i]!= copy_heights[i]:
                count += 1
        return count
  1. 思考

题目关键词:简单,排序

  • 0724-(中等)求解方程

  1. 来源
    力扣(LeetCode-640-求解方程)
  2. 问题描述
  • 求解一个给定的方程,将x以字符串"x=#value"的形式返回。该方程仅包含’+’,’ - '操作,变量 x 和其对应系数。
    如果方程没有解,请返回“No solution”。
    如果方程有无限解,则返回“Infinite solutions”。
    如果方程中只有一个解,要保证返回值 x 是一个整数。

  • 输入: “x+5-3+x=6+x-2”
    输出: “x=2”

  1. 算法分析
  1. 获取关键
  • 输入:一个字符串,一个方程
  • 返回:返回解,如果特殊情况,则返回对应的值
  1. 处理逻辑
  • 前言: 逻辑并不复杂,只是处理过程有点复杂。
  • 解析字符串,获取等号两边的元素
  • 根据正负收集元素(最关键的一步):假如以等号左边的正数为正的元素,那么等号右边的负数则为正。其它相反即可
    -分别处理正负元素,返回解即可
  1. 细节处理
  • 其中正负收集元素的这一步中,非常关键,可以使用两个集合分别收集正负元素。最后统计集合的元素,,返回解。
  1. 代码实现
class Solution:
    def sum(self,workList):
        nums,count = 0,0
        for i in workList:
            if i:
                if 'x' not in i:
                    nums += int(i)
                else:
                    if len(i) == 1:
                        count += 1
                    else:
                        count += int(i[:len(i)-1])
        return nums,count
    def getELement(self,workStr,pList,mList):
        # '+x+5+x' len = 8 index = 7
        current = 0
        while len(workStr)-1>current:
            if workStr[current]=='+':
                plushStr = ''
                k = 0
                for i in range(current+1,len(workStr)):
                    k = i
                    if i == len(workStr)-1:
                        plushStr += workStr[i]
                        break
                    if workStr[i] != '+' and workStr[i] != '-':
                        plushStr += workStr[i]
                    else:
                        break
                pList.append(plushStr)
                current = k
            else:
                minusStr = ''
                k = 0
                for i in range(current+1,len(workStr)):
                    k = i
                    if i == len(workStr)-1:
                        minusStr += workStr[i]
                        break
                    if workStr[i] != '+' and workStr[i] != '-':
                        minusStr += workStr[i]
                    else:
                        break
                mList.append(minusStr)
                current = k
        return pList,mList
    def solveEquation(self, equation: str) -> str:
        workList = equation.split('=')
        if workList[0] != '+' and workList[0] != '-': 
            front = '+'+workList[0]
        else:
            front = workList[0]
        if workList[1] != '+' and workList[1] != '-': 
            back = '+'+workList[1]
        else:
            back = workList[1]
        # plus or minus
        plusList,minusList = [],[]
        plusList,minusList = self.getELement(front,plusList,minusList)
        plusList,minusList = self.getELement(back,minusList,plusList)
        plus = self.sum(plusList)
        minus = self.sum(minusList)
        if plus[1] == minus[1]:
            if plus[0] != minus[0]:
                return 'No solution'
            else:
                return 'Infinite solutions'
        else:
            if plus[1] > minus[1]:
                return 'x={}'.format(int((minus[0]-plus[0])/(plus[1]-minus[1])))
            else:
                return 'x={}'.format(int((plus[0]-minus[0])/(minus[1]-plus[1])))
  1. 思考

题目关键词:解析
在现实中简单的一个加减法求解方程,使用代码实现,却如此复杂。说明自己的代码水平还是需要有所提高的。

  • 0725-(简单)分糖果Ⅱ

  1. 来源
    力扣(LeetCode)-1103-分糖果Ⅱ
  2. 问题描述
  • 排排坐,分糖果。
    我们买了一些糖果 candies,打算把它们分给排好队的 n = num_people 个小朋友。
    给第一个小朋友 1 颗糖果,第二个小朋友 2 颗,依此类推,直到给最后一个小朋友 n 颗糖果。
    然后,我们再回到队伍的起点,给第一个小朋友 n + 1 颗糖果,第二个小朋友 n + 2 颗,依此类推,直到给最后一个小朋友 2 * n 颗糖果。
    重复上述过程(每次都比上一次多给出一颗糖果,当到达队伍终点后再次从队伍起点开始),直到我们分完所有的糖果。注意,就算我们手中的剩下糖果数不够(不比前一次发出的糖果多),这些糖果也会全部发给当前的小朋友。
    返回一个长度为 num_people、元素之和为 candies 的数组,以表示糖果的最终分发情况(即 ans[i] 表示第 i 个小朋友分到的糖果数)。

  • 输入:candies = 7, num_people = 4
    输出:[1,2,3,1]
    解释:
    第一次,ans[0] += 1,数组变为 [1,0,0,0]。
    第二次,ans[1] += 2,数组变为 [1,2,0,0]。
    第三次,ans[2] += 3,数组变为 [1,2,3,0]。
    第四次,ans[3] += 1(因为此时只剩下 1 颗糖果),最终数组变为 [1,2,3,1]。

  1. 算法分析
  1. 获取关键
  • 输入:糖果的个数,人的个数
  • 返回:以集合的方式返回每个人收到糖果的个数
  1. 处理逻辑
  • 按照题目的逻辑走一遍即可
  1. 细节处理
  • 定位索引这个地方可以使用求余符号%实现。
    例如:设定有n个小朋友
    第0次分糖果,应该定位到第0位小朋友,即0%n=0
    第n+1次分糖果,应该定位到第1位小朋友,即(n+1)%n=1
  1. 代码实现
class Solution:
    def distributeCandies(self, candies: int, num_people: int) -> List[int]:
        resultList = [0 for _ in range(num_people)]
        index,count = 0,1
        while candies:
            if candies >= count:
                resultList[index%num_people] += count
                candies = candies - count
                index += 1
                count += 1
            else:
                resultList[index%num_people] += candies
                candies = 0
        return resultList
  1. 思考

题目关键词:顺序 分
从看到问题到写完,没有花多少时间,而且直接一遍过。
可能因为这道题非常简单吧。

  • 0725-(中等)用最少数量的箭引爆气球

  1. 来源
    力扣(LeetCode)-452-用最少数量的箭引爆气球
  2. 问题描述
  • 在二维空间中有许多球形的气球。对于每个气球,提供的输入是水平方向上,气球直径的开始和结束坐标。由于它是水平的,所以y坐标并不重要,因此只要知道开始和结束的x坐标就足够了。开始坐标总是小于结束坐标。平面内最多存在104个气球。
    一支弓箭可以沿着x轴从不同点完全垂直地射出。在坐标x处射出一支箭,若有一个气球的直径的开始和结束坐标为 xstart,xend, 且满足 xstart ≤ x ≤ xend,则该气球会被引爆。可以射出的弓箭的数量没有限制。 弓箭一旦被射出之后,可以无限地前进。我们想找到使得所有气球全部被引爆,所需的弓箭的最小数量。

  • 输入:[[10,16], [2,8], [1,6], [7,12]]
    输出:2
    解释:
    对于该样例,我们可以在x = 6(射爆[2,8],[1,6]两个气球)和 x = 11(射爆另外两个气球)。

  1. 算法分析
  1. 获取关键
  • 输入:一个列表
  • 返回:需要最少的弓箭数量
  1. 处理逻辑
  • 这道题是属于贪心算法,所以有两个常规操作:排序和贪心选择
    问题描述中有一句非常关键:由于它是水平的,所以y坐标并不重要,因此只要知道开始和结束的x坐标就足够了。
    上面的这句关键说明只有x轴方向,即一维的。另外其实更确切地说,排序的关键在于结束坐标。如下:
    在这里插入图片描述那么引爆以上气球需要2个弓箭。一个弓箭在x=3,引爆气球1,2和2;另一个弓箭引爆气球4即可。
  1. 细节处理
  • 其实对上面的样例,不止有以上的办法。还有:
  1. 代码实现
class Solution:
    def findMinArrowShots(self, points:list) -> int:
        points.sort(key=lambda x:(x[1]))
        flag = 0
        while points:
            workList = []
            workList.append(points.pop(0))
            while points:
                if workList[0][1] >= points[0][0]:
                    workList.append(points.pop(0))
                else:
                    break
            flag += 1
        return flag
  1. 思考

题目关键词:排序,贪心
以前解决问题,都是按照c或者c++的思维,手写排序,手写逻辑。固然非常锻炼编程能力,但也是重复造轮子。所以,有些时候,使用一些轮子非常重要,当然这些轮子的使用前,必须知道这个轮子是怎么造的。例如代码中的

points.sort(key=lambda x:(x[1]))

即代表根据对象的位置1进行排序。
另外,贪心算法并不是一种“完美“的解决方案:

  1. 当存在多个最优方案时,无法列举所有的方案
  2. 最终的方案是基于每一次的最优选择,而许多问题并不全是走好每一步就代表可以得到最优方案。例如动态规划问题等等。
  • 0727-(中等)搜索推荐系统

  1. 来源
    力扣(LeetCode)-1268-搜索推荐系统
  2. 问题描述
  • 给你一个产品数组 products 和一个字符串 searchWord ,products 数组中每个产品都是一个字符串。
    请你设计一个推荐系统,在依次输入单词 searchWord 的每一个字母后,推荐 products 数组中前缀与 searchWord 相同的最多三个产品。如果前缀相同的可推荐产品超过三个,请按字典序返回最小的三个。
    请你以二维列表的形式,返回在输入 searchWord 每个字母后相应的推荐产品的列表。

  • 输入:products = [“mobile”,“mouse”,“moneypot”,“monitor”,“mousepad”], searchWord = “mouse”
    输出:[
    [“mobile”,“moneypot”,“monitor”],
    [“mobile”,“moneypot”,“monitor”],
    [“mouse”,“mousepad”],
    [“mouse”,“mousepad”],
    [“mouse”,“mousepad”]
    ]
    解释:按字典序排序后的产品列表是 [“mobile”,“moneypot”,“monitor”,“mouse”,“mousepad”]
    输入 m 和 mo,由于所有产品的前缀都相同,所以系统返回字典序最小的三个产品 [“mobile”,“moneypot”,“monitor”]
    输入 mou, mous 和 mouse 后系统都返回 [“mouse”,“mousepad”]

  1. 算法分析
  1. 获取关键
  • 输入:xxx
  • 返回:xxx
  1. 处理逻辑
  • 通过问题描述,觉得很高大上,其实通过暴力法很简单
  • 对产品组进行排序,由于产品组都是字符串,所以排序是根据字典排序。
  • 通过两个for即可不断遍历产品组,获得符合条件产品即可。
  1. 细节处理
  1. 代码实现
class Solution:
    def suggestedProducts(self, products, searchWord):
        products.sort()
        result = [[] for _ in range(len(searchWord))]
        workStr = ''
        lenStr = 0
        for i in searchWord:
            workStr += i
            lenStr += 1
            for j in products:
                if j[:lenStr] == workStr and len(result[lenStr-1]) < 3:
                    result[lenStr-1].append(j)
        return result
  1. 思考

题目关键词:暴力 遍历
现在刷题都是暴力法解决的,往往遇到最多的不是出现错误,而是超时。有时总在技巧和暴力之间犹豫不决。两个方向各有好处,技巧锻炼思维,暴力锻炼逻辑。虽然更偏向于技巧,但有时因为时间和思维有限,所以走向暴力法。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值