眨眼功夫,日历又翻完一本。这一年我成功把“人生地不熟”升级成“半熟不熟”:新环境从“找不着北”到“闭眼能摸厕所”,新朋友从“网友奔现”到“能借到充电宝”,新故事从“狗血八点档”到“温水煮青蛙”。总结一句话——没翻车,就是胜利!

去年复盘

健康计划

上半年执行了严格的体重管理方案,体重稳定控制在 76–78 kg,尿酸值亦维持在正常范围。

  • 指标监测:每周固定测量体重与尿酸,数据可视化追踪。
  • 运动记录:周一到周四晚间进行 2 h 功能性力量训练,周期化递进负荷。
  • 饮食策略:曾因刻意降低脂肪摄入导致免疫力下降;现已调整为训练前后及时补充优质脂肪与碳水,确保恢复与稳态。

提升学历和专业度

基于“就业优先”的考量,综合评估读研所需的高昂学费与机会成本后,我决定以技能提升为主线。借助 AI 辅助的职业画像分析,将目标锁定为 AI 架构师——需同时掌握云平台、模型训练与运维,以及云原生应用架构设计能力。为此,我系统深耕了 Kubernetes 与 GitOps 体系,并持续跟进模型构建技术,目前已进入 Agent 开发模式的实战阶段。

  • MEM管理硕士: 上海交大(待定)
  • 行业专家: Google开发者专家,微软专家(未开始)
  • 云和AI领域认证专家: 阿里云,亚马逊云,AI??(进行中)

收入多元化

这一年在投资方面投入了不少心血,包括股票和加密方面的实践,看了不少投资方面的书,总体取得了一定成绩,但认知上还缺乏,选股能力需要提升。A股收益率23个点,美股8个点,加密50个点,每月定投黄金也有40个点左右的收益。

  • 副业
  • 金融投资

教育规划

小朋友今年报了很多兴趣班,包括口才,钢琴,跆拳道,舞蹈和写字,在线学习思维、英语还有认字。这个年龄阶段主要想多发掘小朋友爱好为主,同时维即将到来的小学打好基础。

  • 学前教育规划

养老规划

对未来的不确定性的担忧,今年大部分收入存起来了,少部分拿去了投资。

  • 储蓄(增加30%)
  • 个人养老金(放弃)
  • 商业养老金保险(放弃)

读了哪些书:

主要看了提升技术专业能力,身体健康和投资能力方向的书籍

  • Agentic模式
  • 肌肉强化训练
  • 强化学习前沿算法和应用
  • 交易大师
  • 投资中最重要的事
  • 从0开始构建大模型
  • 估值原理
  • 膝盖康复
  • GitOps
  • 如何选择成长股

播客

独居上海,碎片时间被播客高效填满。通勤、午休与睡前,我系统订阅了科技、商业、投资、教育及健康类节目,通过高密度信息输入持续更新认知框架,显著提升了知识获取的 ROI。

其他

今年是我在 eBay 工作的第一个年头。我很庆幸能与同事们建立起融洽的关系,并逐渐适应了从“特种兵”式的快节奏、高强度工作,到“大教堂”般精雕细琢、追求极致的模式转变。
然而,美国14117法案的颁布,给公司带来了翻天覆地的变化。随着环境的彻底隔离和部分运营能力的剥离,中国 CCOE 不可避免地被边缘化,我们的工作愈发需要依赖日本和美国同事的支持与协作。这场变革也波及到了我身边的一些同事,他们不幸被裁员,而我则幸运地留了下来。
尽管如此,我深知未来的不确定性依然巨大。公司项目技术范畴的收敛、工作职能的持续剥离,以及公司整体实力的弱化,都让我意识到自己随时可能面临被“优化”的风险。因此,我必须未雨绸缪,提前做好更充分的准备和更长远的规划。

今年计划

去年没做到位的地方 & 改进方案

  • 职业规划与提升: 职业规划推进缓慢,执行力时有中断。为系统性地朝AI架构师方向发展,我将制定并执行以下证书考取与能力建设计划:

    目标:获取1-2张高含金量的AI架构师认证

    根据调研,当前业界认可度较高的认证主要分为两类:国际云服务商认证和国内头部企业认证。 4 5 考虑到AI架构师需深度结合云平台进行设计与部署,云服务商的认证尤为重要。 1

    首选认证(建议选择其一):

    1. AWS Certified Machine Learning – Specialty:全球认可度极高,专注于在AWS云上设计、实施、部署和维护机器学习解决方案,非常贴合架构师的定位。 1 4
    2. Microsoft Certified: Azure AI Engineer Associate:同为顶级云厂商认证,专注于使用Azure认知服务、Azure机器学习等工具来构建和部署AI解决方案,含金量与AWS相当。 1 3

    备选认证(作为补充或国内发展重点):

    • 华为认证HCIE-AI:在国内企业中认可度很高,如果未来职业发展重心在国内,这是一个极佳的选择。 2 4
    • 阿里云人工智能工程师认证(ACP):适合使用阿里云平台的技术人员,在国内市场同样具备较强竞争力。 3

    季度执行计划 (OKR):

    • Q1: 选型与基础准备

      • O: 完成AI架构师认证的选型与基础知识储备。
      • KR1: 确定主攻认证(从AWS/Azure中二选一),并报名官方或权威机构的培训课程。
      • KR2: 完成至少50%的课程学习,并系统梳理知识体系。
      • KR3: 完成至少2个基于该云平台的AI服务实践项目(Lab)。
    • Q2: 冲刺与考取认证

      • O: 成功通过认证考试。
      • KR1: 完成所有课程学习,并将模拟题得分稳定在85%以上。
      • KR2: 预约并参加官方认证考试。
      • KR3: 获得认证证书,并更新个人职业档案。
    • Q3 & Q4: 实践与深化

      • O: 将所学知识应用于实际工作,并探索第二个认证的可能。
      • KR1: 在公司内部寻找或创造一个应用场景,实践认证所学的架构设计能力。
      • KR2: 对学习过程进行复盘,并开始规划第二张证书(如HCIE-AI或另一个云厂商认证)的学习路径。

