Python技术迷

老公税后收入从月薪20K+,骤降到月薪6K。想问下男同胞们,我现在该怎么做能让他好受一些?

刚看到个贴子,说博主老公收入从月薪2w多掉到6k,她自己税后13k,还有个去发达国家的机会,还带着刚出生的宝宝,就问怎么做能让老公好受点。

Image

我的看法是:先别急着选国家,先选立场——你们俩是一队的。

男人收入骤降,自尊肯定受冲击,这时候最怕听到的是比较:“你看我赚得都比你多。” 最需要的是一句:“这段时间先我多扛一会儿,我们一起想办法。” 多肯定他的价值,不只看工资,多聊未来规划,让他参与决策,而不是被你“安排出局”。

至于出国,别变成谁为谁牺牲,而是算一笔“全家总收益”的账:发展前景、育儿环境、感情成本,都摊开说清楚。

选什么都是冒险,但只要是两个人商量后一起扛的决定,就不会是谁欠谁。

算法题:Z 字形变换

昨天晚上十一点多,我在沙发上划水,手机刷到我们组小李在群里问一句: “哥,那个 Z 字形变换到底咋实现啊,用 Python 写,总感觉哪哪都绕。”

我一看这题名字挺吓人,其实真不难,就是有点抽象。咱今天就边唠嗑边把这个题捋顺了。

题目意思很简单:给你一个字符串 s,还有一个行数 numRows,让你把这个字符串按 “Z 字形” 写下来,然后再一行一行读出来。

举个官方那种经典例子(别嫌丑哈):

s = "PAYPALISHIRING", numRows = 3

排成 Z 字形长这样:

P   A   H   N
A P L S I I G
Y   I   R

一行一行读: "PAHNAPLSIIGYIR"

所以本质上就是:模拟写字 → 再按行读,就完事了。 听起来还挺像小学抄写作业对吧。

脑子里先有个画面,不要一上来就敲代码

你可以想象有几行纸条:

  • 有 numRows 行
  • 你手里拿着一个笔,从上往下写,写到最底下一行了,就折回来往上写
  • 写到最上面,又往下写

就这么上下上下地来回走,把字符串一个字符一个字符写下去。

比如 numRows = 4 的时候,你写出来会像这样:

s = "0123456789ABCDEF", numRows = 4

0     6     C
1   5 7   B D
2 4   8 A   E
3     9     F

你会发现笔的“当前行号”其实就这样走:

0,1,2,3,2,1,0,1,2,3,2,1...

是不是有点像一个小人,在几行之间上下爬楼梯。

所以代码核心思路其实就俩变量

别管什么高深公式,最省心的写法就是这一套:

  1. 准备一个列表 rows,长度是 numRows,每一项都是当前行的字符串。

  2. 准备一个变量 cur_row 表示“我现在写到第几行了”。

  3. 准备一个变量 step,要么是 1(往下走),要么是 -1(往上走)。

  4. 遍历字符串里的每个字符:

  • 把它加到 rows[cur_row] 这一行里
  • 如果到了最顶行(0)或者最底行(numRows-1),就把 step 反向
  • 然后 cur_row += step

最后把所有行拼起来就行了。

有一个小坑别忘了: 如果 numRows == 1,根本不存在上下走,直接返回原字符串,否则会 bug。

直接上代码,你可以丢到 LeetCode 那题(Zigzag Conversion / Z 字形变换)里跑:

defconvert(s: str, numRows: int) -> str:
# 特殊情况:只有一行,根本没法“Z 字形”,原样返回
if numRows == 1or numRows >= len(s):
return s

# 每一行先用空字符串占位
    rows = [""] * numRows

    cur_row = 0# 当前在哪一行
    step = 1# 1 表示往下走,-1 表示往上走

for ch in s:
# 把当前字符写到当前行
        rows[cur_row] += ch

# 到顶或者到底,掉头
if cur_row == 0:
            step = 1
elif cur_row == numRows - 1:
            step = -1

# 往下一行(或者上一行)走
        cur_row += step

# 最后按行拼接就是答案
return"".join(rows)

你可以自己拿个简单点的例子跑一跑,比如:

print(convert("LEETCODE", 3))

自己在纸上画一下三行 Z 形的样子,再跟打印结果对比,一般印象就牢了。

这题复杂度挺“朴素”的:

  • 每个字符只遍历一遍 → 时间复杂度 O(n)
  • rows 里总共存的字符也是 n 个 → 空间复杂度 O(n)

n 就是字符串长度。

在算法题里,这已经是挺完美的复杂度了,没啥可优化的,顶多就是换种写法,比如不用字符串累加、用列表再 join,性能会再好一点点:

defconvert_fast(s: str, numRows: int) -> str:
if numRows == 1or numRows >= len(s):
return s

    rows = [[] for _ in range(numRows)]
    cur_row = 0
    step = 1

for ch in s:
        rows[cur_row].append(ch)

if cur_row == 0:
            step = 1
elif cur_row == numRows - 1:
            step = -1

        cur_row += step

# 把每行的字符列表拼成字符串,再整体拼起来
return"".join("".join(row) for row in rows)

Python 里字符串是不可变的,+= 会不停创建新对象,字符串一长差距就出来了,用列表再拼更稳。

有几个点,你在面试的时候顺嘴能说出来,面试官一般会点点头那种:

  1. 边界条件

  • numRows == 1:直接返回 s,因为根本不会形成 Z 字形
  • numRows >= len(s):每个字符最多一行一列,也是直接 return s
  • 思路不是“画 Z”,而是“按行收集”很多人一开始会想:“我是不是要开一个二维矩阵,先把 Z 字形画出来,然后再按行读?” 这样也能做,但空间浪费巨大,很多位置是空的。 最好的方式其实就是:只关心每一行最后会有哪些字符,不关心它们列坐标。

  • 方向反转的写法上面我用了 if cur_row == 0: step = 1 ... 这种写法。 你也可以玩一点小技巧,直接:

    if cur_row == 0or cur_row == numRows - 1:
        step = -step

    这样每次到边界就取反,代码会再短一点,看你自己习惯。

  • 反正这题你只要脑子里有那个“人在楼梯上上下走”的画面,再记住“按行收集”,Python 那几行代码就很好背。

    下次要是有同事半夜微信问你这题,你直接把这段代码甩过去,再配一句:“别慌,这题就是模拟走路。”就显得你老稳了。

    -END-

    我为大家打造了一份RPA教程,完全免费:songshuhezi.com/rpa.html

    🔥虎哥私藏精品🔥

    虎哥作为一名老码农,整理了全网最全《python高级架构师资料合集》,总量高达650GB