2020-07算法刷题集

  • 前言
  • 0715-一年中的第几天
  • 0716-分数加减运算
  • 0717-移动石子直到连续
  • 0719-拼写单词
  • 0720-有效的回旋镖
  • 0722-最后一块石头的重量
  • 0723-有效三角形的个数
  • 0724-(简单)高度检查器
  • 0724-(中等)求解方程
  • 0725-(简单)分糖果Ⅱ
  • 0725-(中等)用最少数量的箭引爆气球
  • 0727-(中等)搜索推荐系统

前言

前段时间一直忙于上课与其它事情,一直都没有抽出时间来刷算法题,现在看来,浪费了许多时间,非常后悔。
错过了昨天,今天将是一个非常好的起点。所以重新开始刷算法题。
(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 = dateStrdef 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] = 29else:if year % 4 == 0:daysList[1] = 29result = 0for i in range(month-1):result += daysList[i]result += dayreturn 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)表示将会有:a∗b=(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 resultListdef commonList(self,worklist,commonNum):for i in range(len(worklist)):value = commonNum/worklist[i][0]*worklist[i][1]worklist[i] = valuereturn worklistdef gcd(self,n1,n2):"""greatest common divisor function """return self.gcd(n2, n1 % n2) if n2 > 0 else n1def 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 = '+'+expressionfor 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:breakloseList.append(ele)elif (expression[i]=='+'):ele = ''for j in range(i+1,len(expression)):if(expression[j]!='+') and (expression[j]!='-'):ele += expression[j]else:breakpositive.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 (c−b−1)+(b−1−a)=c−a−2
      -情况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]+1else:worddict[i] = 1 return worddictdef countCharacters(self, words, chars):charsdict = self.counter(chars)result = 0for i in words:i_dict = self.counter(i)flag = 0for 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 Falseelse: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 Trueelse:return Falseelif not vector_a[0] and vector_a[1]:if not vector_b[0]:return Falseelse:return Trueelif not vector_a[1] and vector_a[0]:if not vector_b[1]:return Falseelse:return Trueelse:return Falseelse: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 lselse:low = 0hight = len(ls)while hight > low:mid = int((hight-low)/2)+lowif ls[mid] > ele:if ele > ls[mid-1]:ls.insert(mid,ele)return lshight = mid-1elif ele > ls[mid]:if len(ls)-2>mid and ls[mid+1] > ele:ls.insert(mid+1,ele)return lslow = mid+1else:ls.insert(mid,ele)return lsls.insert(low,ele)return lsdef 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 : continuecurrent = i+2for x in range(i+1,len_nums-1):while len_nums > current and nums[i]+nums[x] > nums[current]:current += 1result += current -x-1return 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 = 0for i in range(len(heights)):if heights[i]!= copy_heights[i]:count += 1return 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,0for i in workList:if i:if 'x' not in i:nums += int(i)else:if len(i) == 1:count += 1else:count += int(i[:len(i)-1])return nums,countdef getELement(self,workStr,pList,mList):# '+x+5+x' len = 8 index = 7current = 0while len(workStr)-1>current:if workStr[current]=='+':plushStr = ''k = 0for i in range(current+1,len(workStr)):k = iif i == len(workStr)-1:plushStr += workStr[i]breakif workStr[i] != '+' and workStr[i] != '-':plushStr += workStr[i]else:breakpList.append(plushStr)current = kelse:minusStr = ''k = 0for i in range(current+1,len(workStr)):k = iif i == len(workStr)-1:minusStr += workStr[i]breakif workStr[i] != '+' and workStr[i] != '-':minusStr += workStr[i]else:breakmList.append(minusStr)current = kreturn pList,mListdef 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 minusplusList,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,1while candies:if candies >= count:resultList[index%num_people] += countcandies = candies - countindex += 1count += 1else:resultList[index%num_people] += candiescandies = 0return 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. 细节处理
  • 其实对上面的样例,不止有以上的办法。还有:

    • 弓箭在x=2出,另一个弓箭在x=4处
    • 等等
      但最终的答案是不会变的。正如贪心算法的一个案例-活动安排。
      贪心算法解决活动安排-Python实现(排序+贪心选择)
      活动结束时间对应气球的结束坐标,排序还是那个排序;
  1. 代码实现
class Solution:def findMinArrowShots(self, points:list) -> int:points.sort(key=lambda x:(x[1]))flag = 0while points:workList = []workList.append(points.pop(0))while points:if workList[0][1] >= points[0][0]:workList.append(points.pop(0))else:breakflag += 1return 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 = 0for i in searchWord:workStr += ilenStr += 1for j in products:if j[:lenStr] == workStr and len(result[lenStr-1]) < 3:result[lenStr-1].append(j)return result
  1. 思考

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