新目标 & 落地路径

  • 投资规划
    • 2025 年目标投资收益20%,主要投资于中国本土的科技公司和有中国影响力的公司。
    • 投资策略
      • 抓主要矛盾,跟随聪敏钱,做预期可控的投资,
      • 美股杠铃策略,A股做短线为主,定投创业板/科创板指数为辅
      • 关注稳定币,RWA 是个不错的选择,长期看是个不错的投资方向
  • 副业先开 1–3 个“试验田”,不图马上赚钱,但要跑通闭环。选人一生都能做的赛道,市场别太小,启动成本可控;小步快跑,三个月内验证,不行就换。
    • 每天看一个独立开发的案例,边看边做。
    • 每周写一篇独立博客。
    • 每半个月到20天看一本书

新目标 & 落地路径

  • 把下班后的第一时间留给家人,把周末的一天留给孩子的兴趣班。家才是所有计划的终点,也是重新出发的起点。
  • 持续监控体重,记录每天的饮食和运动数据。
  • 持续关注和学习新的技术和知识,保持专业技能的更新。

2024总结

2024年是比较波折的一年,经历了很多的事情,尤其是健康和工作方面遭受了前所未有的挑战,如何管理健康和工作稳定性变得尤为重要。

工作

24年6月中公司出现财务危机,不得不开始寻找新的工作机会,由于之前基本处于躺平的状态,没有提前做好准备。在花了一个月时间不断地修改简历和投简历,但是鲜有公司回应,机会也是稍纵即逝,抑或是人太多需要排队等面试。这是在之前找工作的过程中从未遇到过的情况,这一个月心情一直比较压抑,压力很大,不知道如何才能解脱。好在第二个月,通过知乎认识了一个美团的前端架构师,人非常的nice,愿意花时间和我电话沟通,我和他通话长达1个多小时,开始重新审视自己的优劣势,简历也重新改版,经过半个月的调整期,在第二个月有得到几个面试机会,最终经过五轮面试进入一家外企,工作地点也变成了上海。

找工作这件事情让我充分认识到需要抬头看路,例如经济的大环境,行业竞争格局,科技变革等对职业会产生影响的方面。另外一个是一定要有忧患意识,准备好Plan B。如果等到遇到问题了才去想办法会很慌张,并且很难在短期内得到好的结果。

健康

健康这件事之前也并未在意,自从有一段时间接了一个朋友的活,养成了熬夜工作的习惯,我总是想如果晚上在家人和孩子睡觉之后,我便可以有些时间来思考自己想做的事情,于是不知不觉很难早睡。饮食方面也从未注意,暴饮暴食,吃肉喝酒样样来,有句老话,everything has a price,当你做这些事的时候,就总有一天要付出相应的代价。5月17号,我大脚趾肿痛,无法走路,无法睡觉,连续几个夜晚,后来通过查阅资料和看医生,知道是痛风发作,之前从未经历过这种疼痛,虽然内心不想承认这个疾病,但还是很无奈。其实在发作之前身体已经给过很多次信号,例如脚踝和脚侧边小骨头在早晨睡起来之后痛,几天不好。还有尿酸指数一直在500多,这些也从没在意。经历过才知道,教训是最好的老师。

家里老人做了2次手术,除去老人的农保,自费和自付部分一共超过2万。虽然已经给老人配置了苏惠保,但是需要大病才能报,买的时候没有关注保险细节条款,以为都可以报销,也很无奈。不过大病医疗也算是对重大疾病有个保障,所以决定还是继续保上。

支出

24年提前还了房贷,增加了消费贷,每月还款额度比之前多了4000多。孩子教育每年加起来有2w5左右,课外一万多。家里的保险支出一年2万多。每月日常开销差不多6000。这里面大部分是固定支出,平均每月达到2-3w。如何能够持续保持生活质量是一个挑战。

2025规划

2025年来的很快,今天是第一天,恍惚间感觉还在2024年的日子里,可惜这是不可能的,任何人和事物在时间面前都会显得苍白无力。如何更好的利用好时间是需要认真考虑的问题,什么事情重要,什么该花时间什么不该花时间需要有清晰的认知。

健康计划

健康是第一要务,去年在床上躺了2个月,无法办公无法活动,人生突然失去了很多意义。健康计划包含身体和心理的健康,身体从吃的方面持续改进,目标是维持低尿酸,保持充沛的精力。心理上需要多一些关注家人,维持好感情,在精神上达到共鸣。

  • 指标监测
  • 运动记录
  • 饮食

