CodingTour
ARTS #12

Algorithm

本周选择的算法题是:Contains Duplicate III

规则如下:

Given an array of integers, find out whether there are two distinct indices i and j in the array such that the absolutedifference between nums[i] and nums[j] is at most t and the absolute difference between i and j is at most k.

Example 1:

Input: nums = [1,2,3,1], k = 3, t = 0
Output: true

Example 2:

Input: nums = [1,0,1,1], k = 1, t = 2
Output: true

Example 3:

Input: nums = [1,5,9,1,5,9], k = 2, t = 3
Output: false

Solution

我实现的方案:

解法一:

先用了暴力解法:

class Solution:
    def containsNearbyAlmostDuplicate(self, nums: List[int], k: int, t: int) -> bool:
        for i in range(len(nums) - 1):
            for j in range(i + 1, min(len(nums), i + k + 1)):
                if abs(nums[i] - nums[j]) <= t:
                    return True
        return False

果然还是超时了 - -

解法二:

用到了桶的思想,同时为了减少内存占用,运行时动态删除桶中超过 k 个索引的元素:

class Solution:
    def containsNearbyAlmostDuplicate(self, nums: List[int], k: int, t: int) -> bool:
        if t < 0: return False
            
        buckets = {}
        for i in range(len(nums)):
            bucket = nums[i] // (t + 1)
            if bucket in buckets:
                return True
            
            if bucket - 1 in buckets:
                if nums[i] - buckets[bucket - 1] <= t:
                    return True

            if bucket + 1 in buckets:
                if buckets[bucket + 1] - nums[i] <= t:
                    return True

            buckets[bucket] = nums[i]
            if i >= k:
                del buckets[nums[i - k] // (t + 1)]

        return False

Runtime:72 ms,快过 52.6%。

Memory:16 MB,低于 33.33%。

Review

Modularize an iOS application
关于模块化的文章,观点没有太多新颖之处,总结下常见的两种模块类型:

  • 功能性模块(Feature modules)
  • 非功能性模块(Kit modules)

从依赖关系上看:

  1. 功能性模块能依赖一个或多个非功能性模块
  2. 非功能性模块不能依赖任何功能性模块
  3. 功能性模块不能依赖其他功能性模块

那么,模块化的过程中容易碰到的问题有哪些呢?一般来说是如下几个部分:

  • 模块之间横向依赖
  • 模块之间相互依赖
  • 如果一个新功能会横跨几个模块,那这个功能放在哪个模块中
  • 资源不共享,每个模块都有自己的 bundle
  • 将已经紧密耦合的功能梳理到不同的模块

还在继续思考我上周的模块化思路,传送门,希望新的一周能尝试落地

Tip

终端

  • sudo !! - !! 将执行最后一条语句,但是前面会加上 sudo 前缀;可以任意前缀
  • tig - git 的反序,这是一个文本模式的 GUI 工具,几乎支持所有的 git 功能,地址

Share

如何写出干净的代码?

重视命名

任何变量、方法、对象的名字都要回答三个问题:

  1. 它为什么存在
  2. 它做什么
  3. 它干什么用的

方法只做一件事

好的方法有两条黄金准则:

  1. 它们很小
  2. 它们只做一件事,而且这件事干得很好

注释不能弥补坏的代码

注释是一把双刃剑。合适的位置出现合适的注释,这对开发者来讲帮助很大;同时,注释也能产生混乱和谎言,特别是当注释和代码不匹配时。

干净、自解释的代码,再配上少许注释就够了,这比大量注释+复杂代码更有用,与其将时间花在解释你的恶心代码,不如把时间用来改善你的代码。

格式化代码总是很重要的

代码是面向沟通的,它是一个与程序员沟通的窗口,虽然人们不会记得哪些程序员的代码风格很好,但是风格不好的程序员往往会被记住。代码的风格应该是所有团队成员能理解的简单规则的约束,它影响着可读性,也影响着代码可维护性。

先写 “try-catch-finally” 语句

入参可能不正常,硬件也有可能出问题,作为程序员,我们需要确保程序在做我们期望它做的事。这其中重要的地方不在于错误有没有被处理,而在于错误被如何处理,是不是以一种干净、可读的方式来处理。

代码应该是干净、易读且健壮的,它同时还可以优雅地处理错误,这是一位伟大的软件工匠的标志。