Fannngxun写字的地方

老和山下的书桌


  • 首页

  • 关于

  • 标签

  • 分类

  • 归档

  • 搜索

如何从「赚一笔是一笔」到拥有「可持续的未来财富」?

发表于 2021-08-25 | 分类于 财富
字数统计: 1.2k | 阅读时长 ≈ 4

首先是赚钱意识上的革新,就像李笑来在《通往财富自由之路》中写的那样: 每个人赚钱的方式就是每个人的商业模式,商业模式决定了个人财富的上限。

与书里稍有不同,我把个人商业模式重新划分为四类:

  • 一份时间售卖一次

大部分的打工人都属于这种模式,签订了劳动合同就相当于把未来的劳动时间出卖给了公司,公司对你的时间只支付一次劳动报酬,在全球大放水的背景下,这种商业模式的利润也是被稀释的最厉害的,很难实现有效的资本积累。

  • 一份时间售卖多次

作家、自媒体从业者、网课等都属于这类,通过出书,广告分成,会员费的方式,让自己的一份劳动能够售卖多次。这种方式的难点在于需要长时间的积累,必须要要在前期积累起足够的口碑才能得到市场的认可,在此之前,微薄的收入往往无法支撑日常生活的开支。如果说第一种模式是线性函数,那么第二种模式则是指数函数。

  • 不再出售自己的时间,或者说是剥削自身,甚至可以从外部获取收入

理财投资类,创业,分销拿推广人头费等都属于这类。理财者付出时间精力学习选股、择时、研究策略,投入资金雇佣专业人士操作资产进行增值,创业者利用自身的知识、能力、人脉创办企业,雇佣劳动者,从而获取超额利润;推广者通过扩大产品的受众从而从受众处获取报酬,本身并不生产价值。

  • 时间精力投入很少,躺赚

08 15年后买房、挖矿都属于此列。唯一的区别是,前者门槛较高,上牌桌的筹码要以百w起步,后者门槛较低,几w的台式机就可以参与。但相同的是,这两者都几乎不需要额外付出精力就能产生利润。

需要指出的是,这四种模式的赚钱绝对值未必是1<2<3<4, , 比如,如果从事的是金融或互联网行业,那么作为打工者的收入很有可能超过普通的自媒体从业者,甚至一般的实体店利润也就几十w,远不及头部企业的中层。这种排序的标准更关注投入产出比,或者是个人自由度,这种意义下,打工的性价比是最低的,因为你大部分时间都被锁死在别人交给你的任务上,很大概率失去了建立自己事业的可能。从资质要求看,几乎所有人能够做到1,普通人垫垫脚能够做到2,3需要天赋和汗水,非人中龙凤无法做出名堂,而4则更看历史的进程和机遇,属于在电梯里做俯卧撑。

因此,为什么说师医公yyds,这三类职业只要做好自己的本职工作,口碑和人脉是水到渠成,在不违反规定的情况下,变现的方法也比比皆是,如老师出去讲座、公务员写作、医生去公司当顾问等等,相当于本职工作就包含了 1,2两种模式的潜力。

头脑中有了赚钱模式的分别,下一步就是执行,建立起合适的职业规划是最重要的一步。

大部分人都没有原始积累,因此第1步是不可或缺的,生存是基础。但如果不想靠打工挣钱一辈子,就尽量避免那些需要996的公司,超大强度的工作会让人失去思考前途的时间和精力,透支未来的潜力,如果想要过渡到模式2,就尽量选执行8小时工作制的、能够保证人文关怀的企事业单位和外企,这样在正常的工作之外,还能拥有足够的闲暇时间发展自己的副业。主业用来保证现金流,副业用来增值,主业就相当于正态分布的均值,控制着你的平均生活水平,副业则为方差,让你有机会搏一搏,单车变摩托。大刘和当年明月已经为我们探索了一条可行的路径。

模式3和4如前所述,可遇不可求,命里有时终须有,命里无时莫强求。

Python之word文档批量处理

发表于 2021-05-27 | 分类于 办公技巧
字数统计: 1.2k | 阅读时长 ≈ 4

在文字工作中,经常会碰到与需要批量生成、编辑word文档的需求,这类机械性的工作非常适合编写小工具进行批量处理,使得原本人工处理需要几小时的工作量缩短为几分钟。

工具介绍

Python下处理word文档最好用的库为 python-docx, 目前已收入pypi,直接使用pip安装即可。

新建文档

可以新建一个空白文档 Document ,如下:

1
2
3
from docx import Document

document = Document()

编辑现有文档

将现存文件的路径传给Document类即可。

1
2
3
4
from docx import Document

document = Document('myfile.docx')
document.save('new-file-name.docx')

新建标题

新建一个标题, 其中level参数可选,有1-9中规格,代表标题级别。

1
document.add_heading('新建标题', level=1)

新建段落

创建段落 paragraph 的操作如下:

1
paragraph = document.add_paragraph('新建段落。')

设置段落样式

1
2
paragraph = document.add_paragraph('这是一个样式为 ListBullet 的段落')
paragraph.style = 'List Bullet'

设置段落对齐方式

段落对齐方式有 左对齐 、 文字居中 、 右对齐 、 两端对齐,更多对齐方式请参考 WD_ALIGN_PARAGRAPH

1
2
3
4
5
6
7
8
9
10
from docx.enum.text import WD_ALIGN_PARAGRAPH

# LEFT => 左对齐
# CENTER => 文字居中
# RIGHT => 右对齐
# JUSTIFY => 文本两端对齐

paragraph = document.add_paragraph("对其方式测试")
paragraph_format = paragraph.paragraph_format
paragraph_format.alignment = WD_ALIGN_PARAGRAPH.CENTER

设置段落缩进

设置段落缩进,可为负值:

1
2
3
4
from docx.shared import Inches

paragraph = document.add_paragraph("这是缩进")
paragraph.paragraph_format.left_indent = Inches(0.5)

也可以设置首行缩进:

1
paragraph.paragraph_format.first_line_indent = Inches(0.5)

设置段落行距

当行距为 最小值 和 固定值 时,设置值单位为 磅 ,需要用 Pt ;当行距为 多倍行距 时,设置值为数值,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
from docx.enum.text import WD_LINE_SPACING

#SINGLE => 单倍行距(默认)
#ONE_POINT_FIVE => 1.5倍行距
#DOUBLE2 => 倍行距
#AT_LEAST => 最小值
#EXACTLY => 固定值
#MULTIPLE => 多倍行距

paragraph.line_spacing_rule = WD_LINE_SPACING.EXACTLY #固定值
paragraph.paragraph_format.line_spacing = Pt(18) # 固定值18磅
paragraph.line_spacing_rule = WD_LINE_SPACING.MULTIPLE #多倍行距
paragraph.paragraph_format.line_spacing = 1.75 # 1.75倍行间距

片段设置(Run)

段落包含很多块级的格式,比如缩进、行高、制表符等。每一个小片段叫做一个 run ,可以对 run 设置粗体和斜体等属性。

1
2
3
4
5
6
paragraph = document.add_paragraph()
paragraph.add_run('这是一个带有')
paragraph.add_run('粗体').bold = True
paragraph.add_run('和')
paragraph.add_run('斜体').italic = True
paragraph.add_run('的段落。')

更多参考资料

中文:https://zhuanlan.zhihu.com/p/61340025

官方文档 :https://python-docx.readthedocs.io/en/latest/index.html

案例介绍

假设我们需要生成如下的代办xx业务的委托书,客户名字和身份证号来源于其他文件(可以是txt,excel),如果手动进行复制粘贴,费时费力,不如用上一节介绍的工具进行批量处理。

下面示例代码用于生成上图中的模板文档,中文可以自行替换; 函数接收姓名、身份证号组成的列表作为 参数(另有代码进行处理,从其他 文件获得),如[('小明', 330201xxxxxxxxxxxxx1410), ..., ('小红', 330205xxxxxxxxxxxxx1701)]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
def generate_doc(namelist):

# 授权正文
content = '兹委托 xxx (身份证件号:xxxxxxxxxxxxx)' \
'到xx机关办理xx业务事项。委托人和代理人严格遵守有关xx法律法规。'



# 以下为生成保密协议的代码,name_list中的每个人员都将生成对应一份保密协议文档
for names in name_list:
# 创建内存中的word文档对象
file = docx.Document()
file.styles['Normal'].font.name = u'宋体'
file.styles['Normal']._element.rPr.rFonts.set(qn('w:eastAsia'), u'宋体') # 可换成word里面任意字体



# 写入若干段落
p1 = file.add_paragraph()
p1.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.CENTER # 段落文字居中设置
run = p1.add_run("办理xx事项授权委托书")
run.font.size = docx.shared.Pt(20)
run.bold = True

# 抬头
head = file.add_paragraph()
head.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.LEFT
run = head.add_run("\n\nxx市xx区xx局:\n")
run.font.size = docx.shared.Pt(14) # 四号字体
run.bold = True

# 个人信息
p2 = file.add_paragraph()
# p2.line_spacing_rule = WD_LINE_SPACING.ONE_POINT_FIVE
p2.paragraph_format.line_spacing_rule = WD_LINE_SPACING.DOUBLE
p2.paragraph_format.first_line_indent = Inches(0.5)
p2.add_run('本人,').font.size = docx.shared.Pt(14)
run = p2.add_run('%s' % names[0])
run.font.size = docx.shared.Pt(14)

p2.add_run('(身份证件号:').font.size = docx.shared.Pt(14)
run = p2.add_run('%s)' % names[1])
run.font.size = docx.shared.Pt(14)

p2.add_run(content + '\n').font.size = docx.shared.Pt(14)

p3 = file.add_paragraph()
run = p3.add_run('委托人签名: 代理人签名:\n')
run.font.size = docx.shared.Pt(14)
run.bold = True

p5 = file.add_paragraph()
p5.paragraph_format.alignment = WD_ALIGN_PARAGRAPH.RIGHT

run = p5.add_run("2021 年 5 月")
run.font.size = docx.shared.Pt(14)
run.bold = True

# 保存
file.save("./result/办理xx事项授权委托书-%s.docx" % names[0])