提升学历和专业度

在去年找工作的时候,学历是一个硬伤,很多工作计划需要:985/211,年轻不超过35,有大厂经验,能加班。在快到40的年纪,能够提高的主要还是学历了,做好能做的,其他的坦然面对。

  • 计算机管理硕士: 上海交大
  • 行业专家: Google开发者专家,微软专家
  • 云和AI领域认证专家: 阿里云,亚马逊云,AI??

收入多元化

平台给了我机会,如果失去了平台啥也不是。我不想一直处在这种风险中,今年一定要探索出一条自己第二曲线,哪怕只挣一块钱也行,这样至少在风吹草动的时候能多一个选择,如果有机会能以此安生立命一定要抓住。

  • 副业
  • 金融投资

教育规划

孩子越来越大,一直在思考我能给孩子带来什么,什么是最重要的。目前主要是妈妈在考虑孩子看什么书学习什么知识,很感谢妈妈的付出。我想今年争取提高下孩子自主学习的能力,能够自己去发现一个问题并解决,然后可以举一反三的利用。

  • 学前教育规划

养老规划

快要40岁,还没有多少存款,未来如果失去工作计划,该如何有足够的钱给孩子上学,接受最好的教育?如何哉外来退休的时候保证生活质量?如果在父母养老的时候给予足够的帮助?需要思考下,在不同时间节点的消费支出会有多少,为了能保证这个支出我在当下需要做些什么:储蓄,买养老保险,金融理财。这些不同的方式方法有哪些风险,我能不能接受,需要在第一季度自己思考清楚,并和家人商量。

  • 储蓄
  • 个人养老金
  • 商业养老金保险

2025,我已来,希望明年的这个时候我可以很自豪的告诉今天的自己:这一年你很棒,一切都好!

过去的一年因为疫情的原因, 没有什么机会出门,直到年底才突然放开,刚想出去看看又生病了。回首这一年过的太快太快, 有很多想做的事情还没来得及做,没有能够实现,新的一年需要更好的计划起来。

工作上我做了什么? 我学到了什么?

似乎一直忙忙碌碌,但是没有什么成果,不知道问题到底出在哪里? 这一年我主要的规划是在工作之余能够尽可能的学习新的技术和创业方向, 我主要的做法是项目驱动,当有朋友需要合作开发一个小的产品时候,边做边学,并尽可能的深入进去。

这一年首先学习了NLP处理法律文书,接着是3D游戏开发,学习大量的WEBGL知识。在下半年主要学习了Web3的相关技术,包括合约的开发部署测试,实现DeFI,NFT,Web页面。在年底的时候重新学习网络安全,希望能在这个领域有些创新和实践。

看了不少技术,但是还是没有足够的深入,一方面没有足够的时间,另一方面一直在变,最重要的是一直没有商业化的方向。

家庭中我做了哪些? 有哪些不足和好的

这一年一直和家人呆在一起, 我的计划是每天至少有一个小时陪女儿,基本也做到了。但是还远远不够,女儿现在求知欲很强,有很多的新东西教一遍就能学会,可塑性非常强,但是我还没有充分思考怎么教育女儿,在不同的年龄段如何规划。另外今年家人都有生病,每次看医生都花费很多时间和金钱。

虽然我一直坚持锻炼, 但是身体还是有各种各样的小毛病,主要是关节痛发生了好几次。

理财方面?

今年基金惨淡,很多亏损,所有没有全仓买入。好在今年炒股有一些心得,虽然交了些学费,但基本掌握好一定的节奏了, 希望新的一年能够严格执行。

新的一年我的期望是什么样的?我应该做什么?

继续寻找合适的创业方向,记录下来,最多5个, 选择一个最重要的坚持实践,尝试打开商业方向,必要的时候投入一些资金。
看合适的书,不能太过发散, 需要聚焦,把有限的精力放在定好的方向上。
为女儿制定合适的学习计划,半年到一年动态调整,把握住女儿成长的黄金期。
继续学习理财,找出能够稳定收益的方法, 至少年化收益跑赢银行的5年期利息。
配置合适的保险, 重疾险有限看友邦的,主要考虑未来如果生病能够有一些补偿,不影响家庭生活质量

生活就像是巧克力,你永远不知道下一颗是什么颜色。

作为技术人,你的竞争力来源于你的初心,专注和坚持不懈的努力

8月初公司法人出了一些问题,导致公司投资人撤资,不得已在这个尴尬的季节出来看新的机会。

到今年7月, 我就满10年工作经验了, 在这10年中根据工作的需要, 参与过多年前端/后端软件的开发,也主导过系统从0到1的架构,同时也积累了一些管理上的经验,团队合作。在这10年中早期的两年我便找准了工作的方向: 前端工程师。 在之后的5年, 凭借着对前端极高的兴趣和技术专研精神, 从trident的时代到webkit时代,从jQuery到三大框架(React/AnguarJS/VueJS), 从VanillaJS到CoffeeScript到TypeScript到DSL,
一直紧跟技术的发展,没有掉队。16年出来创业做智能家居,坚持了两年,因为各种原因放弃了, 但过程中收获了技术和业务整合的能力, 懂得了从市场的角度思考问题。
18年开始做技术管理和技术架构相关工作,因为工作需要,学习了大数据架构ETL技术,实现了NLP处理广告的技术,同时在API的实现方面, 整合了SpringBoot和Zuul, 实现阿里和滴滴等客户通过接口稳定的调用。

