5B的同学吐槽说,他们HR每天像守株待兔一样盯着监控。有人刚离开工位,系统还没反应,HR已经开始记录离岗次数了。
刚看到个贴子,说的是5B那边的同学吐槽公司管理方式,核心就一个字:盯。HR天天守着监控,人刚起身倒杯水,记录已经记上了。
我觉得这事吧,问题不在技术,在心态。贴子里描述的感觉,已经不像正常办公,更像进了流水线工厂,人在工位上不是干活,是随时等着被抓现行。
网友回帖里不少人也炸了,有人说这是“反向激励”,还有人直接劝快跑,说这种地方仲裁一告一个准。我比较认同后一种,但也得说句实话,公司敢这么搞,说明内部早就不信任员工了。
怎么说呢,管理就像拴风筝,线太松会飞走,线太紧直接断。一天到晚算离岗次数、扣钱罚款,短期可能“纪律好了”,长期只会把人心耗光。大家来上班是交换价值,不是来参加真人版监控秀。
真正靠谱的管理,是让人把事干好,而不是把人盯死。公司想走远,先把格局放大点吧。
面试题:连续空余座位
想象下,周五下班,你在手机上打开选座页面,一排小圆点,有灰有蓝,你跟朋友说“我要三连坐,中间别夹人”,其实这道“连续空余座位”的算法题,说的就是这个事。
一般会有这么几种常见说法,意思差不多:
已知一排座位,用 0 表示空,1 表示有人,比如 [1,0,0,1,0,0,0]问你:能不能给出 k个连续的空位?要的话,把起始位置返回或者再顺带问一句:这一排里,最长连续空位有多长,从哪开始
所以核心就是:在一个 0/1 数组里,找最长的 0 段,或者找有没有长度 ≥ k 的 0 段。
先写个最实用的:能不能安排 k 个连续座
就按电影院选座来想,你从左到右扫一遍,看到空位就计数,遇到有人就清零,谁先数到 k,谁赢,对吧。
用 Python 写出来,很顺手:
from typing import List, Optional, Tuple
deffind_continuous_seats(seats: List[int], k: int) -> Optional[Tuple[int, int]]:
"""
在一排座位中找到第一个可以坐下 k 个人的连续空位区间
:param seats: 0 表示空位,1 表示已占
:param k: 需要连续空位的个数
:return: (start, end) 下标区间,找不到就返回 None
"""
count = 0# 当前已经连续的空位数量
start = 0# 当前这一段连续空位的起始下标
for i, seat in enumerate(seats):
if seat == 0:
# 新的一段空位开始
if count == 0:
start = i
count += 1
# 一旦长度够了,直接返回这一段
if count >= k:
return start, start + k - 1
else:
# 遇到有人,连续被打断,计数清零
count = 0
# 扫完还没找到
returnNone
if __name__ == "__main__":
seats = [1, 0, 0, 1, 0, 0, 0]
k = 3
res = find_continuous_seats(seats, k)
print(res) # 输出:(4, 6),表示 4 到 6 位置可以坐 3 个人
这个思路就一个关键词:一次遍历。
从左往右扫一遍,时间复杂度 O(n) 只用了几个变量,空间复杂度 O(1) 不需要什么花里胡哨的数据结构,面试官问也好讲
再顺带把“最长连续空位”也算出来
现实里你可能还想知道:这一排里最舒服的一段空位有多大,方便给晚来的大部队预留下。
这个跟上面差不多,只不过不是发现 ≥k 就返回,而是全程记录一个“历史最优”:
deflongest_empty_block(seats: List[int]) -> Tuple[int, int, int]:
"""
找到最长的一段连续空位
:param seats: 0 表示空位,1 表示已占
:return: (max_len, start, end)
"""
max_len = 0
max_start = -1
max_end = -1
cur_len = 0
cur_start = 0
for i, seat in enumerate(seats):
if seat == 0:
if cur_len == 0:
cur_start = i
cur_len += 1
if cur_len > max_len:
max_len = cur_len
max_start = cur_start
max_end = i
else:
# 这一段结束了
cur_len = 0
return max_len, max_start, max_end
if __name__ == "__main__":
seats = [1, 0, 0, 1, 0, 0, 0]
print(longest_empty_block(seats)) # (3, 4, 6)
逻辑上就是:
cur_len记录“当前这段空位连续了多长”一旦遇到 1,就说明这段断了, cur_len清零每次 cur_len变大,就拿它和max_len比,比赢了就更新“最佳答案”
所以这个函数不但告诉你最长空位的长度(比如 3 个座),还告诉你是从哪到哪。
如果再实际一点,加个边界校验
日常写业务代码,肯定要多啰嗦两句,防一手奇怪的入参,不然排查起来特别烦:
defsafe_find_continuous_seats(seats: List[int], k: int) -> Optional[Tuple[int, int]]:
if k <= 0:
raise ValueError("k 必须是正整数")
ifnot seats:
returnNone
# 简单防一手非法数字
for x in seats:
if x notin (0, 1):
raise ValueError("座位数组只能包含 0 或 1")
return find_continuous_seats(seats, k)
这种小题,很多时候不是算法本身有多难,更多是看你有没有把“正常情况 + 极端情况”都想明白:
全是 1 怎么办?——函数老老实实返回 None全是 0 怎么办?——最长那段就是整排 k 比座位数还大?——照样返回 Nonek 是 0 或负数?——直接抛异常,别装作正常
顺便说一句滑动窗口
有的小伙伴一看到“连续”“区间”这种词,就会想到滑动窗口,其实上面的写法,本质就是最简单那种“指针往前走 + 计数”的滑动窗口,只是我们没特意给两个指针起名字而已。
如果真的想写成两个指针的味道,也可以这样:
deffind_continuous_seats_window(seats: List[int], k: int) -> Optional[Tuple[int, int]]:
left = 0
zero_count = 0# 当前窗口里的空位数
for right, seat in enumerate(seats):
if seat == 0:
zero_count += 1
else:
# 窗口里混进去了 1,就把 left 挪到 right+1
left = right + 1
zero_count = 0
if zero_count >= k:
return right - k + 1, right
returnNone
味道差不多,只是你脑子里更容易把它和“滑动窗口这个套路”对应起来,后面刷别的题也能用同一套思路。
差不多就这样,这个题一般就是这么一遍扫过去的事儿,多写两遍手就熟了,面试碰到也不紧张。
-END-
我为大家打造了一份RPA教程,完全免费:songshuhezi.com/rpa.html
虎哥作为一名老码农,整理了全网最全《python高级架构师资料合集》,总量高达650GB