做了一到比较简单的题目,回文数的判断,用 Python 代码实现如下:
方式 1,将数字转为字符串后进行反转,若反转前后相等,则是回文数
class Solution:
def isPalindrome(self, x):
"""
:type x: int
:rtype: bool
"""
str_x = str(x)
palindrome_x = str_x[::-1]
return str_x == palindrome_x
上面方式需要新建一个回文字符串,如果字符串较大的话会占用较多的空间,可以直接在原有字符串上同时进行前后遍历比较,实现如下:
class Solution:
def isPalindrome(self, x):
"""
:type x: int
:rtype: bool
"""
str_x = str(x)
length = len(str_x)
index = int(length / 2)
for i in range(index):
if not str_x[i] == str_x[length - 1]:
return False
length -= 1
return True
本周 Review 读了耗子叔练级攻略中推荐的一篇数据库相关的文章: SQL vs. NoSQL Databases: What’s the Difference?。
文章主要比较 SQL 数据库(关系型数据库) 和 NoSQL 数据库各自的特点以及主要的数据库软件,并给出了数据库选择的一些建议。简要整理如下:
关系型数据库数据存储有表和列组成,在数据存储前必须定义表中数据的基本信息以及表字段之间的关系,即 schema。Schema 的定义可以保证数据的准确性,但一定程度上也会影响扩展的灵活性。比如某一个字段要存储美国的手机号码,是长度为 10,一开始我们加上长度为 10 的限制可以避免不符合条件的数据存入,但是如果哪天我们要求改了,不仅仅存储美国的,还要存储世界各地的手机号,那么就需要修改表,这是非常重的操作。
关系型数据库通过 SQL 结构化查询语句实现数据的增删改查,同时一个非常有用的功能就是 join 查询,可以通过一条语句查询出几张表中的数据。
另外关系型数据库的 schema 结构非常符合现实世界中很多业务场景的逻辑关系,配合 Java 等面向对象语言,非常适合大部分的软件开发。
何时使用关系型数据库
-
需要保证数据的 ACID。当对数据的一致性、隔离性等有严格要求时,就需要使用关系型数据库,对数据操作进行较为严格的约束。
-
数据结构较为稳定,且暂时没用大规模增长的情况下可以考虑使用关系型数据库
NoSQL 数据库没有 SQL 数据库那样严格也明确的数据结构限定,因为数据的存储可以非常的灵活。可以将 NoSQL 数据库看做是一个文件夹,如同电脑中的文件夹,我们可以存放图片、文本、音乐、视频等一个个文件数据,NoSQL 数据库也可以将数据看做一个个的文档进行存储。 但是 NoSQL 数据库往往占用的空间比 SQL 数据库要大,并且对事务操作支持并不好。
NoSQL 数据库分类
- 键值数据库: 最简单的 NoSQL 数据库,数据由键值对组成,比如 Cassandra, Azure, LevelDB、 Riak 等
- 文档数据库: 可以存放文档数据,其数据格式为 Json/Bson/XML 等,比如 MongoDB 数据库
- 列数据库: 将数据存储为列而不是行,具有很高的扩展性,比如 HBase、BigTable 等
- 图数据库: 存储相互关联并且可以用图来表示的数据,比如 Polyglot, Neo4J。
选择 NoSQL 的一些建议
- 需要存储大量的非结构化数据时
- 需要充分的利用到云计算和云存储时可以考虑采用 NoSQL 数据库。
以上就是文章的简要总结,关于 NoSQL 耗子叔在专栏中推荐了一本 NoSQL 精粹,非常值得一读。
分享几个在 Go101 中读到的几个关于 Go 的编程技巧。
const MaxUint = ^uint(0)
const MaxInt = int(^uint(0) >> 1)
在其他语言中可以快速的实现 0 到 N 的遍历,比如 Python:
In [2]: for i in range(5):
...: print(i)
...:
Go 语言中可通过如下方式快速实现同样的功能:
ackage main
import "fmt"
func main() {
const N = 5
for i := range [N]struct{}{} {
fmt.Println(i)
}
for i := range [N][0]int{} {
fmt.Println(i)
}
for i := range (*[N]int)(nil) {
fmt.Println(i)
}
}
Go 的 wiki 中提供了如下两种方式:
// 方式 1
b = make([]T, len(a))
copy(b, a)
// 方式 2
b = append([]T(nil), a...)
但是以上两种方式都有问题:
- 方式1: 即使 a 是 nil, b 也会创建一个新的 slice
- 方式2: 如果 a 是一个非 nil 的空切片,那么 b 是为 nil
可以通过下面方式解决上面的两个维内托:
if a == nil {
b = nil
} else {
b = make([]T, len(a))
copy(b, a)
}
相当于提前做了一次判断,但代码并不优雅,可以简化如下:
b = append(a[:0:0], a...)
这样保证如果 a 不是 nil 则 b 也不是 nil,a 是 nil 的话 b 也是 nil。
读了耗子叔新发表的问题 K8S 的问题排查文章,简要写几点看之后的感想:
-
对于技术问题,高效的问题定位与解决取决于知识的广度与深度。作为程序员拥有扎实的基础知识永远是最硬核的技能。 耗子叔在文中提高的很多命令行、网络相关的知识、systemd 相关的知识,都是实打实的基础知识,这些都是需要下功夫学习实践之后才能积累下来的。
-
对于知识的掌握要结合场景,明白哪个知识点对应怎样的场景,这样就能在实践中充分发挥威力,针对每个故障找到可能的对应的问题点和检验方式。
-
接上一条,当明白了知识点对应的场景时,就能按部就班的排查问题了,使用排除法一步步的探查问题,如果一遍没有查出,就退回去重新梳理所有细节再次排查。
举一个自己的反面例子吧,最近公司自己出了一门能够进行人脸识别的智能门禁,用户上传照片后服务端拿到算法生成的人脸 ID 需要同步给各个门禁端。但有一个门禁无论怎样就是同步不过去。当时和客户端同事没有细致的思考原因所在,一步步的排查问题,大部分都是在用蛮力在一次次的重试。正确的做法应该如下:
- 门禁设备是通过长连接连到服务端的,接收服务端数据 push。因此最有可能的原因是 push 服务不可用
- 检查其他门禁,是没有问题的,说明 push 服务可用,问题出在这台门禁上。(当时就是卡在这里,没有进一步思考连不上的原因)
- 拿到服务监控 API 发现该门禁对应的 channel 没有连接,说明门禁没有连上长连接 - 门禁的连接是通过和服务端约定好的通过 md5 算法生成的 channel_id 连接的,按理来说只要 channel_id 一直不应该连接不上
- 经比对后发现,客户端用 Java 开发,当 md5 开头一位是 0 时会省略掉,由此导致客户端生成的 channel_id 和服务端不一致,因此导致连接不上,因为并不必现,因此之前没有发生这个问题。
回头来看只是是一个挺简单的问题排查过程,但是确实当时没有动脑子快速解决,需要深刻反省。另外就是想到了上司说的一句话: 软件开发没有玄学,问题都是能找到原因的。