苏州市场上目前有以下几类公司:

  • 上市公司, 代表: 华为,微软
  • 诞生在本地的大企业, 代表: 同程旅游,智慧芽,食行生鲜, 启信宝等
  • 外企, 代表: 微软,EPAM,艺能软件等
  • 新兴创业公司: Momenta/PlusAI(自动驾驶),Atman(医疗AI),金数数据等

对于第一类公司,主要考察的是算法和数据结构能力,需要候选人掌握最基本的排序和搜索算法, 如果要通过需要足够的算法经验,至少在Letcode或者TopCoder上刷满100题。 这类公司一般待遇在市场上比较优渥,聚集了很多牛人,你可以专心的做技术, 但是因为职位固化, 上升的空间不会很大。

第二类公司, 主要还是看应聘公司同时期项目的需求,主要考察的是候选人项目开发能力,采取的什么技术,解决了什么样的问题,面对了多大的挑战等等,需要实实在在的解决问题的能力, 如果要进入这类公司,需要候选人在某个垂直的技术领域比较精通。这类公司有一定的上升空间, 但不排除内部勾心斗角问题,据我所知同程内部拉帮结派现象严重, 这时候你要进升是不可能的事情

第三类公司,主要考察的是技术能力和英语能力,有很多人倒在了英语这个环节。这类公司相对来说职位也比较固定,但是可以享受到欧美弹性的工作环境和多元的企业文化, 有很多针对员工的培训也会很有用,例如:EPAM会定期安排员工的英语培训课程

第四类公司,主要考察候选人多方面的能力,前后端技术,框架工程能力,吃苦耐劳的能力等。这类公司一般有较大的上升空间, 同时有机会放大自己的价值,得到优厚的回报,但是稳定性会差一些。

每个类别我都面试了一些公司,和每家公司的交流都能学习到不少东西, 和微软的面试中我发现自己的算法能力还是不足的,可以在后续重点加强。在和智慧牙的沟通中, 我面试的是技术架构师一职,我的技术全面性得到了对方的认可, 但是他们还是希望找一个垂直领域的专家,这点在JD上有所误导, 所以在应聘之前一定要先沟通清楚职位的描述是否准确, 否则会花费大量时间做无用功。在和EPAM接触的过程中,我面试的是JavaScript技术专家,和面试官聊了很多技术问题,技术方面也得到了对方的认可, 但是在英语方面他们希望可以找一位Native能力的候选人, 这也是很无语的事情,同时他们要找的人准确的说ReactJS技术专家。Atman是一家做机器翻译的公司,我面试的是前端架构师, 面试中沟通了项目从0到1的解决方案,团队管理问题,因为和我的技术栈非常的契合,聊的非常愉快,也很快发了Offer。

还有其他一些公司,面试中聊了各种奇葩和八卦的问题,或者聊完了说这个职位没有了, 等等各种各样的情况。所以在面试前一定要沟通清楚职位的情况, JD描述是否准确,否则会浪费很多时间。

我个人比较倾向的是第三类和第四类公司, 同时根据自己的特长我判断自己适合做垂直领域的技术专家相关的工作, 也很喜欢创业公司的氛围。总体而言,领域专家是最受欢迎的。

你的身价取决于地域和稀缺性

  我大概统计了一下, 对于一个高级开发人员, 相同的一份工作, 在一线城市的收入要比在二线城市高出5000左右。
  另外,可替代性比较强的工作薪资水平必然会低
  • 地域因素

    • 公司竞争激烈,要找合适的人才就要能给出匹配的薪酬;
    • 市场经济规模大,职工的薪酬平均水平高,薪资自然也就水涨船高;
  • 稀缺性

    • 是否是领域专家
    • 所在地是否这方面人才少
    • 是否一专多能, 别人有的我更好, 别人没有的我有
    • 是否是管理层

关于大龄和创业

网上很多人说35岁是开发人员的分水岭, 后续要开始走下坡路, 这点我不是很赞同。

讲一个我在面试中遇到的两家创业公司, 两家公司的创始人都是40岁左右,其中一个CEO是二次创业,第一次创业已经取得了成功,但还是坚决出来做其他方面的创业, 对他而言还有很多有价值的事情等着去做,希望可以通过数据分析来解决商业营销方面的问题。 另外一家做AI机器翻译的, 团队有很多微软研究院出来的,他们希望通过机器翻译技术发现潜在的医药规律,降低新药的研发周期和成本, 市场价值巨大。

另外, 从目前网上所Open出来的职位来看, 架构师和技术总监相关的职位还是有不少的,对开发的要求大部分是5-10年工作经验,如果技术能力够强,完全可以应聘

如果你工作之余有足够的精力, 可以考虑成为一个斜杠青年。

斜杠青年指的是一群不再满足“专一职业”的生活方式,而选择拥有多重职业和身份的多元生活的人群。

