第 6 周 ARTS

一. Algorithms

本周做的题目是 33. Search in Rotated Sorted Array

看到题目的第一眼是懵逼的。感觉就是一个查询数组中元素索引的算法,最简单的方式就是直接遍历了吧。于是就有了下面这段代码。

class Solution {
    public int search(int[] nums, int target) {
         for (int i = 0; i < nums.length; i ++) {
            if (nums[i] == target) {
                return i;
            }
        }
        return -1;
    }
}

写完后的运行效率 ***runtime beats 91.75 %***。 看起来还不错的样子。但作为一个 medium 难度的题目这样解未免也太简单了。回到题目。给的数组是一个经过轴旋转的数组。比如 [0,1,2,4,5,6,7] 变为 [4,5,6,7,0,1,2])。其本质变化就是由原来一个有序的数组变为了两个有序的数组。题目要求时间性能为 O(logN),很容易想到是二分查找。但问题在于数组是双段有序的,因此思路是先通过一次 二分查找找到轴点,在 [4,5,6,7,0,1,2] 中就是元素 0 的位置。在这之前之后是两端有序的数组,然后根据 target 的值二分查找其中的一个即可。时间效率为 O(logN)

class Solution {

    public int search(int[] nums, int target) {

        if (nums.length <= 2) {
            for (int i = 0; i < nums.length; i ++) {
                if (nums[i] == target) {
                    return i;
                }
            }
            return -1;
        }

        int pivot = this.getPivot(nums);
        int min = 0;
        int max = nums.length-1;
        if (pivot != -1) {
            int pivotNum = nums[pivot];
            if (pivotNum == target) {
                return pivot;
            }

            if (pivot == 0 || nums[nums.length - 1] >= target) {
                min = pivot;
            }else {
                max = pivot;
            }
        }

        int mid = 0;
        while (min <= max) {
            mid = (min + max) / 2;

            if (nums[mid] == target) {
                return mid;
            }
            if (nums[mid] > target) {
                max = mid - 1;
            }else {
                min = mid + 1;
            }
        }
        return -1;
    }

	// 获取轴点
    public int getPivot(int[] nums) {
        int min = 0;
        int max = nums.length - 1;
        int pivot = min + ((max - min) >> 1);

        while (min <= max) {


            if (pivot == 0 || pivot == (nums.length - 1)) {
                return pivot;
            }
            if (pivot > 0 && nums[pivot] < nums[pivot - 1]) {
                return pivot;
            }
            if (nums[pivot] > nums[max]) {
                min = pivot + 1;
            } else {
                max = pivot;
            }
            pivot = min + ((max - min) >> 1);
        }



        return -1;
    }
}

完成后通过,中间调试的过程遇到了一些问题主要是自己算法功底不行,对二分查找不熟导致的,下周拿出时间专门熟悉一下这个算法。

二. Review

本周读了耗子哥在分布式架构的工程设计中的一篇文章: 4 Things to Keep in Mind When Building a Platform for the Enterprise

如题目所言,文章简单概述了在构建企业平台的时候该注意的一些时间。

  • 广泛思考的设计,谨小慎微的构建。在开始建设平台的初期,我们要设想尽可能多的使用场景,包括现在的需求和已有可能的功能。然后在构建时要注意为这些可能的拓展留下空间。
  • 选择合适的接口暴露。暴露的 API 越多,以后要做出改动的代价就越大,你必须向后兼容这些 API,尤其是向 APP 端提供的 API。但是开放一定的 API 也可以让开发者用户拿到资源构建在平台核心应用之外的自己的有特殊需求或功能的应用。这对开发者平台也会起到一定借鉴作用。
  • 增量、反馈与迭代。平台构建是一个不断添加增量的过程,在这期间我们需要收集反馈,重视反馈,不断迭代。
  • 平台优先。在开发过程中要建立平台优先的工程师文化。我的理解就是一切的任务都是向平台整体服务的大目标看齐的。这样可以避免开发过程中陷入某些不是非常紧要的细节和功能中,保证我们只做最重要的事。

三. Technique

本周分享一个 Python 下划线的几个用处吧,

1. 在解释器中代表上一个表达式的值

在Python中, 解释器会将最后一个表达式的值存储在名为 _ 的临时变量中, 这个特性首先在CPython中引入,你也可以在其他的Python解释器中使用。
示例代码:

>>> 10
10
>>> _
10
>>> _ * 3
30
>>> _ * 20
600

2. 忽略某些值