如需完整代码,可以关注公众号”CodingSirTutor”, 回复“word批量处理”获取;如需定制文件模板或有特殊需求,可添加wx助手:caxiaozhushou咨询。

Python之数据分析学习路径

发表于 2021-04-25 | 分类于 数据分析
字数统计: 1.6k | 阅读时长 ≈ 5

应浙江大学心理系研博会邀请,笔者于24日晚做了一场关于利用Python进行数据分析的讲座,现将分享的材料稍作整理,希望能帮助更多的在科研等活动中需要此类工具的同学。

工具链与流程

从全景视角来看数据分析,我们需要关心的只有两个部分:1. 有哪些工具可以使用?2. 整体的流程是怎么样的?

一般地,数据分析需要的工具可以分为几类:

  • 解释器:python3.5 + , python2已经停止维护了,所以不推荐, 官网链接。
  • 环境管理工具: Anaconda 已经包含大部分数据科学相关的三方库,无需再手动安装,官网链接。
  • 编辑器: Jupyter Notebook, 可以将公式、分析、代码、结果等结合在一个文档中,非常方便梳理思路和展示成果,支持部署在本地或远程服务器中,通过浏览器访问,更加轻便, 官网链接。
  • 第三方库:
    • NumPy:提供了许多高级的数值编程工具,如:矩阵数据类型、矢量处理,以及精密的运算库,官方文档。
    • Pandas: 基于NumPy的一种工具,为了解决数据分析任务而创建,可以处理不规整的数据。Pandas 纳入了一些标准的数据模型,提供了高效操作百万级别数据集所需的工具。官方文档。
    • SciPy: 一个用于数学、科学、工程领域的常用软件包,可以处理插值、积分、优化、图像处理、常微分方程数值解的求解、信号处理等问题。官方文档
    • Scikit-Learn: 集成了众多的机器学习模型,主要分为六个部分,分类、回归、聚类、数据降维、模型选择、数据预处理。官方文档。
    • statsmodels: 用于拟合多种统计模型,执行统计测试。statsmodels包含更多的“经典”频率学派统计方法,而贝叶斯方法和机器学习模型可在其他库中找到。官方文档。
    • Matplotlib(Seaborn): 2D绘图库,它以各种硬拷贝格式和跨平台的交互式环境生成出版质量级别的图形, 可以通过几行代码生成各种统计图表。Seaborn在Matplotlib基础上构建,能够提供更好的视觉效果。Matplotlib官方文档,Seaborn官方文档。

数据分析的流程可以概括为四个大步骤:

  1. 数据采集
    • 内部数据
    • 开源数据
    • 爬虫爬取
    • 购买数据
  2. 数据预处理: 看似最不起眼的步骤,往往消耗最多的时间。
    • 数据导入
    • 数据检查
    • 数据清洗
    • 数据规约
    • 数据变换
    • 数据聚合
  3. 数据分析(部分方法):重点在于理解理论背后的思想,至于编程工作往往只需调用一下API即可。
    • 假设检验
    • 方差分析
    • 相关分析
    • 回归分析
    • 据类分析
    • 因子分析
    • 时间序列分析
  4. 数据或结果可视化

    • 图表:柱状图、折线图、散点图等等。(详细类型及适用范围见下图,图片来源知乎)

    • 工具:Python、Excel或其他BI工具均可。

学习资料分享

授人以鱼不如授人以渔,这一节分享关于Python基础和数据分析的相关资料,包含视频、博客、书籍等。一般地,对初学者来说,以视频课程学习为先,第一是因为视频可以较为完整的呈现编程的过程,而文字资料不太容易面面俱到,且视频课程往往有讨论区和课后作业,能够给学生提供良性的反馈,事半功倍;当初步入门的时候,视频学习的效率是不如书籍或博客的,文字资料可以快速定位至感兴趣的章节进行重点学习,而视频较为困难;当开始着手项目时,就更适合挑选大而全的资料作为一个字典,有问题时再查询。不同阶段适合使用不同的资料,无需持一种非此即彼的态度。

Python资料

对于没有Python基础的同学来说,那就必须先搞定语言基础,下面列举一些质量有保证的资料:

  • 视频课程,有作业和讨论区,能够及时得到反馈,最适合小白的方式。
    • Coursera: Python for Everybody specialization (University of Michigan, Charles Severance) 注:coursera有些课程需要注册费,但也可以写申请信免费上课,如果确实经济上力有不逮,可以走这个通道。
    • Coursera: Introduction to Scripting in Python Specialization (Rice University)
    • Datacamp: Data Analyst Track
    • 中国大学MOOC Python程序语言设计
  • Python 官方文档
  • 系列博客:
    • 廖雪峰Python教程
    • 菜鸟教程
  • 问题解决(不止适用于python, 编程世界的大部分疑难解决方案都在其中):
    • stackoverflow: 一般的编程问题(bug)都可以找到答案,重点是合理的描述问题。
    • Geeksforgeeks: 更综合一点的博客,更多的是给出一个具体的问题的解决方案。
    • Github repo的issue区:遇到少见的报错,如果前面几种方式都无法解决,可以看看官方库中的issue是否对此有说明,有可能并不是你的问题。
    • math stackexchange: 数学问题解答社区,比较适合数据科学。
    • 搜索引擎: google为上,baidu为下。常备一些常用的搜索引擎技巧,如指定特定网站搜索、指定文件类型搜索等。

数据分析学习资料

其中公开课和书籍是帮助建立起数据分析的完整图景,代码示例可以尝试这修改来验证自己的想法,各类库的文档和cheating sheet主要用来检索合适的API。

  • 公开课

    • Python数据分析与展示
    • 借助 Python 应用数据科学专项课程(看前两个)
  • 书籍:

    • https://github.com/iamseancheney/python_for_data_analysis_2nd_chinese_version
  • 代码(和上书对应):

    • https://github.com/wesm/pydata-book
  • 各类库的官方文档(部分):

    • Pandas: https://www.pypandas/

    • Numpy: https://www.numpy.org/

    • Matplotlib: https://matplotlib.org/

  • Cheating sheets, 下图为Pandas的部分API及使用范例,更多sheet详见以下repo。

    • https://github.com/PokerRambo/Data-Science--Cheat-Sheet

更多编程学习内容可以关注公众号”CodingSirTutor”, 回复“心理系讲座”获取讲座ppt及案例代码文件。

个人应对矿潮的方法

发表于 2021-04-18 | 分类于 经济
字数统计: 6 | 阅读时长 ≈ 1
Here's something encrypted, password is required to continue reading.
阅读全文 »

2021届秋招经验总结

发表于 2021-04-03 | 分类于 求职
字数统计: 5.2k | 阅读时长 ≈ 17

前言

前段时间因忙于搞定毕业答辩、离校手续相关的事情,使得这篇经验贴酝酿过久,几乎过了保质期。趁此清明节之际,将去年求职的经验稍作梳理,以飨读者,希望能对求职路上的小伙伴们有所帮助。

本人是CS专业,故言语站位上难免偏向本专业视角,不过有些应对思路和其他专业未必不存在共性,因此本文主要从两个角度出发:其一是共性的个人管理,主要分享一些时间管理的节奏、资料管理的技巧和人脉管理的思路;其二是我参与秋招的两个行业及相关岗位作为具体的案例穿插在宏观的应对思路中进行叙述,一是互联网,主要投递的是算法岗,基本上目标范围内的公司都拿到了SP offer ; 二是体制内的岗位,这里也可以分为两块,第一块是事业单位的人才引进,第二块是选调生,由于和选调生考试面试以及毕设提交时间段重叠严重,因此人才引进我只参与了一场, 选调生主要参加了浙江选调和中央选调。

正餐篇

本章主要分享在求职准备过程中的一些宏观的思考并结合具体行业做详细说明。

每年招聘季的大致时间是可以确定的,而且这里也存在一个马太效应,如果能拿到好的实习,就能够利用好的实习撬动好的秋招offer; 如果能拿到好的秋招offer,就能argue到更高的package,最后你会发现,拿了一大把优质Offer的总归是那么几个大佬,相信这类“卷法”大家已经在评奖评优的过程体会过了。因此,从找实习开始,时间节奏就非常重要。

时间管理

我的整个求职过程持续了漫长的一整年,这其实是本人过于贪心或者说是对未来还比较迷茫所致,对于规划特别明确或者对特定行业特别热爱的同学来说,大半年时间足矣,无需把自己折腾的太累,将这份时间规划拆分成两半来看即可(互联网 2019.11 - 2020.09, 体制内 2020.06 - 2020.12,此时实习应替换成政府单位)。

