目录
01 删除排序数组中的重复项-20200314
题目
给定一个排序数组,你需要在 原地 删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。
不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。
示例
给定数组 nums = [1,1,2],
函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 1,2。 你不需要考虑数组中超出新长度后面的元素。
说明
为什么返回数值是整数,但输出的答案是数组呢?
请注意,输入数组是以「引用」方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。
注意事项
- 函数输入为列表(List),输出为列表长度(int),但是要注意,输出答案为数组,所以这里是直接对传入参数进行直接修改。这里 复习学习下 python 3 函数 知识点。
思路一
将数组第一个项设置为临时变量temp,然后与后面的每一项比对。相同就remove掉该项,不同就pass,在循环的最后令temp等于当前项。
修改经历:
1. 输出为 [ ],没有考虑在内。(第一次提交)
- 执行用时 :872 ms, 在所有 Python3 提交中击败了6.85%的用户
- 内存消耗 :14.5 MB, 在所有 Python3 提交中击败了13.77%的用户
2. 这里用到了两个临时变量 lenght 和 temp,所以内存消耗太大,可以省去一个。(第二次提交)
- 执行用时 :720 ms, 在所有 Python3 提交中击败了8.40%的用户
- 内存消耗 :14.6 MB, 在所有 Python3 提交中击败了8.22%的用户
3. 省去一个临时变量,效果依旧不好,可能是remove()函数占用了太多的资源。换成pop()操作试试看。使用pop()就需要数组倒着数了。
- 执行用时 :80 ms, 在所有 Python3 提交中击败了79.71%的用户
- 内存消耗 :14.4 MB, 在所有 Python3 提交中击败了60.72%的用户
心得体会:
- list的remove()比pop()操作耗时太多了!
最终代码展示:
class Solution:
def removeDuplicates(self, nums: List[int]) -> int:
if len(nums) != 0 and len(nums) != 1:
temp = nums[-1]
for i in range(len(nums)-2, -1, -1):
if temp == nums[i]:
nums.pop(i+1)
else:
pass
temp = nums[i]
else:
pass
return len(nums)
02 买股票的最佳时机 II-20200315
题目
给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
示例
输入: [7,1,5,3,6,4]
输出: 7
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6-3 = 3 。
注意事项
- 一次只能持有一只股票,只能操作一只股票。
- 这个相当于给定一个乱序的数组,然后在不改变数组顺序的,降序找到差值最大的组合,将差值相加。若降序被打断,则从新降序计算。
- 价格的数组应该要大于一天,不然买卖没有意思的。
思路一
说白了就是低买高卖,所以,只要买的时候价格比第二天价格低,卖的时候价格比第二天高就可以了。
这道题,可以设置两个指针,former和latter。former指向明天,latter指向今天。
修改经历:
1. 当输入价格数组为[1, 2]时,输出为-1。经检查显示,former的指针放在elif中,并没有被检查到,所以单独拿出来写。(第一次提交)
- 执行用时 :68 ms, 在所有 Python3 提交中击败了57.95%的用户
- 内存消耗 :14.4 MB, 在所有 Python3 提交中击败了9.84%的用户
2. 由于设置了太多的临时变量,比如,指针former和latter,状态位status,总利润 sum_profit等,导致内存消耗太多了,试试看能不能减少点,先把指针省去。(第二次提交)
- 执行用时 :44 ms, 在所有 Python3 提交中击败了89.50%的用户
- 内存消耗 :14.4 MB, 在所有 Python3 提交中击败了9.75%的用户
3. 这个感觉提升不大啊!
- 执行用时 :44 ms, 在所有 Python3 提交中击败了89.50%的用户
- 内存消耗 :14.3 MB, 在所有 Python3 提交中击败了9.84%的用户
心得体会:
我好想考虑的太多了,什么状态啊,看题解上的贪心算法并没有考虑这个。不过在动态规划中,有相似的地方,比方说现金是否在手上。我还考虑了股市只要两天的情况。
最终代码展示:
class Solution:
def maxProfit(self, prices: List[int]) -> int:
sum_profits = 0 # 累计盈利
if len(prices) > 1: # 判断价格天数时候大于1天
status = 0 # 是否买入的状态位,0为未买入,1为已买入
for i in range(0, len(prices)-1):
if status == 0 and prices[i+1] >= prices[i]:
sum_profits -= prices[i]
status = 1
elif status == 1 and prices[i+1] < prices[i]:
sum_profits += prices[i]
status = 0
else:
pass
if status == 1 and i+1 == len(prices) - 1:
sum_profits += prices[i+1]
else:
pass
else:
pass
return sum_profits
思路二
相比于上面的思路一,这种思路参考题解的方法,其实就是贪心算法,每次只考虑连续两天的差价,差价大于零就加上,小于零就pass。这种方法,没有考虑买卖的关系,单纯地从数学的角度考虑。我想,如果有手续费,可能就不是最优的啦。
修改经历:
1. 贪心算法其实和上面差不多,这个内存怎么能降下来呢。
- 执行用时 :40 ms, 在所有 Python3 提交中击败了93.86%的用户
- 内存消耗 :14.3 MB, 在所有 Python3 提交中击败了9.84%的用户
心得体会:
- 根据题目,不要想得太复杂。
最终代码展示:
class Solution:
def maxProfit(self, prices: List[int]) -> int:
sum_profits = 0
for i in range(len(prices)-1):
if prices[i+1] - prices[i] > 0:
sum_profits += prices[i+1] - prices[i]
else:
pass
return sum_profits
03 旋转数组-20200316
题目
给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。
示例
输入: [1,2,3,4,5,6,7] 和 k = 3
输出: [5,6,7,1,2,3,4]
解释:
向右旋转 1 步: [7,1,2,3,4,5,6]
向右旋转 2 步: [6,7,1,2,3,4,5]
向右旋转 3 步: [5,6,7,1,2,3,4]
说明
- 尽可能想出更多的解决方案,至少有三种不同的方法可以解决这个问题。
- 要求使用空间复杂度为 O(1) 的 原地 算法。
注意事项
- 循环移动,最笨的办法就是逐一移动,因为要求空间复杂度为O(1),所以不能另外开辟新数组。
- 题目不要求返回值,直接对数组操作。
- 不用考虑数组长度和k的大小关系,就是移动就行。
- 写出来三种算法。
思路一
先来个笨办法,在数组后面加一项,逐个向后移,再把最后一项移到最前面,这个方法的时间复杂度应该很高。
修改经历:
1. 我知道时间会很长,但是没想到超出了时间限制?!!头大,两层循环看来不行啊。(第一次提交)
心得体会:
这个方法,我没有在题解关于Python 3上看到,可能就是太笨了吧。
最终代码展示:
class Solution:
def rotate(self, nums: List[int], k: int) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
for i in range(k%len(nums)):
temp = nums[-1]
for j in range(len(nums)-1, 0, -1):
nums[j] = nums[j-1]
nums[0] = temp
思路二
我想我可能是个傻子。。。直接把数组后面的截取放到前面不好吗?但是这样就可能要新开辟一个数组了。
修改经历:
1. 没有考虑到输入数组为[1],k=0的情况(第一次提交)
2.没有考虑到k大于数组长度的情况,比如[1],k=2。(第二次提交)
- 执行用时 :36 ms, 在所有 Python3 提交中击败了95.39%的用户
- 内存消耗 :13.8 MB, 在所有 Python3 提交中击败了97.58%的用户
心得体会:
我是真的没有想到,思路二竟然能通过,而且排名还这么靠前。。。O(1)的空间复杂度呢?这不就是相当于O(N)的空间复杂度了吗?
看到一种非常简单的写法:
lenth = len(nums) nums[:] = nums[lenth-k:]+nums[:lenth-k]
这个真的是牛逼了,连临时变量都不需要了
最终代码展示:
class Solution:
def rotate(self, nums: List[int], k: int) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
if k == 0 or len(nums) == 1:
pass
else:
nums1 = nums[-k%(len(nums)):]
nums2 = nums[:-k%(len(nums))]
nums[:k%(len(nums))] = nums1
nums[k%(len(nums)):] = nums2
思路三
这个思路是在题解上看到的,把最后一个pop(),再插入第一项。。。代码就两行。看来Python还是没玩转啊。
最终代码展示:
class Solution(object):
def rotate(self, nums, k):
"""
:type nums: List[int]
:type k: int
:rtype: None Do not return anything, modify nums in-place instead.
"""
k=k%len(nums)
for i in range(k):
nums.insert(0,nums.pop())
思路四
这是看题解的大神写得,我觉得这种是最复合题目要求的空间复杂度为O(1)的要求了。这就是翻转法,我把解法贴出来了。
三次反转
对于[1,2,3,4,5,6,7],
根据k=k\%n,将数组分为两段:
- 第一段,对应数组下标范围[0,n-k-1]段,即[1,2,3,4]
- 第二段,对应数组下标范围[n-k,n-1],即[5,6,7]
分为三步:
- 反转第一段,[4,3,2,1,5,6,7]
- 反转第二段,[4,3,2,1,7,6,5]
- 反转整体,[5,6,7,1,2,3,4]
完成!
心得体会:
这个方法妙哉啊,要好好记下来!这个是怎么想到的呢?
最终代码展示:
class Solution:
def rotate(self, nums: List[int], k: int) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
def swap(former, latter):
while former < latter:
nums[former], nums[latter] = nums[latter], nums[former]
former += 1
latter -= 1
k = k % (len(nums))
swap(0, len(nums)-k-1)
swap(len(nums)-k, len(nums)-1)
swap(0, len(nums)-1)
04 存在重复-20200317
题目
给定一个整数数组,判断是否存在重复元素。
如果任何值在数组中出现至少两次,函数返回 true。如果数组中每个元素都不相同,则返回 false。
示例
输入: [1,2,3,1]
输出: true
注意事项
- 这道题并没有要求空间复杂度。
- 注意要求任何数字出现至少两次。
- 要考虑负整数。
思路一
这道题应该不难,最简单的思路就是直接新建一个数组,里面放上每个元素出现的次数。如果这个数组里有大于1的存在就输出false。不过要注意的是这个数组的索引问题。
修改经历:
1. 这种新建数组查询的操作,可以实现,但是太费时间了,结果当然是超出时间限制。我想应该是在“not in”这个操作上,这相当于是有执行了一遍循环查询,所以时间就会很长。如果一遍循环就能做出来就行。(第一次提交)
- 超出时间限制 N/A
心得体会:
这个方法好像总是会出现超出时间限制的要求。
最终代码展示:
class Solution:
def containsDuplicate(self, nums: List[int]) -> bool:
# 思路一
duplicate_array = []
for i in range(0, len(nums)):
if nums[i] in duplicate_array:
return True
else:
duplicate_array.append(nums[i])
return False
思路二
先对数组进行排序,再对比相邻的项,重复就输出True。
修改经历:
1. 没有考虑数组长度小于2的情况。(第一次提交)
- 执行用时 :36 ms, 在所有 Python3 提交中击败了98.18%的用户
- 内存消耗 :16.7 MB, 在所有 Python3 提交中击败了100.00%的用户
心得体会:
其实这种方法感觉并不是很优秀。
最终代码展示:
class Solution:
def containsDuplicate(self, nums: List[int]) -> bool:
# 思路二
if len(nums) < 2:
return False
else:
pass
nums.sort()
for i in range(0, len(nums)-1):
if nums[i] == nums[i+1]:
return True
else:
pass
return False
思路三
这个是看题解得到的,是利用Python中的set无序不重复集合。知识点放在下面的复习的 3. set 无序不重复集合 中了。不过这种的空间复杂度要高一些。
心得体会:
这种方法还是要对Python有一定的了解才行。不过这个时间复杂度也挺高的。
最终代码展示:
class Solution(object):
def containsDuplicate(self, nums):
"""
:type nums: List[int]
:rtype: bool
"""
return len(list(set(nums))) != len(nums)
思路四
这个想法其实和我之前想的差不多,就是把数组中的数字当做下标。不过我担心这样新建的数组长度会过大。这个思路用字典可以很好地避开长度的问题。利用字典的键当做下标,值是重复次数,完美。
最终代码展示:
class Solution:
def containsDuplicate(self, nums: List[int]) -> bool:
dic = {}
for i in nums:
if dic.get(i):
return True
dic[i] = 1
return False
05 只出现一次的数字-20200317
题目
给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
示例
输入: [2,2,1]
输出: 1
说明
你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?
注意事项
- 整数数组非空,按照题目要求,数组最少有三个数字。
- 时间复杂度是O(N),最好一个循环内搞定。
- 不新开存储空间。
思路一
先对数组排序,然后一次遍历,找到三个相邻指针不一样的,中间那个指针就是。
修改经历:
1. 不是最少三个数字吗,你给我个[1],我心态崩了啊!(第一次提交)
- 执行用时 :84 ms, 在所有 Python3 提交中击败了74.65%的用户
- 内存消耗 :15.1 MB, 在所有 Python3 提交中击败了64.46%的用户
2. 虽然没有开辟新的空间,但是这里的内存消耗有点大。不过平台每次提交都会有稍许的差别,在提交一次。(第二次提交)
- 执行用时 :48 ms, 在所有 Python3 提交中击败了87.08%的用户
- 内存消耗 :15 MB, 在所有 Python3 提交中击败了93.98%的用户
3. 第二次提交结果说明,大部分答案都集中用时40-100ms,内存更是集中在15MB左右。
心得体会:
排序还是很好用的,先排序再找元素,只要不要索引就行。
最终代码展示:
class Solution:
def singleNumber(self, nums: List[int]) -> int:
nums.sort()
if len(nums) > 1:
for i in range(1, len(nums)-1):
if nums[i-1] != nums[i] and nums[i] != nums[i+1]:
return nums[i]
elif i == len(nums)-2 and nums[i-1] == nums[i] and nums[i] != nums[i+1]:
return nums[i+1]
elif i == 1 and nums[i-1] != nums[i] and nums[i] == nums[i+1]:
return nums[i-1]
else:
return nums[0]
思路二
看到题解的方法,不得不的说大神还是大神。就一个公式 2∗(a+b+c)−(a+a+b+b+c)=c。
最终代码展示:
class Solution(object):
def singleNumber(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
return 2 * sum(set(nums)) - sum(nums)
思路三
朋友,听说过异或吗?大神就是不一般啊!下面就是大神的思路。
所以只要将所有的项都异或起来就可以得到只出现一次的项了。Python 3的位操作,参看知识点 Python 中的 位运算符
最终代码展示:
class Solution(object):
def singleNumber(self, nums):
"""
:type nums: List[int]
:rtype: int
"""
a = 0
for i in nums:
a ^= i
return a
06 两个数组的交集 II-20200318
题目
给定两个数组,编写一个函数来计算它们的交集。
示例
输入: nums1 = [1,2,2,1], nums2 = [2,2]
输出: [2,2]
说明
- 输出结果中每个元素出现的次数,应与元素在两个数组中出现的次数一致。
- 我们可以不考虑输出结果的顺序。
进阶
- 如果给定的数组已经排好序呢?你将如何优化你的算法?
- 如果 nums1 的大小比 nums2 小很多,哪种方法更优?
- 如果 nums2 的元素存储在磁盘上,磁盘内存是有限的,并且你不能一次加载所有的元素到内存中,你该怎么办?
注意事项
- 注意数组长度,尤其是空数组。
- 就算是重复的元素,也要算入交集中,就想示例中一样,就算有两个2也要写出来。
思路一
先用set()操作筛选无重复的数组元素,然后看相同元素中,两个数组中是否出现里相同次数,最后把输出相同次数的重复元素。
修改经历:
1. 写得太复杂了,但是执行的时候函数出错了。先化简下。注意到,测试的答案中,并没有排序!(第一次提交)
- 解答错误
2. 将前面的set()操作删除,只剩下一个双层的循环,但是这次提交也是错误,在 [1,2] 和 [2,1] 输入后得到的是 [1] 而不是 [1,2]。主要是第二个数组pop()的index没有设置好。(第二次提交)
- 解答错误
3. 这次成功了,但是成绩惨不忍睹。看来双层循环不是好的答案啊。(提三次提交)
- 执行用时 :176 ms, 在所有 Python3 提交中击败了5.33%的用户
- 内存消耗 :13.7 MB, 在所有 Python3 提交中击败了5.40%的用户
心得体会:
有时候自己写的代码自己都发愁,这是啥玩意啊。发现自己很容易掉入一个细节内,浪费大量的时间,应该是先考虑整体,再回来看细节。
最终代码展示:
class Solution:
def intersect(self, nums1: List[int], nums2: List[int]) -> List[int]:
final_intersect = []
for i in range(len(nums1)-1, -1, -1):
for j in range(len(nums2)-1, -1, -1):
if nums1[i] == nums2[j]:
final_intersect.insert(0, nums1[i])
nums2.pop(j)
break
else:
pass
return final_intersect
思路二
参考大神的题解,发现和我之前想的hash做法一样,但是我之前都是直接把代码放上去的,没有自己写过,所以这次没写出来。因此之后的代码都要写,不管是谁的。
思路二的做法是针对无序的数组,采用字典将项的值看做键,将重复次数看做值。对数组分别遍历。
修改经历:
1. 在遍历第二个数组时,少一个判断条件。(第一次提交)
- 解答错误
2. 这一次成功了,用时也少了很多,但是内存依旧不理想。毕竟还是用到字典。(第二次提交)
- 执行用时 :64 ms, 在所有 Python3 提交中击败了52.98%的用户
- 内存消耗 :13.7 MB, 在所有 Python3 提交中击败了5.40%的用户
心得体会:
采用hash的算法,可以针对无序的数组。可以对较小的数组进行hash遍历。
- 时间复杂度:O(n + m)。其中 n,m 分别代表了数组的大小。
- 空间复杂度:O(min(n,m)),我们对较小的数组进行哈希映射使用的空间。
最终代码展示:
class Solution:
def intersect(self, nums1: List[int], nums2: List[int]) -> List[int]:
dict = {}
for item in nums1:
if item in dict:
dict[item] += 1
else:
dict[item] = 1
final = []
for item in nums2:
if item in dict and dict[item] > 0:
final.append(item)
dict[item] -= 1
else:
pass
return final
思路三
还可以,提前对数组排好顺序,再操作(也可以直接针对已经排好序的数组)。排好序好之后,两个指针同时操作。因为是排好序的,所以数组时递增的,两个比较,相等就存下来。若不相等,大的不动,小的加一。
修改经历:
1. 输出的结构没有算入重复项的重复次数。主要是判断条件用的是 or,这个应该用 and,只要一个不行就不行。(第一次提交)
- 解答错误
2. 这次没有问题,耗时由上升一步,而且我还是用了排序,如果像进阶中说的那样,排好序的数组,这个方法耗时还能再少一些。但是内存还是很大,可能有最后的输出数组吧。如果在数组一上直接删除,就不用那么多内存了。(第二次提交)
- 行用时 :52 ms, 在所有 Python3 提交中击败了81.65%的用户
- 内存消耗 :13.6 MB, 在所有 Python3 提交中击败了5.40%的用户
3. 这次没有新建最后的输出数组,而是直接在第一个数组上操作,但是出现了当输入为[1],[0]时,输出错误的问题。也就是没有考虑空数组的情况。(第三次提交)
- 解答错误
4. 超出时间限制了?为什么呢,怎么可能呢?卧槽。。。else没对齐(第四次提交)
- 解答错误
5. 又错了,应该是我把第一个数组默认为最大数值小的那个数组了,而当情况相反时,就是数组的最大值大于数组一时,就出错了。做一个判断就好。(第五次提交)
- 执行用时 :68 ms, 在所有 Python3 提交中击败了49.74%的用户
- 内存消耗 :13.6 MB, 在所有 Python3 提交中击败了5.40%的用户
心得体会:
我就想知道这个内存怎么变小?这怎么和给出的内存复杂度不一样呢?
- 时间复杂度:O(nlogn+mlogm)。其中 n,m 分别代表了数组的大小。我们对数组进行了排序然后进行了线性扫描。
- 空间复杂度:O(1),我们忽略存储答案所使用的空间,因为它对算法本身并不重要
最终代码展示:
class Solution:
def intersect(self, nums1: List[int], nums2: List[int]) -> List[int]:
nums1.sort()
nums2.sort()
i, j = 0, 0
if len(nums1) and len(nums2):
while i < len(nums1) and j < len(nums2):
if nums1[i] == nums2[j]:
i += 1
j += 1
elif nums1[i] > nums2[j]:
nums2.pop(j)
else:
nums1.pop(i)
if nums1 == nums2:
return nums1
elif len(nums1) > len(nums2):
return nums2
else:
return nums1
else:
return []
还有一种效果一般,但是写法很炫酷的写法,就不单独拿出写了。
class Solution: def intersect(self, nums1: List[int], nums2: List[int]) -> List[int]: inter = set(nums1) & set(nums2) l = [] for i in inter: l += [i] * min(nums1.count(i), nums2.count(i)) return l
07 加一-20200319
题目
给定一个由整数组成的非空数组所表示的非负整数,在该数的基础上加一。
最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。
你可以假设除了整数 0 之外,这个整数不会以零开头。
示例
输入: [1,2,3]
输出: [1,2,4]
解释: 输入数组表示数字 123。
注意事项
- 这。。。直接在最后一项加一不就好了。。。不过要注意,如果大于9就要进位。
思路一
直接在最后一项加1,注意进位。
修改经历:
1. 当输入为[8,9,9,9]时,答案不对,尽管我已经考虑了进位,可能是哪里出问题了。经过检查发现是逻辑判断出错,index=0的项多判断了一次(第一次提交)
- 解答错误
2. 修改过后就成功了。但是这个内存消耗还是很大。(第二次提交)
- 执行用时 :32 ms, 在所有 Python3 提交中击败了87.47%的用户
- 内存消耗 :13.6 MB, 在所有 Python3 提交中击败了5.12%的用户
心得体会:
这题还是很简单的。
最终代码展示:
class Solution:
def plusOne(self, digits: List[int]) -> List[int]:
digits[-1] += 1
for i in range(len(digits)-1, -1, -1):
if digits[i] > 9 and i == 0:
digits[0] = 0
digits.insert(0, 1)
elif digits[i] > 9 and i != 0:
digits[i] = 0
digits[i-1] += 1
else:
pass
return digits
08 移动零-20200320
题目
给定一个数组 nums
,编写一个函数将所有 0
移动到数组的末尾,同时保持非零元素的相对顺序。
示例
输入: [0,1,0,3,12]
输出: [1,3,12,0,0]
说明
- 必须在原数组上操作,不能拷贝额外的数组。
- 尽量减少操作次数。
注意事项:
- 不能额外开辟新空间。
- 注意空数组。
思路一
直接一次遍历,找到0进行pop()操作,再将0进行insert()插到最后。
修改经历:
1. 没有考虑到两个零相邻的情况,这样的话,循环会一直卡在这里。(第一次提交)
- 解答错误
2. 换了个思路,将pop和insert分开做,先弹出再再最后insert0。但是这个内存很是很大,每次都是14MB左右。(第二次提交)
- 执行用时 :36 ms, 在所有 Python3 提交中击败了95.86%的用户
- 内存消耗 :14 MB, 在所有 Python3 提交中击败了5.02%的用户
心得体会:
要注意while和for的区别,for里的索引会一直增加,如果想要自己控制指针的话,还是用while好。
最终代码展示:
class Solution:
def moveZeroes(self, nums: List[int]) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
if len(nums) != 0:
length, i = len(nums), 0
while i < len(nums):
if nums[i] == 0:
nums.pop(i)
else:
i += 1
nums.append(0)
else:
pass
for _ in range(0, length-len(nums)):
nums.append(0)
return nums
思路二
采用双指针,一个向前遇到0弹出,指针不变,遇到非零加一。一个向后,当弹出0时插入,并减一。最后两个相等结束。
修改经历:
1. 一次就成了。(第一次提交)
- 执行用时 :36 ms, 在所有 Python3 提交中击败了95.86%的用户
- 内存消耗 :14.1 MB, 在所有 Python3 提交中击败了5.02%的用户
心得体会:
当问题分析清楚了,还是很容易就解决了。
最终代码展示:
class Solution:
def moveZeroes(self, nums: List[int]) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
if len(nums) != 0:
i, j = 0, len(nums)
while i < j:
if nums[i] == 0:
nums.insert(j, 0)
nums.pop(i)
j -= 1
else:
i += 1
else:
pass
return nums
思路三
这个思路是从题解大神那学来的,就是交换项。两个指针 i 和 j,i 只管向前走,遇到非零元素就和 j 交互,遇到0就pass。j 遇到非零元素元素,交换后加一。这样,j 就会小于等于 i。开始时 i 和 j 一起走,当 i 遇到 0时跳过,此时 j 就留在了0上。所以交换时就会把0换到后面。
修改经历:
1. 大神就是大神,服了。(第一次提交)
- 执行用时 :28 ms, 在所有 Python3 提交中击败了99.59%的用户
- 内存消耗 :14.1 MB, 在所有 Python3 提交中击败了5.02%的用户
心得体会:
这个换位的想法很妙啊,真的很妙,强烈推荐的!!有大神说这是 Cyclic Sort(循环排序)
最终代码展示:
class Solution:
def moveZeroes(self, nums: List[int]) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
if len(nums) != 0:
j = 0
for i in range(0, len(nums)):
if nums[i] != 0:
nums[i], nums[j] = nums[j], nums[i]
j += 1
return nums
09 两数之和-20200321
题目
给定一个整数数组 nums
和一个目标值 target
,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。
你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。
示例
给定 nums = [2, 7, 11, 15], target = 9
因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]
注意事项
- 注意数组长度,可以返回空数组。
- 有可能是负数存在。
思路一
第一个出现在脑海中的就是双层循环,但是复杂度肯定就是高了。于是想出来采用字典的形式,一次循环。
- 将指针指到的数字与目标做差,将差值作为key,index作为value存下来。
- 指针加一,如果能够在字典中找到相应的key就返回相应的value和当前的index。如果没有找到就重复第一步。
修改经历:
1. 提交成功。是不是Python 3的运行内存都是大于14MB的啊?(第一次提交)
- 执行用时 :40 ms, 在所有 Python3 提交中击败了92.75%的用户
- 内存消耗 :15 MB, 在所有 Python3 提交中击败了5.03%的用户
心得体会:
这个方法挺好,就暂时不考虑其他算法了。
最终代码展示:
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
sum_index = []
dict = {}
for i in range(0, len(nums)):
if nums[i] in dict:
sum_index += [dict[nums[i]], i]
break
else:
dict[target-nums[i]] = i
return sum_index
10 有效的数独-20200321
题目
判断一个 9x9 的数独是否有效。只需要根据以下规则,验证已经填入的数字是否有效即可。
- 数字
1-9
在每一行只能出现一次。 - 数字
1-9
在每一列只能出现一次。 - 数字
1-9
在每一个以粗实线分隔的3x3
宫内只能出现一次。
数独部分空格内已填入了数字,空白格用 '.'
表示。
示例
输入:
[
["5","3",".",".","7",".",".",".","."],
["6",".",".","1","9","5",".",".","."],
[".","9","8",".",".",".",".","6","."],
["8",".",".",".","6",".",".",".","3"],
["4",".",".","8",".","3",".",".","1"],
["7",".",".",".","2",".",".",".","6"],
[".","6",".",".",".",".","2","8","."],
[".",".",".","4","1","9",".",".","5"],
[".",".",".",".","8",".",".","7","9"]
]
输出: true
说明
- 一个有效的数独(部分已被填充)不一定是可解的。
- 只需要根据以上规则,验证已经填入的数字是否有效即可。
- 给定数独序列只包含数字
1-9
和字符'.'
。 - 给定数独永远是
9x9
形式的。
注意事项
- 数独的形式永远都是9x9。
- 空格用字符 '.'来代替了。
每个3x3的数组有5个数字,所以1-9每个数字都要出现5次。也就是说每个数字的重复次数等于5。不对!!
思路一
用四个指针i,j,k,h做。三个判断设置用和三个字典single、row和column实现。数组来存储各个3*3的元素是否存在的指示。字典用来判断每行每列的重复元素。
- 判断字典single[nums[i,j]]是否为1,同时判断字典row[nums[i,j]]的数组中是否有i,字典colum[nums[i,j]的数组中是否有j。如果有一项不成立就返回False。否则字典存入相应项,并j加一。j在range(h,h+3)。
- 判断j是否等于2,5,8。如果等于就重置字典single,h+=3,否则pass。
- i循环range(k,k+3),循环完毕就k+=3。
修改经历:
1. 在判断时,把元素当做行列的index了。(第一次提交)
- 解答错误
2. 修改过后,提交成功。(第二次提交)
- 执行用时 :48 ms, 在所有 Python3 提交中击败了92.28%的用户
- 内存消耗 :13.6 MB, 在所有 Python3 提交中击败了5.03%的用户
心得体会:
- 注意 i = j = 0 和 i, j =0, 0的区别,前者相当于两个指针指向一个地址,后者是两个指针指向两个地址。同理列表,字典等都是一样的。
- 还有 list 和 narray 的区别,前者是[i][j]取元素,后者是[i,j]取元素。
- 这个思路真的是很垃圾,其实这不是列表呢,直接取出来每行每列判断就好,难点就在怎么判断3*3的矩阵。
- 字典可以这么写
rows = [{} for i in range(9)]
最终代码展示:
class Solution:
def isValidSudoku(self, board: List[List[str]]) -> bool:
i, j, k, h = 0, 0, 0, 0
single = {0:[], 1:[], 2:[], 3:[], 4:[], 5:[], 6:[], 7:[], 8:[], 9:[]}
row = {0:[], 1:[], 2:[], 3:[], 4:[], 5:[], 6:[], 7:[], 8:[], 9:[]}
column = {0:[], 1:[], 2:[], 3:[], 4:[], 5:[], 6:[], 7:[], 8:[], 9:[]}
for k in range(0, 9, 3):
for h in range(0, 9, 3):
for i in range(k, k+3):
for j in range(h, h+3):
if board[i][j] != '.':
item = int(board[i][j])
if 1 not in single[item] and i not in row[item] and j not in column[item]:
single[item] += [1]
row[item] += [i]
column[item] += [j]
else:
return False
else:
pass
single = {0:[], 1:[], 2:[], 3:[], 4:[], 5:[], 6:[], 7:[], 8:[], 9:[]}
return True
思路二
这次直接取出来判断。不要一个一个元素进行判断。比较难判断的就是每一小块怎么判断。找到每一块的index与循环次数的关系就好。n=[0,1,...,8],则Z形排列的小块的index为,横坐标=[3*int(n/3):3*int(n/3)+3],纵坐标=[3*int(n%3):3*int(n%3)+3]。
修改经历:
1. 写了半天,or写成and了。。。(第一次提交)
- 解答错误
2. 修改过提交成功。(第二次提交)
- 执行用时 :124 ms, 在所有 Python3 提交中击败了23.89%的用户
- 内存消耗 :13.6 MB, 在所有 Python3 提交中击败了5.03%的用户
心得体会:
- 一定要注意列表的切片与narray不同!!!A=[[1,2],[3,4]]要取出[1,3]要单独在每一个list里取相应index的值。A[:][0]与A[:][0]都是[1,2]。[x[0] for x in A]才行。
- 怎么还慢了呢?
最终代码展示:
class Solution:
def isValidSudoku(self, board: List[List[str]]) -> bool:
for n in range(1, 10):
for k in range(0, 9):
column = [x[k] for x in board]
temp = [x[3*int(k%3):3*int(k%3)+3] for x in board[3*int(k/3):3*int(k/3)+3]]
block = []
for item in temp:
block += item
if board[k][:].count(str(n)) > 1 or column.count(str(n)) > 1 or block.count(str(n)) > 1:
return False
return True
11 旋转图像-20200322
题目
给定一个 n × n 的二维矩阵表示一个图像。
将图像顺时针旋转 90 度。
示例
给定 matrix =
[
[1,2,3],
[4,5,6],
[7,8,9]
],
原地旋转输入矩阵,使其变为:
[
[7,4,1],
[8,5,2],
[9,6,3]
]
说明
你必须在原地旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要使用另一个矩阵来旋转图像。
注意事项
- 图像是一个方阵,长度为n。
- 必需在原地操作,不得新开辟数组,但是可以开辟常数个新空间。
思路一
将方阵分层处理,第一层是最外的一层,然后是里面的一层,逐级旋转。层数k={0,...,n/2},每层的索引范围是m=[k,n-k],每一层的旋转规律是 [k+i][ind], [ind][ind-i], [ind-i][k], [k][k+i] = [k][k+i], [k+i][ind], [ind][ind-i], [ind-i][k],其中i=range(k, n-2*k+1)。
修改经历:
1. 开始的逻辑没有找到,只找到第一层,第二层就出问题了。(第一次提交)
- 解答错误
2. 修改逻辑后,提交成功。(第二次提交)
- 执行用时 :32 ms, 在所有 Python3 提交中击败了92.72%的用户
- 内存消耗 :13.6 MB, 在所有 Python3 提交中击败了5.02%的用户
心得体会:
- 这个逻辑绕了半天,找了半天的行列关系,总是没对准。。。
最终代码展示:
class Solution:
def rotate(self, matrix: List[List[int]]) -> None:
"""
Do not return anything, modify matrix in-place instead.
"""
n = len(matrix)
for k in range(0, int(n/2)):
ind = n - k - 1
for i in range(0, n-1-2*k):
matrix[k+i][ind], matrix[ind][ind-i], matrix[ind-i][k], matrix[k][k+i] = matrix[k][k+i], matrix[k+i][ind], matrix[ind][ind-i], matrix[ind-i][k]
思路二
题解大神的思路,转置然后旋转每一行。。。我跪了。。。
修改经历:
1. 一次就成了。(第一次提交)
心得体会:
生命不息,学习不止啊
最终代码展示:
class Solution:
def rotate(self, matrix: List[List[int]]) -> None:
"""
Do not return anything, modify matrix in-place instead.
"""
n = len(matrix)
for i in range(0, n):
for j in range(i, n):
matrix[i][j], matrix[j][i] = matrix[j][i], matrix[i][j]
for x in matrix:
x.reverse()