下划线 _ 也可以用来忽略某些值。如果你不需要某些特殊值或者某些值已经变得没用,就可以将值分配给下划线 _ 从而将其忽略。
示例代码:

# Ignore a value when unpacking
x, _, y = (1, 2, 3) # x = 1, y = 3
# Ignore the multiple values. It is called "Extended Unpacking" which is available in only Python 3.x
x, *_, y = (1, 2, 3, 4, 5) # x = 1, y = 5  
# Ignore the index
for _ in range(10):     
    do_something()  
# Ignore a value of specific location
for _, val in list_of_tuple:
    do_something()

3. 用于对函数和变量命名来给予其特殊意义

下划线 _ 用的最多的地方应该就是命名了。Python指南中介绍了下面四种命名情况:

【1】.名称前加单下划线

这种方式用来在模块中声明变量、方法、函数、类, 标识其为私有的, 不能被外界访问。所有被声明的内容在使用import引入时会被忽略。

但是, Python实际上并没有真正的访问限制, 我们依然可以在其他模块中直接调用, 一切全靠自觉, 有时我们称它为"弱内部使用标识符"
代码示例:

_internal_name = 'one_nodule' # private variable
_internal_version = '1.0' # private variable

class _Base: # private class
    _hidden_factor = 2 # private variable
    def __init__(self, price):
        self._price = price
    def _double_price(self): # private method
        return self._price * self._hidden_factor
    def get_double_price(self):
        return self._double_price()

【2】.名称后加单下划线

这种用法主要用于避免与Python中的关键字和内置变量发生冲突, 这种方式会被经常用到。代码示例:

Tkinter.Toplevel(master, class_='ClassName') # Avoid conflict with 'class' keyword
list_ = List.objects.get(1) # Avoid conflict with 'list' built-in type
【3】.名称前加双下划线

与上面的两种使用惯例不同, 这是Python种内置的语法, 用来混淆名称来避免类之间发生属性名的冲突。(所谓的混淆本质上是Python编译器或者解释器根据某些规则修改了名称)。
Python中的修改规则是在名词前加上 _ClassName。修改后名称变为 _ClassName__method 这样的格式。
示例如下:

class A:
    def _single_method(self):
        pass
    def __double_method(self): # for mangling
        pass
class B(A):
    def __double_method(self): # for mangling
        pass

因为加了双下划线的属性会被混淆,我们无法直接通过 ClassName.__method 的方式直接调用。 因此有些人会用它来作访问权限控制。当然并不代表它是私有的,而且Python并不建议这么做,更多细节参阅这里。Python命名

【5】.名称前后加双下划线

这种方式用于一些特殊的方法和变量,像_init_, _len_,他们被称为 魔法方法 。这种方法提供了特殊的语法特定或者做一些特殊的事情。比如: _file_ 表示Python文件的位置, _eq_ 方法会在 a==b 比较时执行。

我们可以定义自己的魔法方法,但这种情况非常少见,更多的情况是需要修改某些内置的魔法方法。比如你应该重写__init__方法来初始化类, 他会在类实例创建时被立即调用。

class A:
    def __init__(self, a): # use special method '__init__' for initializing
        self.a = a
    def __custom__(self): # custom special method. you might almost do not use it
        pass

4.作为本地化/国际化功能使用

这也是个使用惯例,并没有语法支持。下划线并不代码i18/l10n, 这只是沿袭C的惯例将i18n/l10n绑定到下划线变量。内置库gettext和Python网络框架Django都采用了这个惯例。

# see official docs : https://docs.python.org/3/library/gettext.html
import gettext
gettext.bindtextdomain('myapplication','/path/to/my/language/directory')
gettext.textdomain('myapplication')
_ = gettext.gettext
# ...
print(_('This is a translatable string.'))

5. 分隔数字

这是在Python3.6 中添加的特性, 通过使用下划线分隔数字来提高可读性。示例如下:

dec_base = 1_000_000
bin_base = 0b_1111_0000
hex_base = 0x_1234_abcd
print(dec_base) # 1000000
print(bin_base) # 240
print(hex_base) # 305441741

四. Share

一个感人至深的故事 。 写 ARTS 的时候看到了大佬 纯洁的微笑推得一篇文章。最近疫苗、me too 等各种事件,看篇感人的文章暖心一下吧。当然看的时候也心生一个好奇,整个社会都在谴责人贩子,可背后的买主我们貌似没怎么关心过。他们又是怎样的一群人呢。

发布了46 篇原创文章 · 获赞 21 · 访问量 9万+

猜你喜欢

转载自blog.csdn.net/Ahri_J/article/details/105398078
今日推荐