下面详细展开一些各阶段的注意事项:

  • 2019年11月 - 2020年01月 刷题:一般研二秋学期末就要开始刷Leetcode了,刷题的时候不需要太在意数量,重要的是要总结题型以及对应使用的数据结构和算法,做到一看到题目就能大概猜到要用什么类型的DS,一般刷完前200 ~ 300经典题就足矣了,同时把剑指offer的题库也刷一遍。这里推荐大家把国内站和海外站结合起来使用,海外站的讨论区质量高,国内站的活动多,刷起来不会枯燥

  • 2020 年 1 月 - 2020 年 2 月末 面试准备:这两个月要开始训练做题的速度和准备面试的内容,

    • 看面经和书:对于算法岗,一般过一遍李航的《统计学习方法》和 hulu的《百面机器学习》(后面新出了一本《百面深度学习》)就可以了,主要还是要结合面经来总结常见基础问题的答案,当时主要是 @常委 总结了一份面经和兄弟们分享了一下,部分目录如下,小伙伴们可以根据自己的需求总结自己的文档:

    • 打周赛:leetcode 每周末都会组织两场周赛,而且在求职季的时候会和公司合作,优胜者能获取奖品和内推码。 周赛主要用来训练思维的敏捷度和提高编码的熟练度,一般在面试前达到半小时内 kill 前三题就可,第四题有时候会出现奇行种,做不出也挺正常。

    • 总结自己的项目:有paper的讲paper,有项目的讲项目,有比赛的讲比赛,不用太多,核心原则就一个——放在简历里的是要和投递岗位最匹配的,要突出项目的亮点和难点以及你在其中扮演的角色,描述过程可以参考著名的STAR法则,同时也可以找个同学来当模拟面试官,互相评估面试过程中的不足。

  • 2020年3月 - 2020年四月 投递实习:这个时间段是几家公司集中招聘实习生的时期,出手要果断,以战代练,不要抱有一种“我准备好了才去投递”的心态,相信我,永远没有一个时刻是完全准备好的;如果心里还是发虚,就找一些小公司先练练手,练完了再把握大公司的提前批,善于反过来利用面试官来完善自己的简历和面试回答。此外,建议建立一个表格来追踪各个公司的进度,这里给一个自己的样例。

  • 2020年5月 选offer: 这个月相对来说会轻松一些,手头也有了一些offer,但是选offer时候还是要三思,不要只看实习工资,还是要结合自己未来的发展城市、小组的前景等等综合考虑,此时多考虑一步就能为秋招省下很多精力。

  • 2020年6月 毕设开题:有些同学会在这个时间段提前去实习,不过因为我后面还要参加体制内的求职,此时去实习必然赶不上3月份的毕业,会对后面的心态造成不利影响,因此这个月我主要做了一些毕设方向的文献调研,基本确定了算法框架和实验思路。

  • 2020年7月 - 2020年9月初 暑期实习及秋招面试:这个时间段我在蚂蚁实习,亲身体会了巅峰的辉煌,和后面的急速坠落相比,真的是世道沧桑。一般这个时候会有一个带你的师兄,不要单纯地完成他给你的任务,要主动地向他请教目前的工作处在业务中的什么位置,与做研究一样,文献综述的目的之一就是找到自己工作在领域big picture中的定位,这么做的目的一是赋予你工作价值感,同时帮助你判断业务在公司中的前景,二是方便你在转正答辩时介绍自己的工作,三则是方便后面包装简历中的项目经历。 一般8月中下旬各个公司都会开始转正答辩,好好把握这个机会,争取在正式秋招开始前就能拿到保底offer。

    • 支线求职:有些公司还会以夏令营的形式来招聘,如招行(包吃包住包交通还有奖金,3天深圳游)、字节跳动(封闭式课程+实践项目)等等。 题外话:最近招行的夏令营开始招聘了,有兴趣私信我帮你内推。
  • 2020年 9月 - 2020年11月初 互联网落幕 体制内登场:8月到9月中旬这段时间里互联网主力的秋招也基本结束,后面大多是谈薪等事务了,此时我的重心也渐渐转向了体制内的工作。9月初陆续有面向应届生的人才引进项目了,去年较早的有宁波、绍兴等地,当时抱着去熟悉一下考试形式的心态未作任何准备就去面试宁波的项目了,一不小心就拿到了offer。总的来说,考察内容比选调生“水”一些,适合作为另一层次的保底offer。除此之外,选调的笔试和毕设的实验也要启动了,因为做的是cv相关的东西,跑实验的时候就可以看各种考公资料,并行处理来争取时间。

  • 2020年 11月 选调生笔试:本人主要参加了两场选调生考试,浙江选调(11.15)和中央选调(11.29 和国考一致),两者相差半个月左右,对我来说这个时间并不算特别舒服,中间隔了半个月,好不容易练起来的做题手速凉了一半;同时继续积累毕业论文所需的实验数据。

  • 2020年 12月 完成毕设: 由于今年选调卷爆炸的形势和自己的失误,两场选调的笔试均以毫厘之差的失败而告终,至此整个秋招基本结束,后续便将重心移至毕业论文的撰写上。

信息管理

网络上现在关于求职的信息多如牛毛,很多情况下需要我们进行过滤和归档,因此我们也需要对繁杂的信息进行管理,本节会分享一些常用的信息渠道、经验资料和管理工具。

互联网

  • 求职信息渠道:牛客网、微信群、学校就业网、CC98、脉脉、领英、朋友圈(很多人可能平时是关闭的,建议求职期间开启)、知乎、github、各个企业官网
  • 经验资料:
    • 笔试资源:
      • leetcode题解博主:花花酱 happygirl cat racket acwing 闫学灿
      • leetcode 题目分类列表
      • 算法oi: https://github.com/OI-wiki/OI-wiki/
      • 我自己总结的算法模板(Python): https://www.zhihu.com/people/mason-21/posts
    • 面试资料:不同岗位的面经不同,没有统一的方案,最好是自己总结一份文档,这里给出常用的算法岗的参考资料,可能在前文中出现过。
      • 书籍:《百面》系列,《统计学习方法》
      • 视频: b站白板推导系列
      • 博客: 海量数据系列
      • 论坛:在牛客上搜索对应公司的面试消息
  • 管理工具:
    • 笔记汇编:推荐onenote和有道云,前者可以通过onedrive在多平台上同步,且配合chrome插件clip to onenote 很容易实现对网页的剪贴;后者可以通过微信进行网页的剪贴。两者都无设备数的限制。
    • 链接存储:很多时候我们看到一份资料很棒,但是又没时间一次性看完,或是几个资料间重合度较高,想要先存起来待后续整理,这时就需要一个临时存放链接的工具,这里推荐pocket,pc端可以利用chrome的插件,移动端也有app。
    • 代码分享:某些时刻需要阅读一些小片段的代码,可以用到一个工具网站 https://paste.ubuntu.com/ 进行格式上的美化,不必再重新保存成文件了。

体制内

  • 资格核对:

    • 一般三大项:中共党员(含预备党员)、校级荣誉(“三好”学生、校级优秀毕业生)、具有相应层级学生干部经历1学年以上,其中浙江选调是”或“关系,江苏和央选是”与“关系。
    • 经历积累:
      • 学生干部经历:班干部(班长,团支书)或党支书
      • 学生组织:基协、紫领
      • 培养计划:各学院青马工程(计算机学院是熔金计划)
      • 单位实习:公管学院面向全校的”青知计划“
  • 岗位信息渠道:

    • 网站
      • 浙江大学就业指导网与服务中心:http://www.career.zju.edu.cn/ ,
      • 浙江人事考试网:http://www.zjks.com/
      • 高校人才网:http://www.gaoxiaojob.com/
    • 公众号/小程序
      • 浙大基协
      • 紫领人才俱乐部
      • 粉笔上岸通
    • 辅导员、同学、微信群
      • 中央选调一般是辅导员通过党支部通知,每个学院有名额,还要上报学校审批,有意向的同学可以提早和老师沟通。
  • 经验资料:

    • 笔试:
      • 行测: fb 980系统地刷一遍,主要是练习资料分析的速算和数量关系的估算。对于选调我其实不建议大量的刷模拟题,一是题目的质量无法保证,二是近年来题目(浙选、国考)难度持续拉升,往年的题目水平已经不具备太大的参考价值。
      • 申论:
        • 做真题,结合 半月谈、申论查等app综合比较答案
        • 对应省份的省委书记讲话稿,主要看一下在任时的发展战略。
        • 半月谈 《申论素材宝典》
        • 人民网时评
    • 面试:本人并未入围两地的面试,这里取成功者经验,各位看官姑且一观。
      • 不建议各种费用昂贵的培训班,玩的就是概率。
      • 最有效的方法——微信互助:和小伙伴建群约定时间进行线下模拟练习,各自找一个题目,对方帮忙计时并且点评,提出修改建议。
      • 紫领会组织已经上岸的学长分享经验
      • 演讲的技巧:
        • How to speak
        • Make Body Language Your Super Power
  • 辅助工具:

    • 资料整理工具同上一小节互联网处一致

    • 讯飞有声app:可以将文字内容转成语音,这样就可以利用吃饭、通勤等碎片时间积累素材,同时解放眼睛。题外话:英文文献可以利用ivona TTS 引擎 + natural reader听读。

    • Anki: 辅助记忆的工具,可以制作一些面试卡片,训练自己的反应力。

很多前辈的经验分享珠玉在前,这里补充两个超详细的求职攻略,细节上的准备过程本文就不再重复造轮子了(内网可访问):

IT业:@冰冰的小冰 https://www.cc98.org/topic/4905157

体制内:@马甲咿呀 https://www.cc98.org/topic/4914295

人脉管理

求职切忌“单打独斗”,求职笔面并不是闭卷考,因此要充分利用师门前辈和同届校友的资源,尽可能的抹平和公司的信息差,一般的,大多数同学能够接触到人脉资源可分为五个类型:

  1. 同门师兄师姐(前提是师门画风和谐):掌握着公司内部的一手信息,且一般非常乐意帮助下一届。

  2. 同届小伙伴:共享信息,一人面试,多人受益。

  3. 校友:包括各类在校内发布内推的前辈,这类较为鱼龙混杂,有无私分享的,也有只看重内推奖励的,还有直接贩卖焦虑割韭菜的。

  4. 企业HR / 组织部或人社局工作人员: 通过这类人能够获得官方公开的消息,但基本不可能获得隐藏的额外信息。
  5. 公网上的陌生人:有很多公司的员工会直接把内推码挂在牛客等论坛上,不建议利用这个渠道,个人信息给了陌生人倒是其次,主要是如果后续无法联系上他就相当于无法追踪你在这个公司的进度,浪费了一次机会。

根据不同的专业和投递的行业,选择不同类型的人脉作为基本盘:

如,本人专业是CS,对于互联网来说根正苗红,因此主要依靠前两个类型的人脉,这是专业优势;但一旦求职方向转换为体制内,就只能依靠校友和少量同届小伙伴了,此时就要借助学校平台的力量,多参加上一节”经历积累“中提到的组织和活动来结识同道中人。

结语

按照原本的计划,我是想互联网和选调两手抓的,奈何对选调变“卷”的加速度估计不足,导致最后准备笔试的时间还是有所不足,事后诸葛亮一下,应该从暑假就开始看一些应试的资料,这样加上9、10两个月份就可以更加宽裕、并行地撰写毕业论文;或者就是要有破釜沉舟地勇气,选择直接延毕,我周围也有这样成功上岸的同学。从整体的趋势看,CS专业在选调中热门程度有所下降,以浙江省为例,19届的优质岗位主要是省级单位,20届的主要是市级单位,而我们这一届主要是区县级和公安局,计算机相关的体制内岗位在逐渐饱和,如果你是CSer的话也建议也先找个保底的互联网offer,因为到时候不一定有想报的岗,而且我们这一届很多省开始把好岗位作为提前批单独给了清北,这一点也需要一并考虑。

