LeetCode 探索初级算法-数组

目录

01 删除排序数组中的重复项-20200314

02 买股票的最佳时机 II-20200315

03 旋转数组-20200316

04 存在重复-20200317

05 只出现一次的数字-20200317

06 两个数组的交集 II-20200318

07 加一-20200319

08 移动零-20200320

09 两数之和-20200321

10 有效的数独-20200321

11 旋转图像-20200322


01 删除排序数组中的重复项-20200314

题目

给定一个排序数组,你需要在 原地 删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。

不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。

示例

给定数组 nums = [1,1,2],
函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 1,2。

你不需要考虑数组中超出新长度后面的元素。

说明

为什么返回数值是整数,但输出的答案是数组呢?

请注意,输入数组是以「引用」方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。


注意事项

  1. 函数输入为列表(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%的用户

心得体会:

  1. 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 。

注意事项

  1. 一次只能持有一只股票,只能操作一只股票。
  2. 这个相当于给定一个乱序的数组,然后在不改变数组顺序的,降序找到差值最大的组合,将差值相加。若降序被打断,则从新降序计算。
  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%的用户

心得体会:

  1. 根据题目,不要想得太复杂。

最终代码展示:

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

题目

给定一个数组,将数组中的元素向右移动 个位置,其中 是非负数。

示例

输入: [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) 的 原地 算法。

注意事项

  1. 循环移动,最笨的办法就是逐一移动,因为要求空间复杂度为O(1),所以不能另外开辟新数组。
  2. 题目不要求返回值,直接对数组操作。
  3. 不用考虑数组长度和k的大小关系,就是移动就行。
  4. 写出来三种算法。

思路一

先来个笨办法,在数组后面加一项,逐个向后移,再把最后一项移到最前面,这个方法的时间复杂度应该很高。

修改经历:

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. 这道题并没有要求空间复杂度。
  2. 注意要求任何数字出现至少两次。
  3. 要考虑负整数。

思路一

这道题应该不难,最简单的思路就是直接新建一个数组,里面放上每个元素出现的次数。如果这个数组里有大于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

说明

你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?


注意事项

  1. 整数数组非空,按照题目要求,数组最少有三个数字。
  2. 时间复杂度是O(N),最好一个循环内搞定。
  3. 不新开存储空间。

思路一

先对数组排序,然后一次遍历,找到三个相邻指针不一样的,中间那个指针就是。

修改经历:

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)

思路三

朋友,听说过异或吗?大神就是不一般啊!下面就是大神的思路。

  • a \bigotimes 0=a
  • a\bigotimes 0 = a
  • a\bigotimes a\bigotimes b=0\bigotimes b=b

所以只要将所有的项都异或起来就可以得到只出现一次的项了。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 的元素存储在磁盘上,磁盘内存是有限的,并且你不能一次加载所有的元素到内存中,你该怎么办?

注意事项

  1. 注意数组长度,尤其是空数组。
  2. 就算是重复的元素,也要算入交集中,就想示例中一样,就算有两个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。

注意事项

  1. 这。。。直接在最后一项加一不就好了。。。不过要注意,如果大于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]

说明

  1. 必须在原数组上操作,不能拷贝额外的数组。
  2. 尽量减少操作次数。

注意事项:

  1. 不能额外开辟新空间。
  2. 注意空数组。

思路一

直接一次遍历,找到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]

注意事项

  1. 注意数组长度,可以返回空数组。
  2. 有可能是负数存在。

思路一

第一个出现在脑海中的就是双层循环,但是复杂度肯定就是高了。于是想出来采用字典的形式,一次循环。

  1. 将指针指到的数字与目标做差,将差值作为key,index作为value存下来。
  2. 指针加一,如果能够在字典中找到相应的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. 数字 1-9 在每一行只能出现一次。
  2. 数字 1-9 在每一列只能出现一次。
  3. 数字 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 形式的。

注意事项

  1. 数独的形式永远都是9x9。
  2. 空格用字符 '.'来代替了。
  3. 每个3x3的数组有5个数字,所以1-9每个数字都要出现5次。也就是说每个数字的重复次数等于5。 不对!!

思路一

用四个指针i,j,k,h做。三个判断设置用和三个字典single、row和column实现。数组来存储各个3*3的元素是否存在的指示。字典用来判断每行每列的重复元素。

  1. 判断字典single[nums[i,j]]是否为1,同时判断字典row[nums[i,j]]的数组中是否有i,字典colum[nums[i,j]的数组中是否有j。如果有一项不成立就返回False。否则字典存入相应项,并j加一。j在range(h,h+3)。
  2. 判断j是否等于2,5,8。如果等于就重置字典single,h+=3,否则pass。
  3. i循环range(k,k+3),循环完毕就k+=3。

修改经历:

1. 在判断时,把元素当做行列的index了。(第一次提交)

  • 解答错误

2. 修改过后,提交成功。(第二次提交)

  • 执行用时 :48 ms, 在所有 Python3 提交中击败了92.28%的用户
  • 内存消耗 :13.6 MB, 在所有 Python3 提交中击败了5.03%的用户

心得体会:

  1. 注意 i = j = 0 和 i, j =0, 0的区别,前者相当于两个指针指向一个地址,后者是两个指针指向两个地址。同理列表,字典等都是一样的。
  2. 还有 list 和 narray 的区别,前者是[i][j]取元素,后者是[i,j]取元素。
  3. 这个思路真的是很垃圾,其实这不是列表呢,直接取出来每行每列判断就好,难点就在怎么判断3*3的矩阵。
  4. 字典可以这么写 
    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%的用户

心得体会:

  1. 一定要注意列表的切片与narray不同!!!A=[[1,2],[3,4]]要取出[1,3]要单独在每一个list里取相应index的值。A[:][0]与A[:][0]都是[1,2]。[x[0] for x in A]才行。
  2. 怎么还慢了呢?

最终代码展示:

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 的二维矩阵表示一个图像。

将图像顺时针旋转 90 度。

示例

给定 matrix = 
[
  [1,2,3],
  [4,5,6],
  [7,8,9]
],

原地旋转输入矩阵,使其变为:
[
  [7,4,1],
  [8,5,2],
  [9,6,3]
]

说明

你必须在原地旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要使用另一个矩阵来旋转图像。


注意事项

  1. 图像是一个方阵,长度为n。
  2. 必需在原地操作,不得新开辟数组,但是可以开辟常数个新空间。

思路一

将方阵分层处理,第一层是最外的一层,然后是里面的一层,逐级旋转。层数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%的用户

心得体会:

  1. 这个逻辑绕了半天,找了半天的行列关系,总是没对准。。。

最终代码展示:

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()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值