2020-07算法刷题集相关推荐

  1. 神了,无意中发现一位1500道的2021LeetCode算法刷题pdf笔记

    昨晚逛GitHub,无意中看到一位大佬的算法刷题笔记,感觉发现了宝藏!有些小伙伴可能已经发现了,但咱这里还是忍不住安利一波,怕有些小伙伴没有看到. 关于算法刷题的困惑和疑问也经常听朋友们提及.这份笔记 ...

  2. 【c++算法刷题笔记】——洛谷2

    1. 洛谷练习--P1579 哥德巴赫猜想(升级版) 题目描述: 现在请你编一个程序验证哥德巴赫猜想. 先给出一个奇数n,要求输出3个质数,这3个质数之和等于输入的奇数. 输入格式: 仅有一行,包含一 ...

  3. 找到所有数组中消失的数字_【一点资讯】千万程序员的呼声:面试如何拿到大厂Offer?这份阅读量超过11W+的算法刷题宝典请你原地查收 www.yidianzixun.com...

    如何才能通过面试拿到大厂Offer? "刷leetcode!" 这是我听到最多的回答! 现在越来越多的人应聘工作时都得先刷个几十百来道题,不刷题感觉都过不了面试. 无论是面测试.算 ...

  4. 算法刷题路线总结与相关资料分享

    算法刷题路线总结与相关资料分享 前言 一.算法刷题路线总结 二.算法题刷题步骤 三.基础数据结构知识汇总 1.时间复杂度 2.空间复杂度 3.线性表 4.栈与队列 5.树 四.基础算法知识汇总 1.递 ...

  5. 深度优先搜索dfs算法刷题笔记【蓝桥杯】

    其实网上已经有不少dfs的算法笔记,但我之所以还再写一篇,主要是因为我目前见到的笔记,都有些太偏向理论了. 对于基础薄弱的或是没有基础的人(like me),有点不合适,因为看了,也不能说自己会了. ...

  6. 字节跳动算法刷题宝典.pdf

    今天推荐一个关于「算法刷题宝典」的开源项目:力扣Cookbook. 力扣 Cookbook是@halfrost(中文名:霜神)去年刷的 力扣整理出的 520 题,每道题都写了解题思路,并且每题都 ru ...

  7. Github最强算法刷题笔记.pdf

    资料一 昨晚逛GitHub,无意中看到一位大佬(https://github.com/halfrost)的算法刷题笔记,感觉发现了宝藏!有些小伙伴可能已经发现了,但咱这里还是忍不住安利一波,怕有些小伙 ...

  8. 算法刷题宝典.pdf

    今天推荐一个关于「算法刷题宝典」的开源项目:力扣Cookbook. 力扣 Cookbook是@halfrost(中文名:霜神)去年刷的 力扣整理出的 520 题,每道题都写了解题思路,并且每题都 ru ...

  9. Leetode算法刷题宝典.pdf

    今天推荐一个关于「算法刷题宝典」的开源项目:力扣Cookbook. 力扣 Cookbook是@halfrost(中文名:霜神)去年刷的 力扣整理出的 520 题,每道题都写了解题思路,并且每题都 ru ...

最新文章

  1. 【Java挠头】Java异常、捕获、处理、throw、throws等绝妙剖析
  2. SAP系统如何快速上手?
  3. Word保存自己格式模板的方法
  4. 五一重装WinXP操作系统所遇问题的解决
  5. bootstraptable treeGrid 懒加载_Java类加载机制及自定义加载器
  6. 《iOS 6核心开发手册(第4版)》——2.11节秘诀:构建星星滑块
  7. java中Date日期类型的大小比较
  8. 【LeetCode】【HOT】301. 删除无效的括号(递归)
  9. python文字转图片_python-将文本文件转换为图形
  10. php教程 网易,网易开源镜像使用帮助
  11. 基于STM32F103——SIM900A发送短信+串口打印
  12. php三极管导通条件,关于NPN三极管的导通条件分析
  13. 图片格式转换 png,jpg,gif等格式 的python小程序
  14. 码农十分钟的音律概述 纯律 五度相生律 十二平均律
  15. 简单的微信使用技巧,你需要掌握的技巧
  16. 【小程序源码】宝宝起名神器
  17. FJUT 1735 开宝箱
  18. 一位大学老师感染新冠期间写的年终总结:怀疑得了新冠39次,吃黄桃罐头5次…...
  19. 第九周 棚拍静物——让你的照片更有味道
  20. 微信小程序滚动Tab选项卡:左右滑动切换、触底加载分页

热门文章

  1. 网站服务器访问变慢是什么原因,网站访问速度太慢的一般解决方法
  2. 09_方法重写丶多态丶抽象类
  3. vSphere ESXI 7.0镜像 U盘安装盘制作(macOS)
  4. html如何打包文件发送,手把手教你webpack如何打包才能输出最优生产文件
  5. 【面试】前端常见面试题总结
  6. 使用大于 16MB 的闪存时, Zynq 和 QSPI 的复位要求
  7. SEO关键词怎么选,用这个方法轻松挑选关键词
  8. 肠道微生物群与五种癌症的相互作用:致癌 -> 治疗 -> 预后
  9. Vue中父子组件如何传值
  10. Java创建线程的七种方法,全网最全面总结~