在找工作期间, 我也考虑过创业, 并且对行业做了很认真的调研。 少儿编程教育是我比较看好的一个市场, 虽然市场规模不大,只有40亿左右, 但目前市面上做的好的公司还比较少, 如果找准切入点, 还是很有机会的。 后续我会继续跟进行业进展, 写更多的关于这方面的文章。

结语

如果不是CEO出现了问题, 我肯定不会出来尝试新的机会, 也就不会发现自身很多问题。 虽然失去了一些时间和金钱, 但是得到了一些可以喘息和思考的机会, 同时我有更多的时间陪伴家人了, 重新认清了接下来的发展方向,那就是一定要成为某领域的专家, 多增加自己的稀缺性, 如果有机会和时间, 有一份自己的事业最好。

生活和工作两者很难平衡,现在人的压力都大, 没有良好的心态很难在遇到一些困难事情的时候调整过来,容易失去信心。得与失之间,都是公平的,当你失去了一部分, 你也必然会在其他方面会有所收获。

Stay hungry, stay foolish, stay thinking deeply!!!

栈(stack)又名堆栈,它是一种运算受限的线性表。 限定仅在表尾进行插入和删除操作的线性表。 这一端被称为栈顶,相对地,把另一端称为栈底。

栈的实现可以通过数组和链表来实现, 用数组实现的可以称为顺序栈, 用链表实现的成为链式栈

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
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
package stack

// 基于数组的栈实现
type ArrayStack struct {
data []string
length int
size int
}

func NewArrayStack(len int) *ArrayStack{
if len <0 {
return nil
}

return &ArrayStack{
data: make([]string, len),
length: len,
size: 0,
}
}

func (me *ArrayStack) push(data string) bool {
if me.length == me.size {
return false
}

me.data = append(me.data, data)

me.size++

return true
}

func (me *ArrayStack) pop() string{
if me.size == 0 {
return ""
}

item := me.data[me.size - 1]

me.size--

return item

}

// 基于链表的栈实现
type node struct {
next *node
val interface{}
}

type LinkedListStack struct {
top *node
}

func NewLinkedListStack() *LinkedListStack {
return &LinkedListStack{nil}
}

func (me LinkedListStack) IsEmpty() bool {
return me.top == nil
}

func (me *LinkedListStack) Push(data interface{}) bool {
nd := &node{val: data, next: nil}

if me.IsEmpty() {
me.top = nd
} else {
nd.next = me.top
me.top = nd.next
}

return true
}

func (me *LinkedListStack) Pop() interface{} {
if me.IsEmpty() {
return nil
}

result := me.top.val
me.top = me.top.next

return result
}

func (me *LinkedListStack) Top() interface{} {
if me.IsEmpty() {
return nil
}

return me.top.val
}

func (me *LinkedListStack) Print() {
if me.IsEmpty() {
return
} else {
curr := me.top

for nil != curr {
fmt.Println(curr.val)
curr = curr.next
}
}

}

本文讨论关于数组排序算法的实现, 给定一个整形数组,按从小到大排列

本节主要实现的排序算法有:希尔排序,计数排序,基数排序,桶排序,堆排序

希尔排序

希尔排序,也称递减增量排序算法,是插入排序的一种更高效的改进版本。希尔排序是非稳定排序算法。
希尔排序是基于插入排序的以下两点性质而提出改进方法的:
1. 插入排序在对几乎已经排好序的数据操作时,效率高,即可以达到线性排序的效率
2. 但插入排序一般来说是低效的,因为插入排序每次只能将数据移动一位
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
package sort

func ShellSort(arr []int, len int) {
if len <=1 {
return
}

times := 0

gap := len / 2

for gap >=1 {
j := 0

for i:=gap; i<len; i++ {
curr := arr[i]

// 对第i个元素以及之前相同的gap间距元素进行对比排序
for j = i - gap; j>=0 && curr < arr[j]; j -= gap {

times++
arr[j+gap] = arr[j]
}

arr[j + gap] = curr
}

gap /= 2
}

println("time O(", times, ")")
}

计数排序

计数排序是一个非基于比较的排序算法,该算法于1954年由 Harold H. Seward 提出。它的优势在于在对一定范围内的整数排序时,
它的复杂度为Ο(n+k)(其中k是整数的范围),快于任何比较排序算法。  当然这是一种牺牲空间换取时间的做法,而且当O(k)>O(n*log(n))的时候其效率反而不如基于比较的排序(基于比较的排序的时间复杂度在理论上的下限是O(n*log(n)), 如归并排序,堆排序)
计数排序的基本思想是对于给定的输入序列中的每一个元素x,确定该序列中值小于x的元素的个数(此处并非比较各元素的大小,而是通过
对元素值的计数和计数值的累加来确定)。一旦有了这个信息,就可以将x直接存放到最终的输出序列的正确位置上。例如,如果输入序列
中只有17个元素的值小于x的值,则x可以直接存放在输出序列的第18个位置上。当然,如果有多个元素具有相同的值时,我们不能将这些
元素放在输出序列的同一个位置上,因此,上述方案还要作适当的修改。
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
package sort

import "math"

func CountingSort(a []int, n int) {
if n <= 1 {
return
}

var times = 0

var max int = math.MinInt32
for i := range a {
if a[i] > max {
max = a[i]
}
times++
}

c := make([]int, max+1)
for i := range a {
c[a[i]]++
times++
}
for i := 1; i <= max; i++ {
c[i] += c[i-1]
times++
}

r := make([]int, n)
for i := range a {
index := c[a[i]] - 1
r[index] = a[i]
c[a[i]]--
times++
}

copy(a, r)

println("Time O(", times, ")")
}