人才引进的话岗位倒是目前来看挺充裕的,不过有些待遇好的地区有些岗位要求博士了(就紧接着我的下一批),每年的政策也都在变化。

考公的话,总体的趋势是岗位往应届生倾斜,也就是选调生普通gwy化,公考职业老油条神仙打架化。所以如果大家想进入体制内的话,还是要早早做抉择,利用好应届生的优势。

致谢

最后,感谢一下提供各种帮助的师兄师姐、校友前辈们和

在互联网赛道上一起奋斗过的小伙伴们: @ 鸽王 @ 黄老板 @final马 @常委 @纬爷

在体制内道路上一起奋斗过的小伙伴们: @孟主任 @许书记

最后, 祝各位22届小伙伴求职顺利~

最最后,如果大家有任何求职的疑问(互联网、体制、转行)都欢迎私聊,lz尽力回答,能力范围之外也可以邀请认识的好友代答,最近应该还会在学校里苟一会~

Q&A

这里选取部分常见的问题做一下统一的回答,方便大家阅读。

Q: 半小时kill3题也太难了吧 现在基本上只能做出来两道是不是没救了?

A: 一般45分钟3道题也行的,一般一场1小时的面试,前面30到40分钟会问你基础知识和项目,后面的时间做1~2道题,10几分钟写道题还是比较正常,主要是要理清思路,多和面试官沟通自己的思路,实在做不出的话,nice一点的面试官会给提示的,而且一般不会出hard题,出了也是经典题,出怪题可以去举报面试官了hhh(应该就是故意挂人)

Q: 【私信】本科不是浙大的,请问会不会卡学历?

A: 现在有浙大学历肯定符合学校要求,但是本科的荣誉和学生干部经历只有当 本科学校也在选调范围内时才作数,否则只能用在浙大时取得的荣誉和干部经历。

Q: 想问下海外高校去选调,是不是机会比较少呀?

A: 人才引进是包含了海外高校的, 招录公告会明确范围;选调的话挺少的,这里截取了知乎一个答案的给大家参考:

截止2021年1月,目前有面向海外定向选调政策的省市包括:

  • 8个省/直辖市:四川、重庆、河南、广东、黑龙江、河北、北京、山东。
  • 2个城市:长春、青岛。

Q: 【私信】这种人才引进是不是首先有限制一定的专业,然后人才引进的面试流程具体形式会是怎样的呀?然后宁波下面不同地区人才引进是不是分开招的呀?证明一个地区没有上还能有机会申别的地区嘛?

A: 对,是分岗位招的,每个岗位有对应的专业限制,像我就是信息技术 面向计算机类的, 其他还有经济管理、综合管理、建筑工程等; 面试流程的话,宁波是先写个申论作为笔试,当天晚上通知是否进面,第二天早上直接进行结构化面试,然后招聘的时候各个区是统一的,你只能选一个区报,一个区没上也没有机会申别的区了。

Q: 那请问浙大本科期间做过班级委员也算符合要求吗?

A: 一般要求班委以上 保险的:班长(副班长)、团支部书记(副书记) 生活委员、学习委员等职务一般不界定为学生干部,实际请咨询辅导员或就业办老师。

hexo主题美化

发表于 2021-02-05 | 分类于 网站建设
字数统计: 515 | 阅读时长 ≈ 2

hexo美化之添加live2d动画

1.hexo博客文件目录下安装live2D插件

1
bashnpm install --save hexo-helper-live2d

2.下载live2D动画模型

动画选项及对应效果展示

live2d-widget-model-chitose
live2d-widget-model-z16
live2d-widget-model-epsilon2_1
live2d-widget-model-gf
live2d-widget-model-haru/01
live2d-widget-model-haru/02
live2d-widget-model-haruto
live2d-widget-model-hibiki
live2d-widget-model-hijiki
live2d-widget-model-izumi
live2d-widget-model-koharu
live2d-widget-model-miku
live2d-widget-model-ni-j
live2d-widget-model-nico
live2d-widget-model-nietzsche
live2d-widget-model-nipsilon
live2d-widget-model-nito
live2d-widget-model-shizuku
live2d-widget-model-tororo
live2d-widget-model-tsumiki
live2d-widget-model-unitychan
live2d-widget-model-wanko

安装示例

1
npm install live2d-widget-model-tororo

3. 修改配置文件_config.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# Live2D
## https://github.com/EYHN/hexo-helper-live2d
live2d:
enable: true #隐藏/显示
pluginRootPath: live2dw/ #插件在站点上的根路径(相对)
pluginJsPath: lib/ #与插件根目录相关的JavaScript路径(相对)
pluginModelPath: assets/ #与插件根(相对)相关的模型路径
scriptFrom: local # Default
# scriptFrom: jsdelivr # jsdelivr CDN
# scriptFrom: unpkg # unpkg CDN
# scriptFrom: https://cdn.jsdelivr.net/npm/live2d-widget@3.x/lib/L2Dwidget.min.js # Your custom url
tagMode: false #是否只替换live2d标签而不是注入到所有页面
log: false #是否在控制台显示日志
model:
use: live2d-widget-model-wanko #安装的模型包名称
display: #设置位置和宽高(设置的宽高*2为实际像素,如:50=100px)
position: right
width: 200
height: 400
mobile:
show: false #是否在移动端上显示

参考资料:

https://www.guoxh.com/blog/2019/03/15/hexo%E9%9B%86%E6%88%90live2D%E5%8A%A8%E7%94%BB/

hexo美化之右上角添加fork me on github入口

1.首先到GitHub Corners或者GitHub Ribbons选择自己喜欢的图标,然后copy相应的代码

2.然后将刚才复制的代码粘贴到themes/next/layout/_layout.swig文件中<div class="headband"></div>

下面一行

3.把代码中的href后面的值替换成你要跳转的地址,比如你的GitHub主页

参考资料:

https://blog.csdn.net/mqdxiaoxiao/article/details/93796367

hexo美化之添加侧边社交链接

在配置文件theme文件夹中的_config.yaml中添加

1
2
3
4
5
6
7
8
social:
GitHub: https://github.com/Po***** || github
E-Mail: mailto:113***@qq.com || envelope

social_icons:
enable: true
icons_only: false
transition: false

参考资料:

https://benpaodewoniu.github.io/2020/03/08/hexo21/

https://cloud.tencent.com/developer/article/1577027(较为全面)

互联网笔试常用数据结构与算法总结(python 模板)

发表于 2021-02-05 | 分类于 求职
字数统计: 7.3k | 阅读时长 ≈ 35

常用数据结构和算法模板(python)

经典

1.埃拉托斯特尼筛法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
def countPrimes(n):
'''
输出 <= n 的质数个数
:param n: 整数
:return: 质数个数
'''
if n < 2:
return 0

isPrime = [True] * (n + 1)
isPrime[0] = isPrime[1] = False
i = 2
while i * i <= n:
if isPrime[i]:
for j in range(i*i, n+1, i):
isPrime[j] = False
i += 1

cnt = 0
for flag in isPrime:
if flag:
cnt += 1
return cnt

参考习题 https://leetcode-cn.com/problems/count-primes/, 答案如下,和模板略有区别:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Solution:
def countPrimes(self, n: int) -> int:
'''
返回小于 n 的质数个数
:param n:
:return:
'''
if n <= 2:
return 0

isPrime = [True] * (n)
isPrime[0] = isPrime[1] = False
i = 2
while i * i < n:
if isPrime[i]:
for j in range(i*i, n, i):
isPrime[j] = False
i += 1

cnt = 0
for flag in isPrime:
if flag:
cnt += 1
return cnt

2. 快速幂

1
2
3
4
5
6
7
8
9
10
11
def myPow(x: float, n: int) -> float:
res = 1.0
base = x
e = abs(n)
while e:
if e & 1 == 1:
res *= base

base *= base
e >>= 1
return res if n >= 0 else 1.0 / res

练习题:https://leetcode-cn.com/problems/powx-n/

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Solution:
def myPow(self, x: float, n: int) -> float:

def quick_pow(x, n):
base = x
ans = 1

while n:
if n & 1:
ans *= base

base *= base
n >>= 1

return ans
return quick_pow(x, n) if n > 0 else 1/quick_pow(x, abs(n))

3. 大数模拟

大数加法

练习题:leetcode 415 https://leetcode-cn.com/problems/add-strings/

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def addStrings(self, num1: str, num2: str) -> str:
'''
大数加法
'''
res = ""
i, j, carry = len(num1) - 1, len(num2) - 1, 0
while i >= 0 or j >= 0:
# 高位补零
n1 = int(num1[i]) if i >= 0 else 0
n2 = int(num2[j]) if j >= 0 else 0
tmp = n1 + n2 + carry
carry = tmp // 10
res = str(tmp % 10) + res
i, j = i - 1, j - 1
return "1" + res if carry else res

大数乘法

练习题:leetcode 43 https://leetcode.com/problems/multiply-strings/

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
def multiply(num1, num2):
product = [0] * (len(num1) + len(num2)) # placeholder for multiplication ndigit by mdigit result in n+m digits
position = len(product) - 1 # position within the placeholder

for n1 in num1[::-1]:
tempPos = position
for n2 in num2[::-1]:
product[tempPos] += int(n1) * int(n2) # ading the results of single multiplication
product[tempPos - 1] += product[tempPos] // 10 # bring out carry number to the left array
product[tempPos] %= 10 # remove the carry out from the current array
tempPos -= 1 # first shifting the multplication to the end of the first integer
position -= 1 # then once first integer is exhausted shifting the second integer and starting

# once the second integer is exhausted we want to make sure we are not zero padding
pointer = 0 # pointer moves through the digit array and locate where the zero padding finishes
while pointer < len(product) - 1 and product[
pointer] == 0: # if we have zero before the numbers shift the pointer to the right
pointer += 1

return ''.join(map(str, product[pointer:])) # only report the digits to the right side of the pointer

