Algorithm
本周选择的算法题是:Trapping Rain Water
规则如下:
Given n non-negative integers representing an elevation map where the width of each bar is 1, compute how much water it is able to trap after raining.
The above elevation map is represented by array [0,1,0,2,1,0,1,3,2,1,2,1]. In this case, 6 units of rain water (blue section) are being trapped. Thanks Marcos for contributing this image!
Example:
Input: [0,1,0,2,1,0,1,3,2,1,2,1]
Output: 6
Solution
我实现的方案:
Runtime:48 ms,快过 85.26%。
Memory:13.4 MB,低于 88.37%。
class Solution:
def trap(self, height: List[int]) -> int:
total, traps, left, right = 0, set(), 0, len(height)-1
for index in range(len(height)-1,-1,-1):
if height[index] >= height[right]:
if right - index > 1:
traps.add((index, right))
right = index
for index in range(len(height)):
if height[index] >= height[left]:
if index - left > 1:
traps.add((left, index))
left = index
for left, right in traps:
total += min(height[left], height[right]) * (right - left - 1)
for index in range(left + 1, right):
total -= height[index]
return total
会从左、从右扫描两次得到各个区间,然后计算总的水量。
时间复杂度和空间复杂度都有可优化的空间,同样的双指针,经过优化成一次循环后:
class Solution:
def trap(self, height: List[int]) -> int:
total, left, right = 0, 0, len(height) - 1
max_left, max_right = 0, 0
while left < right:
if height[left] < height[right]:
if height[left] >= max_left:
max_left = height[left]
else:
total += max_left - height[left]
left += 1
else:
if height[right] >= max_right:
max_right = height[right]
else:
total += max_right - height[right]
right -= 1
return total
Runtime:48 ms,快过 85.26%。
Memory:13.4 MB,低于 97.67%。
只循环一次,且空间复杂度为\(O({1})\)。
Review
任何技术的学习要问自己两个问题:
- 为什么要有这个技术?
- 这个技术解决了什么问题?或者为什么是用这个技术来解决?
这两个问题是价值观的统一,后文都是基于此来进行阐述。
首先登场的是 React.createClass
和 React.Component
。
React.createClass VS React.Component
createClass 作为早期的 API,在 JS 还不支持 class 关键字时提供了对 class 的支持。后来 ES6 有了 class 关键字,React 团队决定拥抱它,于是在 v0.13.0 带来了 React.Component API。
React 与 JS 标准进行了对齐,是很好的做法。不过 React 仍然还有一些问题:
- constructor - 为了初始化组件状态,需要在 constructor 里对 this.state 进行赋值,同时要记得调用 super;还要将实例方法绑定到组件的实例上
这并不是什么太大的问题,状态的初始化可以通过 Class Fields 来解决;把实例方法以箭头函数的方式实现,也避免了绑定的问题。
一些更深层次的问题还没有解决:为了管理复杂度,React 期望将应用程序分割成不同的组件,同时它们还能组合起来,这种组件模型让 React 很优雅。然而这个模型的实现方式却似乎有些问题。
重复的逻辑
React 的组件与 React 的生命周期耦合在了一起,这让一些逻辑不得不分散在组件各处,比如说你需要在 componentDidMount
时机做一些业务初始化,在 componentDidUpdate
时机做刷新,还有一些真正的方法用来承载业务。它们本质上都是一件事:保持状态的同步。
共享业务逻辑
React 本质上是一个 UI 库,考虑到 UI 上存在的各种组件之间的组合,我们也需要将一些非 UI 逻辑的代码做成独立的组件,这样便能在各种 UI 场景下共享相同的业务逻辑。
我们可以将这样的业务代码与状态一起封装到一个业务组件里,然后需要渲染时将状态传递给实际渲染的组件,这样便是一个高阶组件:
function withRepos (Component) {
return class WithRepos extends React.Component {
state = {
repos: [],
loading: true
}
componentDidMount () {
this.updateRepos(this.props.id)
}
componentDidUpdate (prevProps) {
if (prevProps.id !== this.props.id) {
this.updateRepos(this.props.id)
}
}
updateRepos = (id) => {
this.setState({ loading: true })
fetchRepos(id)
.then((repos) => this.setState({
repos,
loading: false
}))
}
render () {
return (
<Component
{...this.props}
{...this.state}
/>
)
}
}
}
这种方法也有一些问题:
- 逻辑上有些奇怪
- 如果你有多个高阶组件,想象一下,你需要多层嵌套
当你需要新的状态时,不得不调整组件的结构与嵌套关系,所谓的“嵌套地狱”,看起来就像这样:
<WithHover>
<WithTheme hovering={false}>
<WithAuth hovering={false} theme='dark'>
<WithRepos hovering={false} theme='dark' authed={true}>
<Profile
id='JavaScript'
loading={true}
repos={[]}
authed={true}
theme='dark'
hovering={false}
/>
</WithRepos>
</WithAuth>
<WithTheme>
</WithHover>
回顾一下
- 调用 super 很烦人
- 方法绑定很烦人
- 组件内的逻辑分散
- 对业务逻辑的重用没有很好的解决方案
简而言之,如果有一套 API 能同时拥有简单、可组合、灵活和可拓展就太好了。
这正是 React Hooks 诞生的背景。
直接说结论:
React Hooks 让函数式组件了有状态,React.Component API 的那些小问题也随之而去:
const [ loading, setLoading ] = React.useState(true) // 👌
忘掉组件的生命周期吧,我们需要的仅仅是将状态同步:
function ReposGrid ({ id }) { const [ repos, setRepos ] = React.useState([]) const [ loading, setLoading ] = React.useState(true) React.useEffect(() => { setLoading(true) fetchRepos(id) .then((repos) => { setRepos(repos) setLoading(false) }) }, [id]) if (loading === true) { return <Loading /> } return ( <ul> {repos.map(({ name, handle, stars, url }) => ( <li key={name}> <ul> <li><a href={url}>{name}</a></li> <li>@{handle}</li> <li>{stars} stars</li> </ul> </li> ))} </ul> ) }
通过实现自定义的 hook 将业务逻辑封装起来,就像这样:
function useRepos (id) { const [ repos, setRepos ] = React.useState([]) const [ loading, setLoading ] = React.useState(true) React.useEffect(() => { setLoading(true) fetchRepos(id) .then((repos) => { setRepos(repos) setLoading(false) }) }, [id]) return [ loading, repos ] }
与 UI 解耦后,需要使用
useRepos
的 UI 组合在使用时只需要:function ReposGrid ({ id }) { const [ loading, repos ] = useRepos(id) if (loading === true) { return <Loading /> } return ( <ul> {repos.map(({ name, handle, stars, url }) => ( <li key={name}> <ul> <li><a href={url}>{name}</a></li> <li>@{handle}</li> <li>{stars} stars</li> </ul> </li> ))} </ul> ) }
感谢作者清晰的思路以及一看就懂的示例!
Tip
Python 中寻找列表中最多的值:
test = [1,2,3,4,2,2,3,1,4,4,4]
print(max(set(test), key=test.count))
#-> 4
Python 中设置递归的最大深度限制:
import sys
x=1001
print(sys.getrecursionlimit())
sys.setrecursionlimit(x)
print(sys.getrecursionlimit())
#1-> 1000
#2-> 1001
Share
高效沟通:沟通方式及技巧
好的沟通方式,三种:
- 尊重 - 两个重要的基本原则:
- 我可以不同意你,但是会捍卫你说话的权利
- 赢得对方的尊重需要先尊重对方
- 倾听 - 学会倾听,就是掌握更多的信息,避免信息不对称,信息不对称会作出错误的假设
- 情绪控制 - 可以控制好自己的情绪就是 EQ 高的体现。具体的:
- 不要过早或者过度打岔和反驳
- 求同共异,冷静客观 - 言语造成的伤害有时难以估计
沟通技巧:
- 引起对方的兴趣 - 了解对方的背景和兴趣点
- 直达主题,强化观点 - 确定自己的目标,学会抓重点,提炼出简明扼要的“金句”,能击中对方的软处或者引起对方的深度思考
- 基于数据和事实 - 通过数据和证据,通过权威的引用和证词,使别人无条件相信你的话
高效沟通:沟通技术
沟通技术分享:
- 逻辑 - 逻辑是武器
- 信息 - 避免 X/Y 问题,找到问题的源头,质疑或者改良它
- 维度 - 人与人之间如果要找共同,就到大局上找;找不同,就到细节上找。能够站在更高的维度来沟通是我们需要努力的目标
- 共同 - 首先是共情,跟对方互相分享各自的情感,拉近距离,然后相互共享自己的观点,在观点中寻求双方共同的利益点,然后不断地循环,一点一点地达成共识
高效沟通:好老板要善于提问
本篇意在如何和员工(下属)沟通,自身作为员工也可以反向思考下。
引导
永远不要给员工答案,要让员工给你答案,而且不要只给一个答案,要给多个答案。
从管理者的角度看,用提问的方式,“倒逼”员工找到答案,从而提高员工的参与感和成就感,其实是给员工成长机会,促使他们深入地进行思考。
从员工角度看,自身给出的方案应该:
- 多种执行方案 - 同一个任务,要考虑快点做怎么做,慢点做怎么做
- 避免 all in - 方案要给多个,并且有能力比较它们
倾听
通过倾听更多地了解员工,了解他们的生长环境和背景,可以帮你对每个员工建立更加合理的预期,从而更好地进行任务分配和人员管理。
共情
共情又称为同理心,或者换位思考,它指的是站在对方的立场设身处地思考问题的一种方式。换句话说,在人际交往的过程中,你需要能够体会他人的情绪和想法、理解他人的立场和感受,并站在他人的角度思考和处理问题。
高维
提升自己的格局观,能从全局利益、长远利益思考问题,解决问题。以更高的维度来看待问题,以业务被砍为例,肯定员工过往的努力和成果,同时引发大家对新业务的兴趣,从而更有利于帮助团队后续过渡到新业务方向上。
反馈
反馈机制能确保问题被反应出来并及时得到解决,这是一种正向反馈,能够帮助公司节约大量的时间和精力,对团队来说是种很好的正向鼓励。
建立反馈机制的关键点:
- 及时反馈
- 能够形成正向循环
高效沟通:好好说话的艺术
跟员工沟通的几个难题
一对一会议
好的一对一会议是以员工为中心而不是管理者;管理者需要做的倾听,而非教育。
会议重点:
- 工作状态 - 状态会变化,需要了解员工最近的状态
- 个人发展 - 为员工创造更有挑战的工作,让员工更好地成长
- 公司组织 - 了解员工是否认可公司的目标和方向
- Leader 自己 - 请员工给自己一些建议,谈谈他认为怎样做会比较好,对管理员来说是个很好的反思和学习的过程
绩效沟通
沟通一定要放在平时,不要搞成像秋后算账一样,因为你是管理者,不是地主监工。
要注意的是,反馈的过程中,不是以指责员工为主,而是在帮助员工,一定要有帮扶的态度,这样员工会更容易接受。
特立独行的员工
- 给他找到匹配的人,要么是比他牛的人,要么是和他旗鼓相当的人
- 给他一些独立的工作,把他隔离出去,让他做一些相对独立和有挑战的事情
有个原则:当你在一个人身上花的精力和时间成本,大于你到外面找一个更好的人或者能力相当的人来替代他的时候,你就要坚决地把他替换掉。
挽留离职员工
- 要明确知道员工离职的原因
- 让员工放下戒心,充分表达他的意见和想法
- 管理者以倾听为主
- 要有“生意不行,友情在”的意识
劝退员工
不要秋后算账,沟通应该放在平时。
要给对方制定一个目标,一个时间期限,表达出“我是愿意帮助你的,我也给你机会”的意思。
任何人都应该有可以纠正错误的机会,公司应该给员工这样的机会,员工也应该给公司同样的机会。
跟客户沟通的几个难题
吸引客户的兴趣
做足功课,确保了解客户的关注点,以及当下的痛点
帮客户发现问题
- 结合客户的痛点,了解客户做过的尝试
- 深入细节,了解细节才会有更准确的信息
- 小心 X/Y 问题,找到 X 问题 - 真实问题才是 X 问题,手段都是 Y 问题
管理客户的期望
既让客户满意,又不会让团队作茧自缚的手段:
- 要至少给出三套方案,低成本、高成本的玩法和性价比比较高的玩法,引导客户的选择
- 要一些相关的案例和参照物来对比上述方案,以此教育客户不同的方案代表着不同的期望和不同的结果
永远不要跟客户说不,要有条件地说是,告诉客户不同的期望要有不同的付出和不同的成本。不要帮客户做决定,而是给客户提供尽可能多的选项,让客户自己来做决定。
跟老板沟通的几个难题
了解你的老板
倾听、了解老板
赢得老板的信任
提高自己的议价能力
管理老板的期望
确认老板的的期望,及时反馈和讨论
非暴力“怼”老板
用沉默让老板感到不安,他一定会安慰你,借此跟老板谈条件