基数排序

基数排序(radix sort)属于“分配式排序”(distribution sort),又称“桶子法”(bucket sort)或bin sort,顾名思义,它是透过键值的部份资讯,将要排序的元素分配至某些“桶”中,藉以达到排序的作用,基数排序法是属于稳定性的排序,其时间复杂度为O (nlog(r)m),其中r为所采取的基数,而m为堆数,在某些时候,基数排序法的效率高于其它的稳定性排序法。

基数排序要求基数的量不能太大, 否则做不到O(n)的时间复杂度
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
package sort

// 基数排序
func RadixSort(arr []int, len int) {
if len<=1 {
return
}

max := arr[0]

for i:=0; i< len; i++ {
if arr[i] > max {
max = arr[i]
}
}

for exp:=1; max/exp > 0; exp *= 10 {
countSort(arr, exp, len)
}
}


func countSort(arr []int, exp int, len int) {

c := make([]int, 10, 10)

for i := range arr {
c[arr[i]/exp % 10]++
}

for i := 1; i < cap(c); i++ {
c[i] += c[i-1]
}

r := make([]int, len, len)

for i := 0; i<len; i++ {
r[c[arr[i]/exp %10] -1] = arr[i]
c[arr[i]/exp %10]--
}

copy(arr, r)
}

桶排序

桶排序(Bucket sort)或所谓的箱排序,是一个排序算法,工作的原理是将数组分到有限数量的桶子里。 每个桶子再个别排序(有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排序)。 桶排序是鸽巢排序的一种归纳结果。 当要被排序的数组内的数值是均匀分配的时候,桶排序使用线性时间(Θ(n))

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
package sort

func getMax(arr []int) int {
max := 0

for i := range arr {
if i > max {
max = i
}
}

return max
}

func BucketSort(arr []int, arrLen int) {
if arrLen <= 1 {
return
}

max := getMax(arr)
buckets := make([][]int, arrLen / 10) // seperate to length/10 buckets

for i := 0; i < arrLen; i++ {
index := arr[i] * ( arrLen - 1 ) / (10 * max) // sperate data to different bucket

buckets[index] = append(buckets[index], arr[i])
}

datalen := 0 // mark current data processed

for j := 0; j < len(buckets); j++ {
bucLen := len(buckets[j])

if bucLen > 0 {
// use quicksort to sort every bucket
QuickSort(buckets[j], bucLen)

copy(arr[datalen:], buckets[j])

datalen += bucLen
}
}
}

堆排序

堆排序是指利用堆这种数据结构所设计的一种排序算法。堆是一个近似完全二叉树的结构,并同时满足堆积的性质:即子节点的键值或索引总是小于它的父节点,

1
// TBD

本文讨论关于数组排序算法的实现, 给定一个整形数组,按从小到大排列

常见的排序算法有:插入排序, 选择排序,冒泡排序,快速排序,归并排序等

插入排序

两种实现方案,第一种方法是新建一个数组并按序排列,遍历原始数组复制元素到新的数组, 时间复杂度为 O(n * n), 空间复杂度为O(n), 同时为不稳定的排序; 另一种方式是在原地选择和排序,数组分为两部分,左边是已排序的部分,右边是带排序的部分,初始状态左侧为数组第一个元素,右侧为第二个元素之后的元素,从数组中第二个元素开始遍历, 依次插入左侧并排序, 时间复杂度为 O(n * n), 稳定排序

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
75
76
77
78
package sort

// 插入排序
// v1: time O(n * n) space O(n)
func InsertSortArray1 (array []int, length int) []int {
// 1. 创建一个新的数组
// 2. 循环旧的数组数据并复制到插入到新数组的指定位置
if length <= 1 {
return array
}

times := 0

newArray := make([]int, length, length)

newArray[0] = array[0]

for i := 1; i < length; i++ {
index := 0

for j := i-1; j >=0 ; j-- {

times++

if array[i] >= newArray[j] {
index = j + 1

break
}

}

if index < i {
for k:=i-1; k>=index; k-- {
times++

newArray[k + 1] = newArray[k]
}
}

newArray[index] = array[i]
}

println("time O(", times, ")")

return newArray
}

// v2 time o(n * n) space O(0)
func InsertSortArray2 ( array []int, len int) []int {
// 原地交换数据
if len <= 1 {
return array
}

times := 0

for i := 1; i< len; i++ {
curr := array[i]
j := i-1

for ; j>=0; j-- {
times++

if array[j] > curr {
array[j+1] = array[j]
} else {
break
}
}

array[j+1] = curr
}

println("time O(", times, ")")

return array
}

选择排序

遍历数组, 每次取剩下的元素中的最小的元素的下标, 如果找到了就和已排序的元素进行交换, 时间复杂度 O(n * n)

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
package sort

func SelectionSort1(arr []int, len int) {
if len <=1 {
return
}

times := 0

for i:=0; i< len; i++ {
minIndex := i

for j:= i+1; j < len; j++ {

times++

if arr[j] < arr[minIndex] {
minIndex = j
}
}

arr[i], arr[minIndex] = arr[minIndex], arr[i]
}

println(times)
}