计算进位: 计算 carry = tmp // 10,代表当前位相加是否产生进位;
添加当前位: 计算 tmp = n1 + n2 + carry,并将当前位 tmp % 10 添加至 res 头部;
索引溢出处理: 当指针 i或j 走过数字首部后,给 n1,n2 赋值为 00,相当于给 num1,num2 中长度较短的数字前面填 00,以便后续计算。
当遍历完 num1,num2 后跳出循环,并根据 carry 值决定是否在头部添加进位 11,最终返回 res 即可。
复杂度分析:

时间复杂度 O(max(M,N))O(max(M,N)):其中 MM,NN 为 22 数字长度,按位遍历一遍数字(以较长的数字为准);
空间复杂度 O(1)O(1):指针与变量使用常数大小空间。

4. GCD

1
2
3
4
5
6
7
8
def gcd(a, b):
'''
这里不用判断a, b的相对大小,如果 a < b, 在递归调用 gcd(b, a%b)时
自动调换了顺序。
'''
if b == 0:
return a
return gcd(b, a % b)

参考习题:https://leetcode-cn.com/problems/simplified-fractions/,最简分数,gcd的应用

1
2
3
4
5
6
7
8
9
10
11
12
13
class Solution:
def simplifiedFractions(self, n: int) -> List[str]:
def gcd(a, b):
if b == 0:
return a
return gcd(b, a % b)

res = []
for i in range(2, n+1):
for j in range(1, i):
if gcd(j, i) == 1:
res.append(str(j) + '/' + str(i))
return res

5. LCM (最小公倍数)

1
2
def lcm(a, b):
return a * b // gcd(a, b)

7. 二分搜索

万能模板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 当分支逻辑不能排除右边界,选左中位数,如果选右中位数则会出现死循环
def binary_search1(left, right):
while left < right:
mid = (left + right) >> 1
if check(mid):
left = mid + 1
else:
right = mid
# 退出循环的时候, 视情况,是否需要单独判断left是否满足条件
return left

def binary_search2(left, right):
while left < right:
# 选择右中位数
while left < right:
mid = (left + right + 1) >> 1
if check(mid):
right = mid - 1
else:
left = mid
# 退出循环的时候, 视情况,是否需要单独判断left是否满足条件
return left

8. 并查集

1
2
3
4
5
6
7
8
9
10
11
12
13
parent = list(range(N))

def find(x):
if x != parent[x]:
# 路径完全压缩
parent[x] = find(parent[x])

return parent[x]

def union(x, y):
root1 = find(x)
root2 = find(y)
parent[root2] = root1

类形式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class DSU:
def __init__(self, N):
self.parent = list(range(N+1))
self.edges = 0

def find(self, x):
if x != self.parent[x]:
# 路径完全压缩
self.parent[x] = self.find(self.parent[x])

return self.parent[x]

def union(self, x, y):
root1 = self.find(x)
root2 = self.find(y)

self.parent[root2] = root1
self.edges += 1

图论

9. 最小生成树(贪心思想)

解析 花花酱:https://www.youtube.com/watch?v=wmW8G8SrXDs

图论500 题 https://blog.csdn.net/luomingjun12315/article/details/47438607

Kruskal算法

主体部分如下,需要用到并查集(下面完整测试案例中会给出)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def kruskal():
'''
适用于求稀疏图
:return: 最小生成树的边和
'''
cost = 0
for u, v, w in sorted(edges, key=lambda x: x[2]):
pu, pv = find(u), find(v)
if pu == pv:
continue
# 等同于union
parent[pu] = pv
cost += w
return cost

leetcode中缺少相关的习题,固用花花酱视频中的例子测试了一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
n = 4
edges = [[0, 1, 1], [0, 3, 3], [0, 2, 6], [2, 3, 2], [1, 2, 4]]

parent = list(range(n))

def find(x):
if x != parent[x]:
# 路径完全压缩
parent[x] = find(parent[x])

return parent[x]


def union(x, y):
root1 = find(x)
root2 = find(y)
parent[root2] = root1


def kruskal():
'''
适用于求稀疏图
:return: 最小生成树的边和
'''
cost = 0
for u, v, w in sorted(edges, key=lambda x: x[2]):
pu, pv = find(u), find(v)
if pu == pv:
continue
# 等同于union
parent[pu] = pv
cost += w
return cost

Prim算法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
def prime():
'''
适用于稠密图,堆优化版本
:return: cost
'''
q = []
cost = 0
seen = set()
# push a dummy node, (cost, node)
heappush(q, (0, 0))

for _ in range(n):
w, u = heappop(q)
if u in seen:
continue
cost += w
seen.add(u)
for v, w in graph[u]:
if v in seen:
continue
heappush(q, (w, v))
return cost

测试代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
from collections import defaultdict
from heapq import *

n = 4 # 顶点数
edges = [[0, 1, 1], [0, 3, 3], [0, 2, 6], [2, 3, 2], [1, 2, 4]]

graph = defaultdict(list)
for e in edges:
graph[e[0]].append((e[1], e[2]))
graph[e[1]].append((e[0], e[2]))

def prime():
'''
适用于稠密图,堆优化版本
:return: cost
'''
q = []
cost = 0
seen = set()
# push a dummy node, (cost, node)
heappush(q, (0, 0))

for _ in range(n):
w, u = heappop(q)
if u in seen:
continue
cost += w
seen.add(u)
for v, w in graph[u]:
if v in seen:
continue
heappush(q, (w, v))
return cost

print(prime())
# 6

最短路算法合集

统一习题:LC743 https://leetcode-cn.com/problems/network-delay-time/

最短路算法的分类:

  • 单源最短路
    • 所有边权都是正数
      • 朴素的Dijkstra算法 O(n^2) 适合稠密图
      • 堆优化版的Dijkstra算法 O(mlog n)(m是图中节点的个数)适合稀疏图
    • 存在负权边
      • Bellman-Ford O(nm)
      • spfa 一般O(m), 最坏O(nm)
  • 多源汇最短路 Floyd算法 O(n^3)

参考代码:https://leetcode.com/problems/network-delay-time/discuss/283711/python-bellman-ford-spfa-dijkstra-floyd-clean-and-easy-to-understand

11. 迪杰斯特拉算法

单源最短路

  • 所有边权都是正数
  • 朴素的Dijkstra算法 O(n^2) 适合稠密图
  • 堆优化版的Dijkstra算法 O(mlog n)(m是图中节点的个数)适合稀疏图
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
def dijkstra(graph, source, N):
'''
单源最短路径算法
:param graph: 邻接矩阵,或者用字典实现
:param source: 起始点
:param N: 节点个数
:return:
'''
# 如果是node 是 1-indexed
dist = [float('inf')] * (N + 1)
prev = [-1] * (N + 1)

dist[source] = dist[0] = 0

hq = [(0, source)]
while hq:
d, u = heapq.heappop(hq)
for v in graph[u]:
alt = dist[u] + graph[u][v]
if alt < dist[v]:
dist[v] = alt
prev[v] = u
heapq.heappush(hq, (alt, v))
return dist, prev

习题解答:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
class Solution:
def networkDelayTime(self, times: List[List[int]], N: int, K: int) -> int:
# construct graph
graph = collections.defaultdict(dict)
for u, v, time in times:
graph[u][v] = time


def dijkstra(graph, source, N):
'''
单源最短路径算法
:param graph: 邻接矩阵,或者用字典实现
:param source: 起始点
:return:
'''
dist = [float('inf')] * (N + 1)
prev = [-1] * (N + 1)

# 如果是node 是 1-indexed
dist[source] = dist[0] = 0

hq = [(0, source)]
while hq:
d, u = heapq.heappop(hq)
for v in graph[u]:
alt = dist[u] + graph[u][v]
if alt < dist[v]:
dist[v] = alt
prev[v] = u
heapq.heappush(hq, (alt, v))
return dist, prev

dist, _ = dijkstra(graph, K, N)
# print(dist)
ans = max(dist)
return ans if ans != float('inf') else -1

Bellman-Ford 算法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# Python3 program for Bellman-Ford's single source
# shortest path algorithm.

# Class to represent a graph
class Graph:

def __init__(self, vertices):
self.V = vertices # No. of vertices
self.graph = []


# function to add an edge to graph
def addEdge(self, u, v, w):
self.graph.append([u, v, w])


# utility function used to print the solution
def printArr(self, dist):
print("Vertex Distance from Source")
for i in range(self.V):
print("{0}\t\t{1}".format(i, dist[i]))

# The main function that finds shortest distances from src to
# all other vertices using Bellman-Ford algorithm. The function
# also detects negative weight cycle
def BellmanFord(self, src):

# Step 1: Initialize distances from src to all other vertices
# as INFINITE
dist = [float("Inf")] * self.V
dist[src] = 0

# Step 2: Relax all edges |V| - 1 times. A simple shortest
# path from src to any other vertex can have at-most |V| - 1
# edges
for _ in range(self.V - 1):
# Update dist value and parent index of the adjacent vertices of
# the picked vertex. Consider only those vertices which are still in
# queue
for u, v, w in self.graph:
if dist[u] != float("Inf") and dist[u] + w < dist[v]:
dist[v] = dist[u] + w

# Step 3: check for negative-weight cycles. The above step
# guarantees shortest distances if graph doesn't contain
# negative weight cycle. If we get a shorter path, then there
# is a cycle.

for u, v, w in self.graph:
if dist[u] != float("Inf") and dist[u] + w < dist[v]:
print("Graph contains negative weight cycle")
return

# print all distance
self.printArr(dist)


g = Graph(5)
g.addEdge(0, 1, -1)
g.addEdge(0, 2, 4)
g.addEdge(1, 2, 3)
g.addEdge(1, 3, 2)
g.addEdge(1, 4, 2)
g.addEdge(3, 2, 5)
g.addEdge(3, 1, 1)
g.addEdge(4, 3, -3)

# Print the solution
g.BellmanFord(0)

# Initially, Contributed by Neelam Yadav
# Later On, Edited by Himanshu Garg
1
2
3
4
5
6
7
8
9
class Solution:
def networkDelayTime(self, times: List[List[int]], N: int, K: int) -> int:
dist = [float("inf") for _ in range(N)]
dist[K-1] = 0
for _ in range(N-1):
for u, v, w in times:
if dist[u-1] + w < dist[v-1]:
dist[v-1] = dist[u-1] + w
return max(dist) if max(dist) < float("inf") else -1

12. spfa

判断有无负环:如果某个点进入队列的次数超过N次则存在负环(SPFA无法处理带负环的图,但是可以判断是否出现负权环)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Solution:
def networkDelayTime(self, times: List[List[int]], N: int, K: int) -> int:
dist = [float("inf") for _ in range(N)]
K -= 1
dist[K] = 0
weight = collections.defaultdict(dict)
for u, v, w in times:
weight[u-1][v-1] = w

queue = collections.deque([K])
while queue:
u = queue.popleft()
for v in weight[u]:
if dist[u] + weight[u][v] < dist[v]:
dist[v] = dist[u] + weight[u][v]
queue.append(v)
return max(dist) if max(dist) < float("inf") else -1

13. Floyd-Warshall

1
2
3
4
5
6
7
8
9
10
def floyd_warshall(graph, N):
'''
:param graph: 邻接矩阵
:param N: 节点数
:return: 修改过的邻接矩阵
'''
for k in range(N):
for i in range(N):
for j in range(N):
dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j])

习题答案:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Solution:
def networkDelayTime(self, times: List[List[int]], N: int, K: int) -> int:
# construct graph
dist = [[float("inf") for _ in range(N)] for _ in range(N)]
for u, v, w in times:
dist[u - 1][v - 1] = w
for i in range(N):
dist[i][i] = 0

def floyd_warshall(graph, N):
'''
:param graph: 邻接矩阵
:param N: 节点数
:return: 修改过的邻接矩阵
'''
for k in range(N):
for i in range(N):
for j in range(N):
dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j])

floyd_warshall(dist, N)
return max(dist[K - 1]) if max(dist[K - 1]) < float("inf") else -1

二分图

14. 染色法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import collections


def isBipartite(graph):
n = len(graph)
UNCOLORED, RED, GREEN = 0, 1, 2
color = [UNCOLORED] * n

# graph 不一定是连通图
for i in range(n):
if color[i] == UNCOLORED:
q = collections.deque([i])
color[i] = RED
while q:
node = q.popleft()
cNei = (GREEN if color[node] == RED else RED)
for neighbor in graph[node]:
if color[neighbor] == UNCOLORED:
q.append(neighbor)
color[neighbor] = cNei
elif color[neighbor] != cNei:
return False, None

return True, color

graph = [[1,3], [0,2], [1,3], [0,2]]
print(isBipartite(graph))
'''
(True, [1, 2, 1, 2])
'''

15. 匈牙利算法 (用于寻找最大匹配)

讲解:https://www.renfei.org/blog/bipartite-matching.html

https://blog.csdn.net/dark_scope/article/details/8880547

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
class DFS_hungary():
# https://www.icode9.com/content-1-615590.html
# 参数初始化
def __init__(self, set_A, set_B, edge, cx, cy, visited):
self.set_A, self.set_B = set_A, set_B # 顶点集合
self.edge = edge # 顶点是否连边
self.cx, self.cy = cx, cy # 顶点是否匹配
self.visited = visited # 顶点是否被访问
self.M = [] # 匹配
self.res = 0 # 匹配数

# 遍历顶点A集合,得到最大匹配
def max_match(self):
for i in self.set_A:
if self.cx[i] == -1: # 未匹配
for key in self.set_B: # 将visited置0表示未访问过
self.visited[key] = 0
self.res += self.path(i)
print('i', i, 'M',self.M)


# 增广路置换获得更大的匹配
def path(self, u):
for v in self.set_B:
if self.edge[u][v] and (not self.visited[v]): # 如果可连且未被访问过
self.visited[v] = 1 # 访问该顶点
if self.cy[v] == -1: # 如果未匹配, 则建立匹配
self.cx[u], self.cy[v] = v, u
self.M.append((u, v))
return 1
else:
self.M.remove((self.cy[v], v)) # 如果匹配则删除之前的匹配
if self.path(self.cy[v]): # 递归调用
self.cx[u], self.cy[v] = v, u
self.M.append((u, v))
return 1
print('v', v, 'M', self.M)
return 0


if __name__ == '__main__':
set_A, set_B = ['A', 'B', 'C', 'D'], ['E', 'F', 'G', 'H']
edge = {'A': {'E': 1, 'F': 0, 'G': 1, 'H': 0}, 'B': {'E': 0, 'F': 1, 'G': 0, 'H': 1},
'C': {'E': 1, 'F': 0, 'G': 0, 'H': 1}, 'D': {'E': 0, 'F': 0, 'G': 1, 'H': 0}} # 1表示可以匹配,0表示不能匹配
cx, cy = {'A': -1, 'B': -1, 'C': -1, 'D': -1}, {'E': -1, 'F': -1, 'G': -1, 'H': -1}
visited = {'E': 0, 'F': 0, 'G': 0, 'H': 0}
dh = DFS_hungary(set_A, set_B, edge, cx, cy, visited)
dh.max_match()
print('res', dh.res)
print('cx', cx)
print('cy', cy)
print('visited', visited)

# 结果显示:
# i A M [('A', 'E')] # 对于E点,可与A点连接,第一次匹配,直接在max_match打印,存在增广路:CEAG
# v E M [('A', 'E')] # 对于E点,不能和B点连接,在path中打印
# i B M [('A', 'E'), ('B', 'F')] # 对于F点,可与B点连接,直接在max_match打印,匹配数增加,存在增广路:CEAG
# v E M [('B', 'F')] # 对于E点,可以与C连接,但已经与A点连接,从M中移除AE,在path中打印,进入递归内部
# v F M [('B', 'F')]
# i C M [('B', 'F'), ('A', 'G'), ('C', 'E')] # 对于G点,可与A点连接,直接在max_match打印,匹配数增加,存在增广路:DGAECH
# v E M [('B', 'F'), ('A', 'G'), ('C', 'E')] # 对于E点,不能与D点连接,在path中打印
# v F M [('B', 'F'), ('A', 'G'), ('C', 'E')] # 对于F点,不能与D点连接,在path中打印
# v E M [('B', 'F')] # 对于G点,可以与D连接,但已经与A点连接,从M中移除AG,在path中打印,进入递归内部,继续移除CE
# v F M [('B', 'F')]
# v G M [('B', 'F')]
# i D M [('B', 'F'), ('C', 'H'), ('A', 'E'), ('D', 'G')] # 无增广路
# res 4
# cx {'A': 'E', 'B': 'F', 'C': 'H', 'D': 'G'}
# cy {'E': 'A', 'F': 'B', 'G': 'D', 'H': 'C'}
# visited {'E': 1, 'F': 0, 'G': 1, 'H': 1}

动态规划

16. 背包问题

reference:https://zhuanlan.zhihu.com/p/93857890

0-1背包

  1. 不装入第i件物品,即dp[i−1][j];
  2. 装入第i件物品(前提是能装下),即dp[i−1][j−w[i]] + v[i]。

即状态转移方程为

1
dp[i][j] = max(dp[i−1][j], dp[i−1][j−w[i]]+v[i]) // j >= w[i]

由上述状态转移方程可知,dp[i][j]的值只与dp[i-1][0,...,j-1]有关,所以我们可以采用动态规划常用的方法(滚动数组)对空间进行优化(即去掉dp的第一维)。需要注意的是,为了防止上一层循环的dp[0,...,j-1]被覆盖,循环的时候 j 只能逆向枚举(空间优化前没有这个限制),伪代码为:

1
2
3
4
5
// 01背包问题伪代码(空间优化版)
dp[0,...,W] = 0
for i = 1,...,N
for j = W,...,w[i] // 必须逆向枚举!!!
dp[j] = max(dp[j], dp[j−w[i]]+v[i])

动态规划的核心思想避免重复计算在01背包问题中体现得淋漓尽致。第i件物品装入或者不装入而获得的最大价值完全可以由前面i-1件物品的最大价值决定,暴力枚举忽略了这个事实。

完全背包

分析一
  1. 不装入第i种物品,即dp[i−1][j],同01背包;
  2. 装入第i种物品,此时和01背包不太一样,因为每种物品有无限个(但注意书包限重是有限的),所以此时不应该转移到dp[i−1][j−w[i]]而应该转移到dp[i][j−w[i]],即装入第i种商品后还可以再继续装入第种商品。

所以状态转移方程为

1
dp[i][j] = max(dp[i−1][j], dp[i][j−w[i]]+v[i]) // j >= w[i]

这个状态转移方程与01背包问题唯一不同就是max第二项不是dp[i-1]而是dp[i]。

和01背包问题类似,也可进行空间优化,优化后不同点在于这里的 j 只能正向枚举而01背包只能逆向枚举,因为这里的max第二项是dp[i]而01背包是dp[i-1],即这里就是需要覆盖而01背包需要避免覆盖。所以伪代码如下:

1
2
3
4
5
// 完全背包问题思路一伪代码(空间优化版)
dp[0,...,W] = 0
for i = 1,...,N
for j = w[i],...,W // 必须正向枚举!!!
dp[j] = max(dp[j], dp[j−w[i]]+v[i])

由上述伪代码看出,01背包和完全背包问题此解法的空间优化版解法唯一不同就是前者的 j 只能逆向枚举而后者的 j 只能正向枚举,这是由二者的状态转移方程决定的。此解法时间复杂度为O(NW), 空间复杂度为O(W)。

分析二

除了分析一的思路外,完全背包还有一种常见的思路,但是复杂度高一些。我们从装入第 i 种物品多少件出发,01背包只有两种情况即取0件和取1件,而这里是取0件、1件、2件…直到超过限重(k > j/w[i]),所以状态转移方程为:

1
2
# k为装入第i种物品的件数, k <= j/w[i]
dp[i][j] = max{(dp[i-1][j − k*w[i]] + k*v[i]) for every k}

同理也可以进行空间优化,需要注意的是,这里max里面是dp[i-1],和01背包一样,所以 j 必须逆向枚举,优化后伪代码为

1
2
3
4
5
6
// 完全背包问题思路二伪代码(空间优化版)
dp[0,...,W] = 0
for i = 1,...,N
for j = W,...,w[i] // 必须逆向枚举!!!
for k = [0, 1,..., j/w[i]]
dp[j] = max(dp[j], dp[j−k*w[i]]+k*v[i])