冒泡排序

比较相邻的数字,将大数字后移, 遍历完一轮右侧是最大的数字,然后在比较第0个元素到第n-1个元素,直到n=1, 时间复杂度: O(n * n)

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
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
package sort

func BubleSort(arr []int, len int) {
if len <=1 {
return
}

flag := false

times := 0

for i :=0; i< len; i++ {
for j :=0; j< len-i-1; j++ {

times++

if arr[j] > arr[j+1] {
arr[j], arr[j+1] = arr[j+1], arr[j]

flag = true
}
}

if !flag {
break
}
}

println("time:", times)
}```


### 快速排序
> 首先选择一个pivot, 一般选最后一个元素, 然后便利数据从start和end, 如果小于pivot, 那么交换正在比较的两个数据, 第一次对比完成后,pivot所在的索引左边都是小于pivot的数据,右侧都是大于pivot的数据, 然后分别对左侧和右侧数据进行排序, 直到start>=end. 时间复杂度O(logN)


```go
package sort

/*
排序过程:

j=0, 1, i=0 1
1 111 23 31 43 54 45
> i=1

j=1 111, i=1 1111
1 111 23 31 43 54 45

j=2 23, i=1 111
1 23 111 31 43 54 45
> i=2 111

j = 3 31
1 23 31 111 43 54 45
> i = 3 111

j = 4, 43
1 23 31 43 111 54 45
> i = 4 111

j = 5, 54
1 23 31 43 111 54 45
> i=4 111

1 23 31 43 45 54 111
> i =4, j =5

*/
func QuickSort(arr []int, len int) {
separate(arr, 0, len-1)

println("time O(", times, ")")
}

func separate(arr []int, start,end int) {
if start >= end {
return
}

i := partition(start, end, arr)

separate(arr, start, i-1)
separate(arr, i+1, end)
}

var times int = 0

func partition(start int, end int, arr []int) int {
pivot := arr[end]

i := start

for j :=start; j< end; j++ {
times++

if arr[j] < pivot {
if i != j {
arr[i], arr[j] = arr[j], arr[i]
}

i++
}
}

arr[i], arr[end] = arr[end], arr[i]

return i
}

归并排序

将数据分成相等两部分(length / 2 = mid), 分别对start到mid和mid到end进行排序, 然后在进行合并, 知道start>=end结束遍历, 时间复杂度O(logN), 空间复杂度O(N)

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
package sort

var exetimes int = 0

func MergeSort(arr []int, len int) {
if len <=1 {
return
}

sort(arr, 0, len-1)

println("tims O(", exetimes, ")")
}

func sort(arr []int, start, end int) {
if start >=end {
return
}

mid := (start + end) /2

sort(arr, start, mid)
sort(arr, mid+1, end)
merge(arr, start, mid, end)
}

func merge(arr []int, start, mid, end int) {
temp := make([]int, end - start + 1)

i := start
j :=mid+1
k := 0

for ; i<=mid && j<= end; k++ {
exetimes++

if arr[i] > arr[j] {
temp[k] = arr[j]
j++
} else {
temp[k] = arr[i]
i++
}
}

for ;i<=mid;i++ {
exetimes++

temp[k] = arr[i]
k++
}

for ;j<=end;j++ {
exetimes++
temp[k] = arr[j]
k++
}

copy(arr[start:end+1], temp)
}

实现链表的基本数据结构

  • 链表的基本数据结构的实现
  • 插入,删除, 更新

思路

链表是包含头节点指针和数据节点指针的数据表, 查询复杂度为O(n)

实现目标

  • 使用Go语言实现基本的链表数据结构
  • 通过Go实现链表的基本操作

代码实现

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
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
package linkedlist

import (
"fmt"
"github.com/pkg/errors"
)

type Node struct {
next *Node
data interface{}
}

type LinkedList struct {
head *Node
length uint
}

func NewNode(value interface{}) *Node {
return &Node{nil, value}
}

func NewLinkedList() *LinkedList{
return &LinkedList{NewNode(0), 0}
}

func (self *Node) GetNext(node *Node) *Node {
if nil == node {
return nil
}

return node.next
}

func (self *Node) GetValue(node *Node) interface{} {
if nil == node {
return nil
}

return node.data
}

// insert new node after specific node
func (self *LinkedList) InsertBefore(node *Node, v interface{}) error {
if nil == node {
return errors.New("Try to insert before a empty node")
}

newNode := NewNode(v)
curr := self.head.next
pre := self.head

for nil != curr {
if curr == node {
break
}

pre = curr
curr = curr.next
}

if nil == curr {
return errors.New("Can not find node to insert")
}

pre.next = newNode
newNode.next = curr

self.length++

return nil
}

func (self *LinkedList) InsertAfter(node *Node, v interface{}) error {
if nil == node {
return errors.New("Try to insert after a empty node")
}

curr := self.head

for nil != curr {
if curr == node {
break
}

curr = curr.next
}

if curr == nil {
return errors.New("Can not find node to insert")
}

newNode := NewNode(v)
nextNode := curr.next
curr.next = newNode
newNode.next = nextNode

self.length++

return nil
}

func (self *LinkedList) Prepend(v interface{}) error {
return self.InsertAfter(self.head, v)
}