相比于分析一,此种方法不是在O(1)时间求得dp[i][j],所以总的时间复杂度就比分析一大些了,为 [公式]级别。

分析三、转换成01背包

01背包问题是最基本的背包问题,我们可以考虑把完全背包问题转化为01背包问题来解:将一种物品转换成若干件只能装入0件或者1件的01背包中的物品。

最简单的想法是,考虑到第 i 种物品最多装入 W/w[i] 件,于是可以把第 i 种物品转化为 W/w[i] 件费用及价值均不变的物品,然后求解这个01背包问题。

更高效的转化方法是采用二进制的思想:把第 i 种物品拆成重量为 [公式] 、价值为 [公式] 的若干件物品,其中 k 取遍满足 [公式] 的非负整数。这是因为不管最优策略选几件第 i 种物品,总可以表示成若干个刚才这些物品的和(例:13 = 1 + 4 + 8)。这样就将转换后的物品数目降成了对数级别。

多重背包

分析一

此时的分析和完全背包的分析二差不多,也是从装入第 i 种物品多少件出发:装入第i种物品0件、1件、…n[i]件(还要满足不超过限重)。所以状态方程为:

1
2
# k为装入第i种物品的件数, k <= min(n[i], j/w[i])
dp[i][j] = max{(dp[i-1][j − k*w[i]] + k*v[i]) for every k}

同理也可以进行空间优化,而且 j 也必须逆向枚举,优化后伪代码为

1
2
3
4
5
6
// 完全背包问题思路二伪代码(空间优化版)
dp[0,...,W] = 0
for i = 1,...,N
for j = W,...,w[i] // 必须逆向枚举!!!
for k = [0, 1,..., min(n[i], j/w[i])]
dp[j] = max(dp[j], dp[j−k*w[i]]+k*v[i])

总的时间复杂度约为 [公式] 级别。

其他情形

参考https://blog.csdn.net/weixin_41162823/article/details/87878853

1 恰好装满

背包问题有时候还有一个限制就是必须恰好装满背包,此时基本思路没有区别,只是在初始化的时候有所不同。

如果没有恰好装满背包的限制,我们将dp全部初始化成0就可以了。因为任何容量的背包都有一个合法解“什么都不装”,这个解的价值为0,所以初始时状态的值也就全部为0了。如果有恰好装满的限制,那只应该将dp[0,…,N][0]初始为0,其它dp值均初始化为-inf,因为此时只有容量为0的背包可以在什么也不装情况下被“恰好装满”,其它容量的背包初始均没有合法的解,应该被初始化为-inf。

2 求方案总数

除了在给定每个物品的价值后求可得到的最大价值外,还有一类问题是问装满背包或将背包装至某一指定容量的方案总数。对于这类问题,需要将状态转移方程中的 max 改成 sum ,大体思路是不变的。例如若每件物品均是完全背包中的物品,转移方程即为

1
dp[i][j] = sum(dp[i−1][j], dp[i][j−w[i]]) // j >= w[i]
3 二维背包

前面讨论的背包容量都是一个量:重量。二维背包问题是指每个背包有两个限制条件(比如重量和体积限制),选择物品必须要满足这两个条件。此类问题的解法和一维背包问题不同就是dp数组要多开一维,其他和一维背包完全一样,例如5.4节。

4 求最优方案

一般而言,背包问题是要求一个最优值,如果要求输出这个最优值的方案,可以参照一般动态规划问题输出方案的方法:记录下每个状态的最优值是由哪一个策略推出来的,这样便可根据这条策略找到上一个状态,从上一个状态接着向前推即可。

以01背包为例,我们可以再用一个数组G[i][j]来记录方案,设 G[i][j] = 0表示计算 dp[i][j] 的值时是采用了max中的前一项(也即dp[i−1][j]),G[i][j] = 1 表示采用了方程的后一项。即分别表示了两种策略: 未装入第 i 个物品及装了第 i 个物品。其实我们也可以直接从求好的dp[i][j]反推方案:若 dp[i][j] = dp[i−1][j] 说明未选第i个物品,反之说明选了。

Leetcode相关练习题

0 - 1 背包问题:416. 分割等和子集

题目给定一个只包含正整数的非空数组。问是否可以将这个数组分割成两个子集,使得两个子集的元素和相等。

由于所有元素的和sum已知,所以两个子集的和都应该是sum/2(所以前提是sum不能是奇数),即题目转换成从这个数组里面选取一些元素使这些元素和为sum/2。如果我们将所有元素的值看做是物品的重量,每件物品价值都为1,所以这就是一个恰好装满的01背包问题。

我们定义空间优化后的状态数组dp,由于是恰好装满,所以应该将dp[0]初始化为0而将其他全部初始化为INT_MIN,然后按照类似1.2节的伪代码更新dp:

1
2
3
4
5
6
int capacity = sum / 2;
vector<int>dp(capacity + 1, INT_MIN);
dp[0] = 0;
for(int i = 1; i <= n; i++)
for(int j = capacity; j >= nums[i-1]; j--)
dp[j] = max(dp[j], 1 + dp[j - nums[i-1]]);

更新完毕后,如果dp[sum/2]大于0说明满足题意。

由于此题最后求的是能不能进行划分,所以dp的每个元素定义成bool型就可以了,然后将dp[0]初始为true其他初始化为false,而转移方程就应该是用或操作而不是max操作。完整代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Solution:
def canPartition(self, nums: List[int]) -> bool:
n = len(nums)
s = sum(nums)

if s % 2:
return False

capacity = s // 2
dp = [False] * (capacity + 1)
dp[0] = True

for i in range(1, n+1):
for j in range(capacity, nums[i-1]-1, -1):
dp[j] = dp[j] or dp[j - nums[i-1]]

return dp[capacity]

完全背包问题:322. 零钱兑换

题目给定一个价值amount和一些面值,假设每个面值的硬币数都是无限的,问我们最少能用几个硬币组成给定的价值。

如果我们将面值看作是物品,面值金额看成是物品的重量,每件物品的价值均为1,这样此题就是是一个恰好装满的完全背包问题了。不过这里不是求最多装入多少物品而是求最少,我们只需要将2.2节的转态转移方程中的max改成min即可,又由于是恰好装满,所以除了dp[0],其他都应初始化为INT_MAX。完整代码如下:

1
2
3
4
5
6
7
8
9
class Solution:
def coinChange(self, coins: List[int], amount: int) -> int:
dp = [float('inf')] * (amount + 1)
dp[0] = 0

for i in range(len(coins)):
for j in range(coins[i], amount+1):
dp[j] = min(dp[j], 1 + dp[j - coins[i]])
return dp[amount] if dp[amount] != float('inf') else -1

17. 最长上升子序列

1
2
3
4
5
6
7
8
9
10
11
class Solution:
def lengthOfLIS(self, nums: List[int]) -> int:
if not nums:
return 0
dp = []
for i in range(len(nums)):
dp.append(1)
for j in range(i):
if nums[i] > nums[j]:
dp[i] = max(dp[i], dp[j] + 1)
return max(dp)

贪心加二分优化:

1
2
3
4
5
6
7
8
9
10
11
class Solution:
def lengthOfLIS(self, nums: List[int]) -> int:
if not nums:
return 0
dp = []
for i in range(len(nums)):
dp.append(1)
for j in range(i):
if nums[i] > nums[j]:
dp[i] = max(dp[i], dp[j] + 1)
return max(dp)

18. 最长公共子序列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Solution:
def longestCommonSubsequence(self, text1: str, text2: str) -> int:
m = len(text1)
n = len(text2)

dp = [[0] * (n+1) for _ in range(m+1)]

for i in range(1, m+1):
for j in range(1, n+1):
if text1[i-1] == text2[j-1]:
dp[i][j] = dp[i-1][j-1] + 1
else:
dp[i][j] = max(dp[i-1][j], dp[i][j-1])

return dp[-1][-1]

字符串

26. KMP 字符串匹配

练习题 https://leetcode.com/problems/implement-strstr/

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# Python program for KMP Algorithm
def KMPSearch(pat, txt):
M = len(pat)
N = len(txt)

if M == 0:
return 0

# create lps[] that will hold the longest prefix suffix
# values for pattern
lps = [0] * M
j = 0 # index for pat[]

# Preprocess the pattern (calculate lps[] array)
computeLPSArray(pat, M, lps)

i = 0 # index for txt[]
while i < N:
if pat[j] == txt[i]:
i += 1
j += 1

if j == M:
# print("Found pattern at index " + str(i-j))
return i - j
j = lps[j - 1]

# mismatch after j matches
elif i < N and pat[j] != txt[i]:
# Do not match lps[0..lps[j-1]] characters,
# they will match anyway
if j != 0:
j = lps[j - 1]
else:
i += 1
return -1

def computeLPSArray(pat, M, lps):
len = 0 # length of the previous longest prefix suffix

lps[0] # lps[0] is always 0
i = 1

# the loop calculates lps[i] for i = 1 to M-1
while i < M:
if pat[i] == pat[len]:
len += 1
lps[i] = len
i += 1
else:
# This is tricky. Consider the example.
# AAACAAAA and i = 7. The idea is similar
# to search step.
if len != 0:
len = lps[len - 1]

# Also, note that we do not increment i here
else:
lps[i] = 0
i += 1


txt = "ABABDABACDABABCABAB"
pat = "ABABCABAB"
print(KMPSearch(pat, txt))

# This code is contributed by Bhavya Jain

27. 字典树

练习题:https://leetcode.com/problems/implement-trie-prefix-tree/

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class TrieNode:
# Initialize your data structure here.
def __init__(self):
self.children = collections.defaultdict(TrieNode)
self.is_word = False

class Trie:

def __init__(self):
self.root = TrieNode()

def insert(self, word):
current = self.root
for letter in word:
current = current.children[letter]
current.is_word = True

def search(self, word):
current = self.root
for letter in word:
current = current.children.get(letter)
if current is None:
return False
return current.is_word

def startsWith(self, prefix):
current = self.root
for letter in prefix:
current = current.children.get(letter)
if current is None:
return False
return True

区间查询

29. 线段树

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
class Node:
def __init__(self, start, end):
self.start = start
self.end = end
self.total = 0
self.left = None
self.right = None