func (self *LinkedList) Append(v interface{}) error {
newNode := NewNode(v)

head := self.head

if nil == head.next {

head.next = newNode

} else {

curr := head

for nil != curr.next {
curr = curr.next
}

curr.next = newNode
}

self.length++

return nil
}


// find
func (self *LinkedList) FindByIndex(index uint) (*Node, error) {

if index > self.length {
return nil, errors.New("Find out of range")
}

curr := self.head

for i := uint(0); i< index; i++ {
curr = curr.next
}

return curr, nil
}

func (self *LinkedList) FindByData(data interface{}) (*Node, error) {
curr := self.head

for nil != curr {
curr = curr.next
}

return curr, nil
}

func (self *LinkedList) DelNode(node *Node) (bool, error) {
if nil == node {
return false, errors.New("Try to delete empty node")
}

curr := self.head

for nil != curr {

if(curr.next == node) {
curr.next = curr.next.next

return true, nil
}

curr = curr.next
}

return false, nil
}

func (self *LinkedList) Print() {
curr := self.head

var formatStr string

for nil != curr.next {
curr = curr.next

formatStr += fmt.Sprintf("|%+v", curr.data)
}

fmt.Println(formatStr)
}

实现数组的基本数据结构

  • 数组的基本数据结构的实现
  • 插入,删除, 更新

思路

数组是一系列连续的数据集合,有固定的长度和下标,根据下标访问查找时间复杂度O(1),删除时间复杂度也是O(1)。

实现目标

  • 使用Go语言实现基本的数组数据结构
  • 通过Go实现数组的基本操作

实现代码

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
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
package array

import (
"errors"
"fmt"
)

type Array struct {
data []int
length int
}

func NewArray(capcity int) *Array {
if capcity <= 0 {
return nil
}

return &Array{
data: make([]int, capcity, capcity),
length: 0,
}
}

func (self *Array) IsOutOfRange(index int) bool {
if index < 0 || index >= cap(self.data) {
return true
}

return false
}

func (self *Array) Len() int {
return self.length
}

// find specfic data index value
func (self *Array) FindIndexByData(data int) int {
for index, value := range self.data {
if value == data {
return index
}
}

return -1
}

func (self *Array) FindDataByIndex(index int) (int, error) {
if self.IsOutOfRange(index) {
return -1, errors.New("Out of range when find")
}

return self.data[index], nil
}

// Delete elements by sepecific value
func (self *Array) Delete(index int) (int, error) {
if self.IsOutOfRange(index) {
return -1, errors.New("Out of range when delete!")
}

v := self.data[index]

for i:=index; i< self.Len() - 1; i++ {
self.data[i] = self.data[i] + 1
}

self.length--

return v, nil
}

func (self *Array) Insert(index int, data int) (bool, error) {
if self.IsOutOfRange(index) {
return false, errors.New("Out of range when insert")
}

if self.Len() == cap(self.data) {
return false, errors.New("Capcity is full when insert")
}

for i := self.length-1; i >= index ; i-- {
fmt.Println("Insert: ", self.data, i, index)

self.data[i] = self.data[i-1]
}

self.data[index] = data

self.length++

return true, nil
}

func (self *Array) Append(data int) (bool, error) {

if self.Len() == cap(self.data) {
return false, errors.New("Capcity is full when insert")
}

return self.Insert(self.length, data)
}

func (self *Array) Prepend(data int) (bool, error) {
if self.Len() == cap(self.data) {
return false, errors.New("Capcity is full when insert")
}

return self.Insert(0, data)
}

func (self *Array) Print() {
var formatString string
for i :=0; i < self.length; i++ {
formatString += fmt.Sprintf("|%+v", self.data[i])
}

fmt.Println(formatString)
}

契机

最近参加了一轮大厂的面试, 直接手撕算法和数据结构, 感觉到很是挺生疏的, 简单的算法也不能很快写出来。 因此, 决定个人博客开立算法板块, 一方面重学数据结构和算法, 一方面将自己的心得记录下来。 最终目标, 可以手写常见的算法和数据结构实现,同时兼顾时间复杂度和空间复杂度, 每周至少学习一个算法。

计划

  • 基础部分

    • 数组: 查询, 插入, 删除
    • 链表: 单链表/循环链表, 插入,删除
    • 栈: 入栈,出栈, 查找
    • 队列 入队,出队,循环队列, 查找
    • 二叉树 查找(前中后序), 删除, 添加节点
    • 堆 搜索, 删除, 添加
    • HashTable 查找,添加,删除
    • 图 插入,删除,查找最短路径
  • 中等

    • 红黑树/B+树
    • 字符串匹配算法
    • 动态规划
    • 回溯算法
    • 贪心算法
  • 实用案例分析

    • 数据库索引算法
    • 实现正则表达式的匹配算法
    • 实时统计TOP N的信息
    • 推荐算法
    • 机器学习算法原理
    • 分布式算法
  • 实践

    • topcoder刷题
    • letcode刷题

反馈机制

  1. 每个课题手写完整的代码, 必须没有语法错误, 边界检查
  2. 验证时间复杂度和空间复杂度, 持续优化找出不足之处该进
  3. 到topcoder或者letcode上找一个相关的案例提交解题答案
0%