class NumArray:
def __init__(self, nums: List[int]):
# helper function to create a segment tree
def create_tree(left, right, nums):
if left > right:
return None

if left == right:
node = Node(left, right)
node.total = nums[left]
return node

mid = (left + right) // 2

node = Node(left, right)

node.left = create_tree(left, mid, nums)
node.right = create_tree(mid + 1, right, nums)

node.total = node.left.total + node.right.total

return node

self.root = create_tree(0, len(nums) - 1, nums)

def update(self, i: int, val: int) -> None:

def update_tree(root, i, val):

if root.start == root.end and root.start == i:
root.total = val
return

mid = (root.start + root.end) // 2

if i <= mid:
update_tree(root.left, i, val)

else:
update_tree(root.right, i, val)

root.total = root.left.total + root.right.total

update_tree(self.root, i, val)

def sumRange(self, i: int, j: int) -> int:

def get_sum(root, i, j):

if root.start == i and root.end == j:
return root.total

mid = (root.start + root.end) // 2

# in left tree
if j <= mid:
return get_sum(root.left, i, j)
elif mid < i:
return get_sum(root.right, i, j)
else:
l_s = get_sum(root.left, i, mid)
r_s = get_sum(root.right, mid + 1, j)
return l_s + r_s

return get_sum(self.root, i, j)

30. 树状数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class FenwickTree:
def __init__(self, n):
self.sums_ = [0] * (n + 1)

def update(self, i, delta):
while i < len(self.sums_):
self.sums_[i] += delta
i += self.lowbit(i)

def query(self, i):
sum_ = 0
while i > 0:
sum_ += self.sums_[i]
i -= self.lowbit(i)
return sum_

def lowbit(self, x):
return x & (-x)

hexo博客迁移

发表于 2021-02-05 | 分类于 网站建设
字数统计: 501 | 阅读时长 ≈ 1

前言

本人因做毕设需要购入新电脑一天,几乎所有开发工作都已迁移至新设备,独缺个人博客,趁寒假之际更新一下N久没有维护的个站。

在网上搜索的时候,看到有人利用git的branch,来实现多台设备同时能够维护博客,对我来说个人博客属于私人性质的东西,没有分布式更新的需求,因此本文所用方法只适合更换一台设备写博客,较为简单粗暴。

配置基础环境

要配置基础环境,需要做以下几个步骤

  1. 安装git,并生成密钥,保存到github账号中
  2. 下载并安装Node.js(npm会自己跟着装好) 注意:nodejs 14 与 nodejs 12不完全兼容,本人是在nodejs 12 中建设网站的,转移到14时会有报错,具体解决方法见官方答疑
  3. 使用npm安装hexo ,具体指令为npm install -g hexo-cli

具体细节可以参照 建站过程整理这篇文章。

❗️ 注意,安装完hexo之后不用hexo init

迁移相关文件

需要迁移的文件只有:

  1. 博客配置文件./_config.yml
  2. 主题配置文件夹./theme/
  3. 文章及相关内容的文件夹./source/
  4. 模板文件夹./scaffolds/
  5. 记录博客所有的插件的文件./package.json

在新电脑中重新部署

在目录下博客主目录下运行以下命令,会自动读取./package.json的配置,完成相关环境的安装

1
npm install

和之前相同,修改文章,生成静态文件,部署到github

1
2
hexo g
hexo d

部署时的问题

当设备完全没有操作过git时,直接部署博客会报错:

此时是还没有将github主机的key添加到本地,可以临时从github中拉一个repo,过程中会提示添加github的key到本地:

参考资料:

https://swayye.xyz/2020/01/10/hexo%E5%8D%9A%E5%AE%A2%E8%BF%81%E7%A7%BB/

投稿经验总结

发表于 2019-12-16 | 分类于 生而为人
字数统计: 1.3k | 阅读时长 ≈ 4

2020 www 投稿复盘:

这篇文章经历非常坎坷,从2019年春节时开始准备,先是投了ijcai, 5月份出结果被拒,8月份修改了大实验,增加了验证性实验,投稿Ubicomp,以内容和普适计算领域无关被desk reject,最后修改一轮在10月份投了www。

在这个漫长的战线中,其实真正修改文章、补充实验的时间不到50%,大量的时间被耗费在等待导师意见的过程中。为了提高投稿的效率,可以有两种选择 1. 先斩后奏,看到合适的期刊先投着再说,至少能返回一些review意见。 2. 在等待过程开启新的课题,流水线作业,以量取胜。除此之外的经验教训是,导师不一定了解你做的工作,对你要投的track是否合适心里不一定有底,这次ubicomp desk reject 的理由就是与track无关, 有些主意一定得自己拿。

今年www首次设立rebuttal环节, 于12.12日收到review意见,相比于稿件数量爆炸性增长的某些顶会,www的reviewer 明显阅读稿件时更加认真,给出的意见也更加中肯,我也从review意见中收获良多,下面主要展示一些不足之处。

Review1 (strong reject):

  • 与DeepCrime 过于相似。 这是非常无奈的一点,学术文章也是手快有,手慢无。idea出来发现已经被人发表了,心情就和吃屎一样,只能在他的基础上增加补充实验。 所以,写文章尽量立意新,方法新,这样在效果上就有一定的转圜余地。
  • 文章和track相关性不高。
  • 实验结果的重要性阐述。 MAE 提升的0.1个百分点对现实有何意义? 这个问题确实难回答,regression的预测指标不像分类问题那么直观。

Review2 (reject):

分段给出了评价。

  • 拼写、语法、用词的错误,writing skill 还需再提升一下。
  • 针对问题定义章节, 数据清洗方法未阐明,word2vec embedding 在少量天气条件下是否合理,输入输出不明确,对三类模型是否单独训练,每一层的数学表述没有必要,缺少模型参数的说明。 这里有的问题我在文章中是确实写明了,如输入输出,也给出了数学定义,对维度也做出了说明。如果review没有理解的话,推测依然还是写作的问题。
  • 针对 evaluation部分, training / testing split 分割 需要对季节性差异做出解释(因为我只用了一年的时间,分为前3/4 和 后1/4);错误率的提高是否适用于真实场景;赞扬对于网络不同结构的效用研究,补充了建议,增加对模型参数数量的考量,比如,引入attention可能并非是attention本身的作用,也有可能仅仅是参数的增加带来的效用?
  • 结论部分。建议增加一些讨论, 现实应用对预测的要求如何?未来的工作?evaluation的局限性?纽约可以代表其他城市么?如果犯罪分子提前知道了预测结果,而改变他们的行为,这种情况该如何处理?

Review3(reject):

  • 缺少技术创新性,仅仅是结合了几种现存的方法。
  • 技术细节缺少,如,demographic 特征的选择缺少理由;没有利用到poi上的check-in数据,poi上的语义信息;使用word2vec embed 天气情况的理由;SOTA方法用的不够多;baseline 里有用到 dynamic spatial pattern,但是paper里只用了static, 缺乏解释;
  • 对中小型城市的效果如何? community的具体定义,是Public Use Microdata Areas (PUMAs)么? community对于实际应用来说依然太大?

自我检讨部分:这篇文章其实先天上就不足,属于赶鸭子上架的作品,作为毕业设计的延伸,我当时在完成毕设后完全没有发文章的念头,划水了大半学期,随后在导师和师姐的鼓励下,于2019年春节前后匆忙投出一稿,果然白给。在写作过程中,因为看到了很多比较水的文章,导致自己心态有点膨胀,对顶会甚至生出一股轻视之意,从而忽略了对遣词造句的精益求精。更糟糕的是,我在整个研究目标设计上没有明确的框架规划,前期调查中也没有阅读足够的相关文章,没有明确问题的输出以及评价指标,没有考虑训练的细节,没有提前预判合理的特征分析,导致在后面的特征提取、实验设计、baseline选取时不得不根据实验结果临时修修补补,导致整篇文章逻辑相对混乱,脉络不甚清晰。总而言之,这篇文章虽然带给我极大的挫败感,甚至归宿也未知(我甚至都不想理它),但也让我实践了一个科研的完整流程,对接下来的研究工作应该也具备奠基意义吧。

面试经验总结

发表于 2019-12-11 | 分类于 生而为人
字数统计: 512 | 阅读时长 ≈ 1

时间: 2019.12.06

事件:微软冬季实习生面试两轮

状态:刚刷完Leetcode Top like 100, 还未针对面试准备过,抱着试试水的心态

结果:凉凉

第一轮:

  • 英文自我介绍
  • 项目经历简单介绍,问题:你从中遇到最大的挑战? (最好能提前准备,言之有理即可)
  • 白板编程。写程序检测是否是合法的ip地址,并将ip地址转换为一个int型整数。要求:检测非法字符,每个字符只读一遍。这些要求是在和面试官的沟通中一步步地明确的,所以不要一上来就闷头写代码,可以先给面试官讲一下思路。
  • 结尾提问环节,尽量提前准备,最好可以问一些和业务有关的问题,展现你对申请岗位的热情。

第二轮:

  • 英文自我介绍
  • 网络基础知识 TCP, UDP 区别, 适用场景。五层网络协议模型。(没准备,答得很差)
  • C++基础知识。 多态实现,运行时堆栈存储的数据,全局变量存放位置。(凭印象答,被面试官说答得不规范)
  • 白板编程: 比较两个字符串版本大小,题目简单, 注意不要用字符串直接比较大小,先转换为int
  • 工程题:设计一个数据结构,高效地检索ip地址。我只想到了把ip先转换为整数,用hash表存。但是空间效率太差,面试官最后公布答案,可以用平衡二叉树,最优解是字典树。(到这里其实面试时间有点超时了,我自己心态也有点崩,非常失败的收尾)

本次面试总结:

  • 对面试的准备不够。 应对措施:买了一本《剑指offer》,全面了解一下面试的流程以及面试官的心理。

  • 第一次经历这种形式的技术面,心态上hold不住。下个阶段多投中小厂锻炼一下。

    ​

12
Fannngxun

Fannngxun

以无法为有法,以无限为有限

20 日志
11 分类
17 标签
邮箱 知乎
© 2021 Fannngxun | Site words total count: 31.8k
由 Hexo 强力驱动
人次 次
0%