本文记录了2024年三月作者观看星舰第三次发射的经历。为了观看发射,他早早预定了KOA房车营地和拖挂房车。出发时遇到电路问题,途中经历了长途驾驶并在挤满游客的South Padre Island等待发射,尽管天气不佳,但整体体验良好。
The post 房车旅行:观看星舰发射 appeared first on Frank's Weblog.
本文探讨了电子邮件伪造问题,介绍了解决方案SPF、DKIM和DMARC。文章通过实例和技术细节解释了这些防范电子邮件伪造的标准和技术。
The post 电子邮件防伪:SPF, DKIM与DMARC appeared first on Frank's Weblog.
This post explains email spoofing, detailing DMARC, SPF, and DKIM protocols that help verify authorized sending servers and authenticate emails to prevent fraudulent activities.
The post Anti Email Spoofing: SPF, DKIM and DMARC appeared first on Frank's Weblog.
作者详细记录了对一台Miata进行软顶更换和内饰恢复的过程。工作包括更换破损的软顶,清洗座椅和地毯,修复生锈的座椅底盘和车体其他部分。作者遇到了许多挑战,如座椅金属部分的严重生锈和断裂的螺栓,但通过创造性的解决方案,成功恢复了车辆的内饰,使其焕然一新。
The post Project Miata – 软顶更换及内饰修复 appeared first on Frank's Weblog.
The author restored the interior of a Miata, initially aimed at cleaning and replacing the soft top, revealed extensive rust. This led to extra work, including seat pan restoration, carpet treatment, and trunk maintenance, showcasing the unpredictable yet rewarding journey of vintage car restoration.
The post Project Miata – Soft Top Replacement & Interior Restoration appeared first on Frank's Weblog.
最初,没有人在意,这不过是一次hire freeze,一场裁员,一家公司的解散,一间银行的倒闭,直到这场危机与每个人息息相关。
The post 2023年终总结 appeared first on Frank's Weblog.
The author encountered a stiff shifter in his Miata and realized it required maintenance. He purchased a shifter rebuild kit and carried out a self-service, replacing rubber, bushings, and old fluids, and meticulously reassembled the shifter. The issue got resolved improving the vehicle's gear shifting performance.
The post Project Miata – Shifter Rebuild appeared first on Frank's Weblog.
这篇文章描述了作者对他的Miata换挡杆进行维护的经历。他发现换挡不顺畅,换挡把活动量大。为此,他购买了换挡杆重建套件和必要工具,如75W-90齿轮油和注射器。维护中发现换挡杆损坏严重,原厂垫圈碎裂。他更换了损坏的零件,重新组装,注入新齿轮油。使换挡体验焕然一新
The post Project Miata – 换挡杆维护 appeared first on Frank's Weblog.
The author purchased a 1995 Miata with various minor damages as a project car from Craigslist. Despite issues like a damaged fender, broken rear plastic window, and non-functioning odometer and gas gauge, the car was deemed a perfect fixer-upper due to the car's inexpensive cost and straightforward repairs. A test drive uncovered no significant problems. The author completed the purchase for $2000, intending to restore the car for daily use and occasional motorsport. This marks the start of the 'Project Miata'.
The post Project Miata – $2000 Project Car appeared first on Frank's Weblog.
作者在Craigslist上购得了一辆1995年的Miata,虽然车辆存在一些问题,如受损的车翼板、破裂的后部塑料窗户、以及不工作的里程表和油表等,但由于这些问题相对较小,而且车辆价格适中,易于修复,因此被视为理想的翻新项目。最终,作者以2000美元的价格完成了交易,计划对车辆进行修复,使其适用于日常驾驶和偶尔的汽车运动。这标志着“Project Miata”正式启动。
The post Project Miata – $2000玩具车 appeared first on Frank's Weblog.
ClickHouse团队这篇文章自今年3月发布以来,就看到Twitter上有很多人转发或推荐了这篇文章。
这是一篇教科书级别的文章,讲述如何基于Kubernetes构建一个Serverless云服务。因为我从事的领域和这方面相关,并且我所参与的产品开发与这篇文章有很多相关之处。我研读了这篇文章很多很多遍,然后决定将这篇文章翻译并添加我的理解。
你有好奇过在一年内开发一个无服务器(Serverless)的SaaS服务都需要些什么吗?在这篇博文中,我们将介绍我们如何从零开始构建ClickHouse Cloud ——一个基于全世界最流行的OLAP(online analytical processing)数据库之一构建的托管服务。我们深入研究了我们的规划流程、设计和架构决策、安全性和合规性考虑因素,如何在云上实现全球可扩展性和可靠性,以及我们在此过程中学到的一些经验教训。
The post 教科书级别的云服务构建指南——「在一年内从零开始构建ClickHouse Cloud」一文的翻译与笔记 appeared first on Frank's Weblog.
This year, Subiefest, the Subaru’s official car meet, came to Texas for the first time. In addition to exhibitions and vendors, there are also car shows and Autocross.
If you are not familiar with Autocross:
Autocross, or “parking lot racing” as my brother likes to call it, is a low-cost, low-struggle, low-risk way to get out and drive your car fast. Typically set up in a parking lot, airport, track, or any place with a wide open piece of tarmac, the “race track” is an improvised course marked with small traffic cones. Cars run one at a time in an effort to score the best time through the course. Hitting cones results in penalty time added to your run, usually a second or two. Most runs are anywhere from 40-100 seconds long.
Usually any car that drives and satisfies the requirements on the tech inspection list can attend. One common exception is SUVs and pickup trucks are excluded in most autocross events because of higher rollover risk.
When I bought the tickets for the Subiefest event, the Autocross registrations were already sold out. At Friday night before the event, I noticed Subiefest instagram said there were a few extra Autocross spots available and luckily I got one of the last a few spots.
However that means as a newbie with no real world experience, I have only one day to prepare
The post My First Autocross at Subiefest Texas appeared first on Frank's Weblog.
今年斯巴鲁官方车聚Subiefest第一次来到了德州。除了展览和Vendor之外,还有Car show和Autocross。
如果你不了解什么是Autocross:
Autocross是一种低门槛,低强度且安全的赛车运动,目的是让车手了解自己的极限和汽车的极限。场地通常是在废弃机场,大型停车场等开阔空间用锥筒摆出的赛道。赛道上通常只有一辆车,通过计时与其他选手竞争。
总之可以理解为“停车场赛车”。参与Autocross的门槛很低,通常任何能开且满足Tech inspection要求的车辆都可以参与。一个常见的例外是大部分的Autocross活动都禁止SUV和皮卡参与,因为较高的重心会带来更高的翻车风险。
当我买票的时候,Autocross的名额已经没有了。就在活动前的周五晚上,我在主办方的Instagram上看到又有Autocross名额放出,然后非常幸运的抢到了为数不多的几个名额之一。
然而这意味着作为没有真实世界经验的新手,我只有周六一天时间去完成准备工作。
The post 在Subiefest Texas的首次Autocross appeared first on Frank's Weblog.
从2013年起计算,今年是我写博客的第10年,这大约是我持续时间最长的业余项目。
最初创建博客的机遇是高中的时候,我和几个朋友一起创建了一个科技社团,我们想给社团做一个网站。我们当时只是觉得拥有一个网站很酷,实际上并没有想好网站要用来做什么内容。如果你翻到最后一页,你仍然可以找到朋友们当时写的文章。
就像中学时折腾过的各种项目一样,过了一段时间过后慢慢就荒废了。并且SAE花费确实有些高昂,于是我又把网站捡起来,并搬到了国外的VPS上,后来就成为了我的个人博客。
内容方面,一开始我的文章以技术类教程为主,当时对于技术的涉猎还不太广泛,主要围绕博客搭建(每个博主绕不开的话题LOL),Linux,树莓派,Arduino等等。
随着时间的推移,和技术的理解和应用逐渐深入,我开始尝试写一些更深入的技术内容,记录生活中的事件,以及一些对于冷门问题的分析和解决方案。
The post 博客10周年纪念 appeared first on Frank's Weblog.
This site is hosted in a single AWS Lightsail instance in Japan West region, it has perfect performance when visiting from near by regions, however it has poor performance if visiting from another continent.
Poor TTFB and LCP from US East and Europe
Core Web Vital fails because of slow LCP, which impacts SEO performance.
I've been using WordPress to run this site for almost 10 years. WordPress have been a very successful software in blogging, CMS and even e-commerce. Comparing with static solutions, it takes more effort to optimize its performance, because of its "dynamic" nature.
I've done a lot of performance tuning for this site, and it already archived ~150 ms TTFB from nearby cities, it's not possible to optimize any further from the server side. It's also very difficult to optimize the time that data travels between visitors and the server, since the speed of the packet is limited by the speed of light, and we don't have the control to the routing of the packets.
One solution that came up to my mind is to add more origin servers and make them distributed all over the world. The visitors will hit the nearest server to eliminate the latency, it also makes the site HA by rerouting the visitors to the working site in case one of the servers is down. While there are some managed WordPress hostings that provide this feature, but these services are very expensive.
I decided to conduct a proof of concept for this idea. The goals are:
This article will cover the design and implementation of a geographically distributed WordPress architecture, and review the design based on its performance, cost, maintainability and scalability.
The post A PoC for Geographically Distributed WordPress Deployment appeared first on Frank's Weblog.
We recently embarked on a cross-country move from Syracuse to Dallas, towing a U-Haul 5x8 trailer behind our car. The trip was split into four days, with stops in Mansfield, OH, Nashville, TN, and Little Rock, AR.
Before leaving Syracuse, I checked every item according to the checklist, including hitch pin, ball mount, coupler, safety chain, wiring, tires, lock, etc. However, I skipped checking the lights because they were already tested when picking up the trailer, so I assumed that they should be working properly as long as the connection was good.
The first stop was Mansfield OH, about 7 hours drive from Syracuse. We stopped for dinner and gas at a small town near Cleveland OH. It was almost dark and I was a bit anxious since I skipped the light check before departure. So I checked the trailer tail lights and found out that the lights were off.
The post UHaul Trailer Lighting Issue During Cross Country Move appeared first on Frank's Weblog.
5月底,我们从雪城搬到了达拉斯。我们选择了开自己的车,后面拖UHaul的5x8拖车。整个行程分为4天,途径Mansfield OH,Nashville TN和Little Rock AR。
从雪城出发前,我按照事先列好的检查单检查了每一个项目,包括拖车的连接(pin, ball mount, coupler, safety chain,wiring),轮胎,锁,两只猫的牵引绳,AirTag,重要的行李等等。然而我唯独跳过了对灯光的检查,因为在取车时已经测试过车灯,所以我认为只要插头插好,车灯应该是正常工作的。
第一天的终点是Mansfield OH,离雪城大约7小时车程。在路过Cleveland OH附近的一个小镇时我们停下来买晚饭和加油。当时天已经快黑了,因为中午出发时跳过了灯光的检查,总是有些不放心。于是我去检查了拖车尾灯,结果发现灯不亮了。
The post 跨州搬家途中的UHaul拖车尾灯短路问题 appeared first on Frank's Weblog.
Cloudflare Load Balancer is a global load balancing product provided by Cloudflare. It can connect to origin servers in traditional ways by DNS name or IP addresses, it also can be integrated with Cloudflare Tunnel to create a seamless and secure network infrastructure.
Using Cloudflare Tunnel with Cloudflare Load Balancer is more complicated as we need to configure the DNS name and host header to make sure the routing and monitoring work correctly.
In this post, we will use an example to demonstrate how to use Cloudflare Load Balancer with Cloudflare Tunnel.
The post Use Cloudflare Load Balancer with Cloudflare Tunnel appeared first on Frank's Weblog.
Cloudflare Load Balancer是Cloudflare提供的一个全球负载均衡产品。它可以以传统方式(域名或IP地址)连接源服务器,还可以与Cloudflare Tunnel集成,以创建一个无缝和安全的网络基础设施。
将Cloudflare Tunnel与Cloudflare Load Balancer一起使用的配置与传统方式相比略微复杂,我们需要正确地配置域名和Host头,以确保路由和监控的正常工作。
在这篇文章中,我们将用一个例子来演示如何配置将Cloudflare Load Balancer与Cloudflare Tunnel一起使用。
The post 配合Cloudflare Tunnel使用Cloudflare Load Balancer appeared first on Frank's Weblog.
On 1/21/2023, my blog was attacked and went down for 4 hours. This article will cover what the incident was like, the root cause analysis and improvements.
On that day, I woke up in the noon and saw the alert email from UptimeRobot. Sometimes a network or server glitch can trigger an alert as well, but it have been 2 hours since alert triggered, so apparently that's not the case. I found I was not able to connect to the website, while sometimes I could connect but got 504.
I ssh-ed to the server and restarted all the Docker containers, but the problem persists. top showed that all the load average were 6.xx and most of the CPU usage were from php-fpm. I checked the graphs in nginx amplify and found that nginx have received large amount of requests during past few hours.
I planned to go grocery shopping for the lunar new year dinner with my girlfriend, so I didn't want to spend too much time on this. I simply turned on the Cloudflare reverse proxy(orange cloud icon) and "Under attack" mode and left home.
After a while I received the alert clear email from UptimeRobot and website was back online.
Over last few years I've implemented a set of monitoring and security measures for my site and automated scripts to mitigate common issues.
1.UptimeRobot for monitoring downtime. I’ll receive alerts if the website cannot be reached or returned HTTP status that indicates a malfunctioning(5xx).
2.nginx amplify for monitoring nginx and OS metrics. I’ll receive alerts if some metrics(eg. disk usage, requests per second) goes over the threshold.
3.If requests per second goes over the threshold, it will automatically turn on Cloudflare proxy and increase security level.
4.WordPress security plugin automatically blocks malicious requests.
Benefit from these measures, my site have maintained a uptime of nearly 100%. Being a blog that only have 2 digits of visitors everyday, 4 hour downtime is nothing to worry about. But my professional habit have been wondering what happened behind the incident, especially why these measures failed to prevent the incident from happening.
The post 2023/1/21 Blog Incident Postmortem appeared first on Frank's Weblog.
2023年1月21日,我的博客收到攻击宕机了4个小时左右。本文将介绍事件的经过,对根本原因的分析,及改进方案。当天中午,我起床之后看到了UptimeRobot的报警邮件。有时一些网络或者服务器的短暂故障也会触发报警,但是当时距离收到报警邮件已经过去了近两个小时,所以事情显然没有这么简单。我简单检查后发现访问博客时有时完全无法连接,有时会返回504。我ssh上去之后重启了一下所有Docker容器,但是故障依旧。top显示全部load average高达6.xx并且大部分的CPU使用来自php-fpm。检查nginx amplify图表之后发现过去几小时内nginx收到了大量的请求。因为当天下午我计划和女朋友去采购年夜饭的食材,不想在这上面花费太多时间,于是我打开了Cloudflare的反向代理(橙色云图标)和Under attack模式,然后就出门了。过了一段时间后就收到UptimeRobot的报警解除邮件,访问恢复。我的博客上有一套监控和安全措施,以及一些自动化脚本来mitigate一些简单问题:- UptimeRobot用于监控可访问性,如果出现无法连接或异常的HTTP状态(5xx)则会发邮件报警- nginx amplify用于监控nginx和操作系统的指标,其中一些指标(例如磁盘使用,每秒请求数量)超过阈值之后会发邮件报警。- 如果每秒请求量超过阈值则会自动开启Cloudflare反向代理并升高安全等级。- WordPress的安全插件会自动block恶意请求。受益于这些措施,博客在过去几年一直保持着近乎100%的uptime。作为一个每日访问量两位数的博客,4个小时的downtime并不是一个需要担心的问题,但是职业习惯还是让我想知道背后到底发生了什么,尤其是为什么这一系列措施都未能阻止宕机的发生
中文版:使用K3s部署预算友好的ARM-X86混合Kubernetes集群 – Frank’s Weblog Kubernetes were used for enterprise level services, which is heavy and expensive. Even for the least-expensive Digital Ocean, its managed Kubernetes starts with $12/month per node. Then I learned about K3s, a lightweight Kubernetes distribution that removes or lightens many of the components in Kubernetes, allowing K3s to run on smaller […]
Throughout 2022, the financial statements from tech companies were extensively discussed, and you probably have seen diagrams like this: I later discovered that this diagram is known as Sankey Diagram, which is a type of flow diagram in which the width of the arrows is proportional to the flow rate of the depicted extensive property. […]
2022 was a somehow a bumpy ride. Although many goals were not achieved, it was fortunate that I did not encounter any major difficulties in such a tough environment.
Kubernetes大多被用于企业级的服务,十分沉重且昂贵。即使是最便宜的DigitalOcean,其Managed Kubernetes最低也需要$12/月每节点。后来我了解到了K3s,一个轻量级的Kubernetes发行版。K3s移除或轻量化了Kubernetes中的很多组件,使得K3s可以运行在较小的VM甚至Raspberry Pi上,同时仍然拥有Kubernetes的可扩展性。 本文将研究如何使用K3s在免费的Oracle Cloud Free Tier上搭建一个预算友好的Kubernetes集群,并在企业级技术和成本之间找到一个平衡,目标是将我之前使用Docker运行的一些个人服务,包括Matomo,Maraidb,Misskey以及若干小工具迁移到Kubernetes上来。本文将侧重选型及概念,不会涉及具体实现细节。
English version: Backup and Restore Kubernetes Volumes with Velero Restic Integration – Frank’s Weblog 我搭建了一个Kubernetes集群,使用OpenEBS作为存储后端。我选择了Jiva作为存储引擎,Jiva是一个高可用的存储控制器,数据被复制到所有节点。为了确保数据的安全,我使用Velero及其Restic集成将卷备份到AWS S3。 安装 首先需要在本地电脑上安装 Velero CLI以控制Kubernetes集群上的Velero控制器,请参阅Velero Docs – Basic Install了解安装说明。 准备Kubernetes配置文件,其应位于.kube/config 并确保 kubectl get pod 返回正确的结果。 使用如下的格式创建一个AWS密钥文件,该密钥应具有访问在下一步骤中提供的S3存储桶的权限,记下该文件的路径。 将Velero安装到Kubernetes集群 备份及还原 运行单个备份 创建定时备份计划 有关更多备份选项,请参阅 Velero Docs – Restic Integration 从备份恢复 导出备份 有时直接恢复到Kubernetes集群并不能满足我们的需求。在这种情况下,我们可以直接从位于S3的备份repo中导出文件。由于Velero在其CLI中没有提供此功能,我们需要使用Restic CLI。 安装 Restic CLI Installation — restic 0.14.0 documentation 获取快照 ID […]
I built a Kubernetes cluster using OpenEBS as the storage backend. I selected Jiva as the storage engine, Jiva is a high-available storage controller, data is replicated to all nodes. To ensure the safety of the data, I used Velero and its Restic integration to backup the volumes to AWS S3.
Disclaimer: 本文仅作为分享,不构成任何投资及税务建议。投资收益及AMT税务非常复杂,如果你是利益相关者,请务必自己花时间研究或咨询会计师。
前段时间,我在公司工作满一年后收到了前1/4的股票期权。对于如何处理这些期权,我进行了一些研究。
和上市公司或PreIPO公司授予的RSU(Restricted Stock Unit, 受限股票单位)相比,Stock Options潜在的收益更大,同时风险也更高。大多数全职员工拿到的是ISO(Incentive Stock Options)期权,另一种期权是NSO(Non-Qualified Stock Option) 本文不作讨论。
ISO期权在拥有一些税务优势的同时也会产生很大的税务风险。简单来说,ISO期权可能让你获得很大一笔钱,或是(花钱买来的)一堆废纸,甚至在最坏情况下,可能让你因为税务破产。要在规避风险的同时使收益最大化,需要提早规划并谨慎操作。
我在我的2019 MacBook Pro上升级了macOS Ventura Public Beta3(22A5321d)之后出现了合盖休眠期间重启,并在唤醒之后丢失之前工作内容的问题。
Cloudflare Tunnel is a tunneling service provided by Cloudflare. With Cloudflare Tunnel you can connect the origin to Cloudflare and provide service without exposing any ports on the server or cluster, therefore minimizing the attack surface.
Cloudflare Tunnel was formerly known as Argo Tunnel. Later Cloudflare Tunnel became part of the Cloudflare Zero Trust and became available to all users for free in 2021.
Cloudflare Tunnel had several iterations in the past two years, many tutorials on the interne have become outdated. The latest Cloudflare Tunnel needs no configuration on the client (cloudflared) side besides token. All sites (services) can be configured on the Cloudflare web console. If a tutorial ask you to configure the site on Cloudflared through yaml, the tutorial is likely outdated.
This post will use httpbin as an example to illustrate how to deploy Cloudflared on Kubernetes and serve other services deployed on the cluster.
Cloudflare Tunnel 是一个隧道服务,通过Cloudflare Tunnel可以无需在服务器上暴露任何端口的情况下将源站连接到Cloudflare并提供服务,从而降低攻击面。
Cloudflare Tunnel以前叫Cloudflare Argo Tunnel。后来Cloudflare Tunnel成为了Cloudflare Zero Trust的一部分,并向所有用户免费提供。
Cloudflare Tunnel在过去两年经过了大量迭代,网络上的很多教程,甚至包括官方的教程都已经过时。最新的Cloudflare Tunnel无需在客户端(Cloudflared)上做除了token之外的任何配置,所有网站(服务)配置都可以通过Cloudflare Web控制台进行。如果一篇教程让你在Cloudflared上通过yaml来配置网站,那么这篇教程大概率是过时的。
本文将以httpbin为例,介绍如何在Kubernetes上部署Cloudflared并路由Kubernetes上部署的其他服务。
由于融雪剂的侵蚀,汽车后轮上方生锈在美国东北部是很常见的现象。我的这辆车从两年前开始,后轮上方出现了一个小锈斑,从去年冬天开始,这块锈开始扩大。如果放任不管的话,它会更快地蔓延,最终变得无法修复。所以我决定修复它。
其中大部分的步骤参照了ChrisFix的视频,Chris的视频已经讲得很全面了。本文中我将介绍Chris在视频中没有涉及的一些细节,以及一些个人技巧。
As mentioned in an earlier post, I used TailScale to create a mesh network of all my devices and I used a cloud server located in AliCloud Beijing as an exit node, in order to access geographically restricted network services.
However, I found that I could not access the Internet at all when using that exit node. I thought it was a network quality issue with the relay so I didn't worry too much about it. But afterward, I noticed some other services on that server was not functioning, so I looked into it and found out that the problem was not that simple.
First I noticed that I couldn't access the internet at all from the server, but curl the IP address was working, which indicated the problem was with DNS. resolvectl status showed that there were two DNS servers, and since the IPs started with 100.100[1], I assumed this was the DNS server for the TailScale internal network (actually not, will elaborate later).
前文提到,我使用TailScale将我的所有设备组成了一个Mesh网络,并且将位于阿里云北京的轻量应用服务器作为出口节点用于访问一些限制地理位置的网络服务。
然而我却发现在使用该出口节点时完全无法访问互联网。我本以为是Relay的网络质量问题就没有在意。但是后来陆续发现该服务器上的其他一些服务都出现了问题,于是进行了一番检查,结果发现问题并没有这么简单。
首先我发现从服务器上完全无法访问互联网,但是直接curl IP地址是可以的,这样就基本上将问题定位到了DNS上。resolvectl status显示有两个DNS服务器,因为IP以100.100打头[1],我以为这是TailScale内网的DNS服务器(实际上不是,请看后文)。
我试图dig @100.100.2.136 baidu.com来检查DNS服务器的回应,得到connection timed out: no servers could be reached.。关掉TailScale之后,上述命令的回应则正常。因此我认为问题在于TailScale从某种形式上影响了系统的DNS解析。
前段时间无意中了解到了TailScale这个产品。TailScale是一个基于WireGuard的Mesh组网工具,可以将所有设备连接起来,组成一个大内网。TailScale是商业软件,免费版最多支持20个设备和一个子网路由,对大多数家用需求来说应该够用了。 我的需求主要有如下几点: 外出时访问NAS 家里的Spectrum网络虽然有公网IP,但是我并没有将NAS的文件服务暴露到公网。我目前使用的是通过端口转发暴露的群晖上自带的OpenVPN,能用但是体验并不好。 安全访问 在不安全的网络(如酒店/机场)中加密通讯以保证安全。 地理位置 用于访问一些有地域限制的网络服务,比如网易云或B站。 特性 TailScale最大的优势是开箱即用,下面的所有功能均可通过Web界面配置,不需要写一行代码或配置文件。 Mesh组网 当在设备上安装并登录TailScale之后,该设备会被分配一个100.x.x.x的IP地址。 WireGuard中并没有服务器/客户端的区分,每个客户端都是一个Peer。Peer之间会首先尝试通过NAT穿透建立点对点连接。如果失败(比如设备位于无状态防火墙和Hard NAT背后)则会通过TailScale提供的Relay进行中继。 如果你想了解NAT穿透的原理,可以参考这篇TailScale的博客及其译文。 DNS TailScale网络中内置了DNS服务器,开启后默认使用设备名作为DNS Name。 子网路由(Subnet Routes) TailScale只能让我们访问安装了TailScale客户端的设备,那么能否通过安装了TailScale的设备访问同一物理网络中的其他设备? 这种情况下可以将某个安装了TailScale的Linux客户端配置为Subnet Router。这会让这台设备广播一条路由,比如192.168.0.1/24,这样从TailScale网络即可访问物理网络中的设备[1]。 出口节点(Exit Node) 网络中的任意节点(iOS设备除外)都可以被配置为出口节点,连接到TailScale时可以选择任意的出口节点作为出口。 自建 TailScale在全球范围内提供了数十个Relay服务器,但是全部位于中国境外。如果你网络要求比较高,也可以自建Relay服务器。 如果你不想使用商用软件,则可以选择TailScale的开源实现HeadScale,但是目前iOS客户端暂不支持自定义服务器。 References How NAT traversal works · Tailscale [译] NAT 穿透是如何工作的:技术原理及企业级实践(Tailscale, 2020) Custom DERP Servers · Tailscale [1] Subnet routers and traffic relay nodes · Tailscale
Disclaimer: 本文仅作为经验分享,不构成任何税务或法律建议。 大家都知道F1身份的前五年属于非税务居民(Non-resident Alien, NRA)。但是在券商那里,对于NRA身份往往处理得很混乱。尤其是居住在美国,但又不是税务上的居民,就成了一个很tricky的点,需要在开户和报税时正确处理。 开户 通常来说,NRA不能在券商或银行在线开户,必须要去线下。如果在线开户的话,券商通常会默认你是RA,因此所有税表都会按照RA来处理。 正确的做法应该是去线下开户,或线上开户后提交W8-BEN表格声明自己的NRA身份。有少量DP表示有些券商不给有美国地址的NRA开户,但是实际执行起来YMMV,如果一家不行,可以换一家。提交W9来“冒充“自己是RA虽然理论上不合规,但实际操作起来也没什么问题。[1] 对于Robinhood这样的纯线上券商,我们显然没有线下开户的选项。Robinhood一开始注册的时候只需SSN即可,不需要确认税务身份,Robinhood同样会默认你是RA。可能由于Robinhood上市之后的合规需要,去年8月我收到了来自Robinhood的邮件要求确认税务身份,如果是NRA则需要填写W8-BEN表格。[2] 报税 如果你在券商那里的税务身份是正确的,那么在报税季应该会收到1042-S表格。但是如果税务身份有误或是券商自己的问题,也可能会收到1099表格。比如即使我已经向Robinhood提交了W8-BEN,我仍然收到了1099表。但是无论收到的是1042-S还是1099都不影响合法身份,只要正确报税即可。概括来说,NRA的投资收入应该填写在1040NR的Schedule NEC,并按照当年是否在美国居住满183天来决定税率。 1099表其实是一个很迷惑的概念。1099并不像W-2那样是一张真正的表,各个券商发来的1099格式也不一定一样,但是上面的包含的信息基本是一样的。1099中和投资收入相关有:1099-B(买卖股票、期权的Summary)和1099-DIV(投资收益或者分红/股息的Summary)。[3] 这里以olt.com为例,首先在收入界面上新增一个1099-B。 在填写完1099信息之后会有选项问这个收入是否属于Effectively Connected with a US trade or business。投资股票或房地产不属于与美国贸易有关联的投资,因此这里选择“Not Effectively Connected”(NEC)的选项,这样报税软件会生成Schedule NEC,而不是 Schedule D 或 8989。 在要求填写税率的位置,如果当年居住不满 183 天,税率是0,反之税率为 30%。如果亏损的话,NRA并不能用亏损的部分抵收入税。 除了1099-B以外,股息收入还需要提交1099-DIV。1099-DIV的处理相对简单,只需要在报税软件中添加一个1099-DIV表格,并填写相应信息即可。 Group Transactions 理论上来说每一笔股票交易都需要填写一张1099,而大多数NRA的报税软件并没有提供从券商自动导入的选项(据我所知TaxAct支持导入,而Sprintax和olt.com都不可以[4])。如果交易太多的话,则可以将Long-term和所有Short-term合并在一起,这样只需要填写两张1099-B即可,按照如下olt.com上的介绍填写即可。 我在这样填写的时候遇到了一些问题,因为olt报错称其中的时间不能为空,我猜应该是olt的bug。因为我的交易比较少(只定投,几乎不卖出),所以我直接手填了所有交易。 Sprintax的坑 Sprintax号称可以efile,但实际上有很多限制,满足下面的任意一条即不能efile。 1. If you have an ITIN 2. Any names and SSN do not coincide with the information provided at step About […]
前一段时间因为年检要due了,花了一些时间在试图troubleshoot我的2009 Hyundai Santa Fe上的P0171/P0174故障。在这个过程中查找了一些资料,了解了一些发动机燃油系统的工作原理。 问题 去年年底,我在用OBDII Scanner检查EVAP问题(图中的P0442)时,发现出现了另外两个故障码P0171和P0174,除了故障码本身之外没有任何症状。这两个故障码表示引擎中燃烧的混合气过稀,也就是空气太多,或汽油太少。 在开始解决问题之前,我们首先需要了解一些背景知识。 背景知识 众所周知,油门虽然叫油门,但是实际上控制的是节气门(throttle body)。既然驾驶者控制的只有发动机吸入空气的量,那么发动机是如何控制喷油量从而让气缸中的燃烧处于合适的空气/燃油比例的? 简单来说,以自然吸气发动机为例,当油门控制节气门打开时,位于空滤下游的空气流量传感器(Mass Airflow Sensor,”MAF”)会读取通过的空气流量。计算机会通过各种传感器读取空气流量,水温,负载,转速等数据,计算出合适的空气/燃油比和喷油量,然后命令喷油嘴喷射燃油。这就是开环控制。 这在理想情况下是OK的,然而实际情况下,发动机会受到各种因素的干扰而无法处于理想工况,从而无法达到最佳的燃油效率和排放标准,比如: 进气管路并不完全密封,有少量未被MAF计量的空气进入到了气缸中参与燃烧 喷油嘴有积碳,喷出的燃油比计算机的指令更少 等等 要解决这个问题,就需要为系统运行的状况提供反馈,这就引出了闭环控制。 闭环控制(Closed Loop) 根据定义: an automatic control system in which an operation, process, or mechanism is regulated by feedback. 发动机的每个Bank各有两个氧传感器,前氧传感器位于排气歧管下游,三元催化器上游,负责给ECM反馈排气中的氧气含量数据。后氧传感器位于三元催化器下游,其数据不影响发动机的行为,只用于监控三元催化性能。氧传感器数据将被用于检测燃烧状况,如果氧传感器数据显示系统过稀(lean condition),计算机将会命令喷油嘴多喷油(rich command);反之(rich condition)则少喷油(lean command)。 发动机刚刚启动时会处于开环控制,因为氧传感器在温度不够的情况下无法读出数据。在氧传感器和水温都达到工作温度之后,系统则转入闭环控制。在一些特殊情况下,比如发动机刚启动时(即使已经达到工作温度),油门全开(Wide Open Throttle),计算机会强制使用开环控制。 那么在闭环控制下,ECM如何修正喷油量?这就引出了燃油修正(Fuel Trim)。 Fuel Trim Short Term Fuel Trim 短期燃油修正(Short Term […]
去年我负责开发了产品中的一个大型feature,这个feature被分解成了A, B两个小feature。大致的工作流程是这样的: 我从master branch out出分支feature-a,在分支feature-a上开发;然后从feature-a branch out出feature-b,在feature-b上开发;最后创建feature-b -> feature-a,feature-a -> master 两个PR分别进行code review并依次合并进master。 当我在开发feature-b时由于master上有了很多更新,于是我将master分别merge进了feature-a和feature-b。整个流程的简化版大致如下: 当我创建从feature-b到feature-a的PR时,奇怪的问题出现了:PR的diff中混入了大量已经在master分支上的提交。虽然我相信这个问题并不会真的影响合并,但是PR中混入了很多无关的commit则会让PR难以review。 Manager告诉我我不应该直接将master分支merge进feature-b,而是应该先将master merge到feature-a,然后将feature-a merge进feature-b,也就是这样: 我照做之后问题解决了,但是这并没有解答我的疑惑:我在本地使用git diff显示出的结果是不包含master上的那些无关commit的,为什么GitHub PR的diff会和本地的diff显示不同的结果? 于是我试着基于上图的简化版模型重现了整个过程:https://github.com/frankgx97/git-diff 在从feature-b到feature-a的PR中(https://github.com/frankgx97/git-diff/pull/2/files)我们可以发现:图中 -A, +AE 的两行是在master上的commit E中的更改,因为feature-a和feature-b上均已经存在commit E,所以这个更改不应该显示在这里。 通过查找一些资料发现,GitHub PR的diff默认提供的是三点diff,而git diff默认则是两点diff。两者的区别在于三点diff是用来比较A和B的公共祖先与B的差异,git diff a...b等价于 git diff $(git merge-base a b) b 然而git cli的三点diff并没有包含 -A, +AE ,这似乎并不能用三点diff来解释。 通过git merge-base命令可以发现,git cli认为feature-a和feature-b的merge base是f4c1318,也就是图中的commit E。也就是说git cli的三点diff是在比较commit E和D,这符合我们上面得到的结果。 不过从GitHub中的diff来看,被比较的应该是topic branch的最新commit D(85c13ed)和feature-a和feature-b的公共祖先B(01c9a49)。我猜测GitHub并不是直接使用的git中的三点diff,而是选取两个分支的公共祖先与topic […]
2021年,虽然疫情并没有像大家期望的那样随着疫苗接种而消失,但是生活多少走上了正轨。 学业 二月,我开始了在雪城大学的最后一学期,我最后一学期只需要修一门课,并且因为系里的选课限制把我可选的课压缩到近乎没有,于是最后一学期在一门水课加每周20小时的实习中度过。 今年学校恢复了线下的毕业典礼,在铺天盖地的Congratulations中感到有些恍惚,在居家隔离和网课中,两年时间似乎一转眼就过去了。 工作 进入到2021年,全职工作的求职终于进入到了正轨,从年初开始一共拿到了5个面试,几乎全部拿到了Offer。 最后我选择了加入TigerGraph,并且在最后一学期开始Part Time实习。实习期间独立完成了两个面向用户的新Feature并且在实习结束后不久被发布到产品中。实习工资虽然不算多,但至少cover掉了生活费和最后一学期的学费,实现了低配版的经济独立。5月我从学校毕业,转正成为Fulltime Employee,成为了光荣的打工人。刚开始的几个月比较摸,完成了若干对现有产品的小修小补。 10月开始,我作为项目owner承接了一个大项目。这个项目涉及到对目前产品的网络架构的改进,整个项目分为两个阶段:第一阶段是使用新架构为一个客户交付一个Managed Cloud Service,第二阶段是将新架构应用到现有的Cloud产品中。对于一个职场新人来说,own一个项目在管理技能上的挑战要远远大于技术技能,项目owner首先需要与各个stakeholders沟通,敲定具体的细节,同时要在实现过程中drive整个conversation,加上涉及到跨部门跨时区的协作,对社恐来说是个不小的挑战。最终Customer Project在感恩节前成功交付,同时产品部分经过了几周的讨论之后终于在圣诞节前敲定了设计方案。 总体来说感觉很充实,也学会了很多新技能。 身份 对于国际学生来说,OPT和H1B是逃不过的两座大山,特朗普政府带来的政策不确定性和2020年底德州Lockbox带来的OPT超长延误,都让人非常焦虑。 好在这些问题最终都在年初得到了解决。上述政策在特朗普下台之后不了了之,最终被撤回;USCIS在芝加哥开设了新的Lockbox来接受OPT申请,处理速度大大提升。我只花了两个月时间就收到了EAD卡。 相比之下H1B今年的形势则急转直下。由于从去年开始H1B抽签改为了电子抽签,让参与抽签的成本大大降低,因此引来了大量的作弊(一个人提交多份申请)。今年参与抽签的人(份)数达到了前所未有的30万份,也因此带来了史无前例的三次抽签。随着就业市场的复苏和重开边境,如果抽签作弊问题得不到解决,H1B的形势只会继续恶化下去。当一个系统放任作弊行为时,整个系统最终将会被作弊者占领。H1B其实是美国很多制度的一个缩影,这些制度已经有了几十年历史,已经无法满足当前时代的需求,大家都苦其久矣,但是又因为涉及到多方利益所以很难进行任何实质的改变,只能往上打一些无关紧要的补丁,于是整个系统就这么半死不活地运行着。 关注这个事情的朋友们可能已经听说了Stop H1B Abuse(Liu v. Mayrokas)的诉讼。我也作为510名原告之一参与起诉了USCIS,我们雇佣了业界非常有名的移民律师Charles Kuck,Greg Siskind和Jeff Joseph为我们代理。初期并不太顺利,第二次和第三次抽签都没能用PI(Preliminary Injunction)挡住,并且被USCIS的Motion To Dismiss拖延了一些时间。目前最新的进展是法院驳回了USCIS的Motion to Dismiss,我们提交了Cross-Motions for Summary Judgment。希望在明年三月之前能有一些实质的进展。 生活 年初,随着疫苗铺开接种,以及川普离开白宫,2020年的两大最糟心的事情算是告一段落了。因为年中的Delta变种和年底的Omicron变种带来的新一轮爆发,大多公司都(又)推迟了回办公室工作的时间。我司也并没有回办公室的计划,几乎全部员工都在远程工作。我也选择留在雪城远程工作,等大部分员工回公司后再relocate。 在毕业到入职的间隙有个一周多一点的小长假,自驾去了Niagara Falls和波士顿,拜访了一些朋友。 7月的独立日长周末自驾去了Washington DC,可惜因为疫情,DC的大部分博物馆都严格限制客流量,需要提前很久预约,因此错过了很多值得一去的地方。今年National Mall的烟火照常举办,由于是经济重开之后的第一个独立日假期,来自四面八方的游客挤满了整个National Mall。 今年最好的消息莫过于4月底宣布的对受旅行禁令影响的国家的学生的NIE政策,很快国内就恢复了学生签证服务。女朋友顺利拿到了签证赴美,两年的异国终于结束了。我们一起领养了一只猫猫,名叫Scarlett。 我们带着Scarlett一起去纽约过了圣诞节 总结 和2020年相比,2021无疑是相对正常的一年,然而2021年并不太平,往年埋下的雷正在一个个爆发,疯涨的物价,暴力犯罪和Asian Hate泛滥,超低的H1B中签率,等等。希望2022年新项目能够成功,H1B问题能得到解决;希望女朋友的找工/升学顺利,Scarlett能健康平安地长大。 2021.12.31 于 Syracuse, NY
今年3月,我在清理车顶上的积雪的时候,一大块结成冰的积雪顺着后挡风玻璃滑下来,把后玻璃的雨刷臂砸断了。 雨刷臂的更换其实很简单,雨刷只有一个螺母固定,只需要把螺母拧掉,把旧的雨刷臂拿下来,再把新的装上去即可。但是问题是,经过纽约上州的大雪加上融雪剂的十几年的洗礼,雨刷臂的金属部分已经完全锈死在spindle上面了。我一开始按照YouTube上的视频,购买了一个如下图所示的工具试图把旧雨刷拔下来。 然而雨刷的剩余部分实在锈得太死了,加上工具本身不好用并且操作不当(没有把螺母装在螺丝上),导致雨刷的塑料部分下来了,但金属部分还在上面,还把螺丝的螺纹搞坏了(大无语)。 虽然后面的雨刷没太大用,但是留一个尖锐物体在车上终归是个安全隐患,要是戳到人就不好了。 要把新的雨刷换上去,首先需要解决两个问题: 把螺纹修好 把旧雨刷的残留部分弄下来 需要用到的工具如下: 新的Wiper Arm + Blade 一套车螺纹的工具(Die Tool Set),我使用的是 https://amzn.to/3lwc24F 一个Wiper Arm Puller,我使用的是 https://amzn.to/3de4X4a 一瓶Penetrating Oil,我用的是Liquid Wrench,如果没有可以用WD-40代替 一个Rachet扳手加上合适尺寸的Socket,我这里用到的是12mm (可选)一把普通扳手 (可选)测量螺纹规格的工具 首先我们需要确定螺丝的规格。Metric螺丝的规格有两个参数:直径(Diameter)和螺纹宽度(Thread Pitch)。一些工具套装里会附带一个如下图的测量螺纹宽度的工具。 测量后得知螺丝的直径6mm,螺纹宽度1mm,相应的Metric规格是M6-1.0。使用相应尺寸的Die将螺纹修复即可。 接下来我们需要使用Wiper Arm Puller将残留的雨刷弄下来。开始之前切记要把螺母装到螺丝上面,否则会损坏螺丝的纹路。同时使用Penetrating Oil尽可能去除铁锈。 如果锈得实在太死,需要很大力才能弄下来的话,可以使用一些工具来辅助,比如用车螺纹工具里的把手来转Wiper Puller,可以增加一些力矩,同时用扳手从下面固定住螺丝不让他转动。必要的时候可以用锤子轻敲。 把雨刷的残留部分移除之后,spindle应该是这样的 最后把新的雨刷装上,螺母上紧即可。
经过大半年的时间,紧张刺激的秋招终于告一段落了。对于New Grad来说,2020年的秋招实在很艰难,我在中间也经历了很多波折,终于成功上岸。整个秋招一共收获了9个面试,4个offer,其中也包括一些比较冷门的公司或职位。本文会介绍我面过的比较有意思的公司或职位,以及我个人在找工过程中的一些体会和经验。 Citrix Software Engineer — Rejected 体验:★★★★☆ Citrix是一家toB的虚拟机公司,主要给企业提供基于虚拟机的IT解决方案。Citrix有Florida,North Carolina,湾区和Boston4个Office,每个Office的面试风格都各不相同,比如有的可能更注重OS,有的更注重网络。总体来说相比其他大厂更注重工程能力,沟通能力和behavior,算法上相对简单一些。 MasterCard Software Engineer — Rejected 体验:★★★★☆ MasterCard是大家都很熟悉的信用卡公司,他家主要有两个location,一个位于OFallen, MO,主要负责支付业务,而Arlington, VA的办公室叫MasterCard Data&Services,是一家被MasterCard收购的startup,原名叫Applied Predictive Technologies。我拿到的面试是位于Arlington的Data&Services。面试风格还是沿用了APT时期的风格,毕竟是startup,题目还是有些难度。除了最后一轮HM面之外的体验都还算不错,就不展开讲了。 Google Cloud Technical Residency — Rejected 体验:★★★☆☆ 因为今年Google New Grad没有开,于是投了这个CTR职位。CTR是2018年才开的,在Residency里也算是比较新的。而且CTR并不是一个普通的SDE Residency,而是Google Cloud下面的一个更偏向Customer Support和Solution Engineering的职位。转正后的职位可以是Customer Engineer, Cloud Engineer或Solution Engineer。 整个面试流程是两场两轮背靠背,包括技术,客户支持和behavior。技术面只有一轮,没有coding,而是更偏向HTTP, REST, DNS这类网络技术,以及为客户设计系统架构——如果你能够自己搭建并维护一个性能不算太差的WordPress博客,那技术面试的题目基本上不会有太大难度。技术面的体验超级棒,和面试官聊得很好。不过两轮BQ的体验实在太差,面试官几乎是没有感情的念题机器——完全就是对着屏幕把题目念出来,然后低头打feedback。可能是Google对于面试流程的要求吧,每个面试官几乎都要打超级多的feedback——多到已经有些影响面试进程了。 虽然是很多人(包括我)梦想的Google,但是个人并不推荐这个职位,首先如上文所说,这个职位更偏向客户支持,和SDE还是有差距的,其次,顾名思义这并不是一个全职工作而是residency,需要一年后转正,(据说转正概率很高,>90%)并且会浪费掉一次抽签机会。另外我的两轮BQ面的面试官同样也来自Cloud Residency转正,基于我极差的面试体验,我对团队的氛围感到有些担忧。 Bloomberg SDE — Rejected 体验:★★★★☆ Bloomberg的面试一直是比较中规中矩的,但是我不幸遇到了一个比较有个性的面试官。我的onsite第一轮面试的面试官是一位SRE,看到我简历上写着Linux,于是上来先问了我一堆很细节的Linux问题。紧接着是一个简单的coding和一个系统设计。这个系统设计并不是像短网址和设计Facebook的那种high level的设计题,而是非常low level的system design,我依靠脑子里为数不多的知识储备磕磕绊绊地答出来一半,还是挂掉了。 我并没有预想到会在Bloomberg遇到基础知识题,所以之前完全没有准备。不过之后回想起来,那些Linux问题其实并没有那么难。以shell如何执行命令这个问题为例,即使事先没有准备过,依靠其他知识也完全可以推出来(想想OS里一个进程是如何调用另一个进程的)。不过可能当时太紧张,脑子浆糊了。 […]
去年有一段时间沉迷YouTube上的修车视频,最近终于有些时间了,也想自己动手试一试。换机油其实并不难,但是会有一些潜在的坑,比如很多人都遇到过的螺丝拧不动的问题,我基本上已经遇到了大部分新手可能遇到的坑。第一次录视频,做得有些粗糙,之后有空也会再更新文字版。 如果你方便访问YouTube,也可以前往:https://youtu.be/s4sEJ7rjcTE 观看。
2020年对所有人都是特殊的一年,年初本来有一个很好的开始,结果被疫情打乱了一切。 一月份我回到学校,经过一周的紧张的面试之后终于拿到了Ancestry的实习offer。搞定了找实习这个小boss之后,我开始了理想中的留学生活:我买了车,开始在学校食堂打工,同时计划着春假的旅游计划,女朋友拿到了SU的20Fall offer,除了正在国内肆虐的COVID之外,看起来似乎一切都很好。 二月的时候加州和华州已经开始零散出现一些COVID确诊,纽约这边还是一派祥和,但是大家心里都很清楚纽约市的人口密度和发达的公共交通几乎就是个巨大的培养皿。那时候中国学生已经开始采购口罩和采取防护措施,不过生活还是一切照常。我打工的食堂最初禁止带口罩工作,但后来也默许了。有一次我和带我的老爷子聊了一下,我感觉他们其实也是在关注这个事情的,只不过也没有采取任何的措施。 这样看似正常的生活持续到了三月中,随着纽约的Westchester County出现了社区传播,情况开始急转直下。3月10日学校发了邮件,宣布春假之后开始网课。当时朋友问我春假有什么计划,我苦笑说只能在家自闭了。 接下来几周全美学生陆续开始了全新的Zoom University生活(那时候还是Blackboard University)。那个学期我有一门早8点的课。之前我一直是早上睡到7点40起床,然后出门,卡着点到教室。然而网课让我直接放弃了治疗,变成每天早上8点准时摸出iPad打开Blackboard,然后继续睡到11点( 最要命的还是疫情对实习和全职工作的打击。三月底的时候很多公司开始取消夏季实习的项目,其中不乏一些规模很大的公司直接撕掉了所有全职和实习的offer。我在三月底的时候收到公司邮件,HR在邮件里说实习项目并不打算取消,但是实习生不能在外州远程工作。虽然HR在想办法来让我们的实习项目能够正常进行,也和每个实习生单独约了电话来讨论每个人都情况和需求。但是不能远程工作并不算是一个好消息,这样一来就只剩下了照常和取消两个选项。这相当于是在和病毒赌博,赌在我们onboard的时候疫情能否控制住。 于是我又回到了上学期那种投简历,写作业,刷题的自闭生活。一些公司,如Cloudflare,宣布扩招2020年夏季的实习项目,可惜这些扩招的名额在实习被取消/没找到实习的大军面前显然是杯水车薪。 在做过几个没有回应的OA和几个没有回应的面试之后,公司终于宣布我们将飞到Lehi, UT然后在酒店里远程工作,随即也开始了背调和relocation的手续。5月几乎是我2020年中最轻松的一个月,我准备着租房,搬家和实习的手续,同时看着COVID Tracker上曲线不断下降。那段时间也有报道称美方工作人员正在陆续返回中国的大使馆和领事馆,意味着女朋友或许有机会正常赴美。那段时间大家都在期待着世界很快就能恢复正常,然而后来的展开大家也都看到了。 几乎在我开始实习的同时疫情开始了第二波爆发。其中犹他州也是重灾区之一。当时公司办公室已经开始phrase 1 reopen,结果因为新一轮爆发又停住了。虽然整个过程很艰难,但我们的半onsite式实习也让我获得了独一无二的体验和收获:Ancestry暑期实习记 – Frank’s Weblog 实习接近结束的时候我们得知了一个噩耗——今年公司已经几乎没有HC去convert intern了。今年的new grad秋招异常艰难,一方面是很多公司缩招甚至干脆直接不招new grad了,另一方面有很多本可以拿到return offer的intern因为HC问题没能拿到return,于是也一起加入找工大军,卷上加卷。整个下半年我几乎把所有时间都花在了投简历,刷题,面试上,目前为止的秋招收获了7个面试,其中挂了4个,其余3个还在面。 特朗普几乎可以算是今年除了疫情之外的第二大灾难。年中特朗普的一通操作让中美关系急转直下,从以前的贸易战+隔空对骂直接加速到了互关领事馆。同时又使出了传统艺能——拿职业移民和国际学生开刀。整个7月到9月,各种行政命令和法案一个接一个。在大家忙着写邮件写评论阻止一个政策的时候,下一个又来了。 于是整个下半年几乎最重要的事情就是大选了。大选那一周我几乎什么都没干,天天泡在Twitter和CNN直播上。回想2016年的大选,媒体call给Trump的时候我正在大物实验课上,当时还没有出国读书的打算,完全是抱着看戏的心态。今年却第一次感受到一场选举直接影响着自己的命运。好在经过几次惊心动魄的反转之后,这场闹剧终于可以收场了。 COVID使很多人失去了生命,其带来的各种次生灾害也直接地或间接地改变了许多人的命运。这两张照片拍摄于去年的圣诞节,当时大家都在抱怨2019年太难了,然而现在却无比怀念那个可以自由通航,可以去学校上课的世界。 不过至少黑暗尽头已经出现了曙光,特朗普输掉了大选,疫苗正在铺开接种,job market也在回暖。希望2021年疫情可以结束,找到工作,女朋友能够顺利入学。祝大家2021年平安顺遂。 2020.12.31 于 Syracuse, NY
中文版:Ancestry暑期实习记 Shortly after I got the internship offer from Ancestry, the COVID outbreak flamed across the US. A number of companies rescinded their offers or switched to remote working. However, because of the Nexus Tax restrictions to companies that provide digital subscription products, we are not allowed to work remotely out-of-state (in states other than […]
English Version: My Summer Internship at Ancestry 在我拿到offer之后不久,COVID在美国开始爆发,一时间很多公司开始取消offer或改为远程工作。然而由于Ancestry受到Nexus Tax的限制,我们不被允许在外州(除UT和CA之外的州)远程工作。最后公司决定让我们飞到犹他,然后在公寓里remote onboard。 6月初我从雪城出发,经芝加哥中转到达盐湖城。出乎我意料的是Lehi这个城市虽小,但是还挺繁华的,路边有不少科技公司的建筑,有Cisco,Adobe这样的大厂,还有好多没听说过名字的小公司和Startup。Uber司机告诉我近几年有很多公司从加州搬过来,而且Facebook也准备在这附近建新的数据中心。 Virtual Onboarding第一天是一整天的Zoom Meetings,主要在讲一些公司历史,使命,各种手续,如何使用一些公司的内部系统等等。我在之前一直以为Ancestry是一家生物公司(之前朋友跟我聊天都问我“听说你去了一家生物公司”),但实际上是一家数据公司。Ancestry的主要业务一直是家谱,其中最珍贵的就是家谱数据。家谱数据的来源非常广泛,包括记载出生,死亡,婚姻,移民,服役的文件,有些甚至是用古英语写成的。DNA和Health反而是最近几年才成立的新业务。 第一周主要是熟悉环境,配置电脑等等。同时也得知了我们要做的project。我们组是Performance Engineering组,广义上属于Quality Assurance,负责协助其他组发现和优化产品中的性能问题,其中也包括开发各种性能相关的内部工具。我们的project是一个全新的内部工具,由我和另一个base旧金山的实习生一起完成。项目本身规模不大,但是需要我们从零开始,收集需求、设计、实现、测试和部署这个项目。 其实项目本身并不复杂,但是企业项目需要考虑的东西很多,比如性能,可用性,预算等问题。另外由于这是一个全新的项目,所以组里给了我们很大的灵活度,语言,框架等等都由我们来选,只要是AWS里面有的都可以用。 公司使用的敏捷开发模型是Scrum,刚接触的时候真的是一脸懵逼,用了一段时间也就习惯了。不过我们用的这个Rally系统是真的难用。 配置环境的工作量并不大,基本半天就搞定了。之后的几周时间基本上都用来开会讨论需求,研究AWS里的各种技术,研究这些东西能否适应项目需求,然后写成文档,由大家讨论。 我们一开始在设计的时候经常overdesign,结果manager经常跟我们强调要keep it simple。我们最后选用了AWS Lambda和SQS来实现,这样可以大大降低成本并获得很好的scalability。从技术角度来说Lambda确实不够酷,如果这是一个课程项目的话,我们大可以用各种框架,加上重量级的消息中间件,最后写好扔到Kubernetes上。然而对于公司来说,技术上的炫酷并不等于实用,使用最低的成本来解决实际问题,并且secure delivery是首要的任务。技术选型完成后我们花了大概三周时间写了设计文档出来,给全组review。 设计敲定了之后实现就很简单了,没有遇到太大的困难,唯一的问题是我们把三个Lambda塞在一个项目里,结果包结构很混乱,导致了各种花式import出错,不过好在最后也解决了。还有一个Behavior Question中经常会遇到的情形,我做完一个feature之后mentor不同意我的设计,并且他的意见和现有设计是冲突的,要按照mentor的想法就得大改。最后我搞出了A,B,C三种方案去和mentor讨论,最后选定了比较折中的一个。 除了实现之外还需要做测试,测试的重要性已经是老生常谈了,不过说实话这是我第一次认真写测试(逃。除了单元测试之外还需要用SonarQube来做静态检查,所以code style还是需要注意的,不然SonarQube的报告会很难看。 我一直坚信Python是最好的语言,但是这次实习过程中也确实体会到了Python的一些劣势。Duck typing确实是一把双刃剑,在跟队友协作的时候,如果不看文档就完全不知道传进来的是什么东西,而且代码越灵活,静态检查能发现的问题就越少,很多问题只有在runtime才会显现出来。 到第7周的时候项目基本warp up,我花了三天时间和各种权限问题做斗争,最后终于部署到了production上。公司有专门的一个Jenkins负责Infrastructure Provisioning,只需要把写好的terraform script扔上去即可,非常简单。唯一比较麻烦的是权限问题,AWS的权限系统实在太复杂了,我自己用AWS的时候都是直接根用户一把梭,但在公司里就不能这么乱搞了。而且很多时候我们使用的权限会依赖组里其他工具的权限,比如组里的Jenkins,这时候就需要发PR去改组里Jenkins的权限。 复盘整个开发过程,其实算下来可能也就50%的时间在写代码,剩下的时间在讨论需求,讨论设计,沟通接口,等Jenkins跑pipeline,以及等着别人unblock…那句话说的没错,工作中最难/最累的并不是工作本身,而是与人沟通,即使是天天和机器打交道的SDE工作。 公司的WLB很不错,即使在WFH的情况下大部分人也是到点就下线。我队友是个肝帝,有一段时间经常半夜push代码或者发PR,我有时候也会死磕一个bug到很晚。后来mentor发现了之后还跟我们说不要加班,如果活干不完就告诉他。 不过第一次实习就是远程实习还是很有挑战的。首先开会就是个大问题,公司给我们订的酒店网很差,结果Zoom通话质量超级差,给我本来就不怎么样的英语水平雪上加霜,不过过了一周左右就差不多适应了。 WFH带来的最大问题还是沟通上的问题,如果是在办公室工作,遇到需要别人改个什么东西或是问问题,直接去工位上说就好了,但WFH的时候,一切都只能通过Slack和Zoom解决,通过share screen来debug的体验也并不怎么好。另外吐槽一下Conluence的搜索简直就是摆设,我遇到问题试图自己找文档解决的时候却发现Confluence上根本搜索不出有帮助的东西,最后只能去找mentor要。 之前看了@MF的 EA 游戏公司实习记 Manfong’s Blog ,里面提到了公司举办的各种各样的活动,非常羡慕。我司在往年的8月第一周会有为期3-5天的Intern Days,旧金山office的intern也会飞到Lehi,参加各种活动。可惜的是今年因为COVID,整个Intern Day被削减到只剩下Executive Intern Presentation和Feedback Session。不过我们的项目有幸被选中去做presentation,相当于也是得到了认可。 虽说是远程工作,但是所有Base Lehi的实习生都住在同一地方,所以周末还可以一起烧烤,或者出去玩。大家第一次见面的时候还非常注意Social Distancing,然而过几次之后就放弃治疗了… 酒店大厅里有个小柜子,里面全是桌游(?) […]
这学期的Object Oriented Design课程要求以组为单位做一个项目,我们的项目前端使用Vuejs,后端使用Spring Boot,通过REST API通信。 有一天前端的朋友告诉我她在调试的时候遇到了一个问题。她在本地运行Vue项目,连接位于AWS上的API。这是一个跨域请求,浏览器会首先发送一个Preflight请求来预检。奇怪的是虽然Preflight请求正常返回了200,但是后面的请求却并没有继续进行。 * Preflight request 术语表 | MDN 我试图在我的电脑上复现这个问题,然而我的电脑上却一切正常,完全不能重现。 经过各种尝试之后找到了一个突破口——出现问题的这个接口是通过Cloudflare和nginx转发的,而换成直接从Docker暴露出来的8080端口则一切正常。 分析请求 第一步,先来对比成功的和失败的请求的header中的属性。稳定版Chrome默认是不显示status为200的Preflight请求的,需要Canary版Chrome才行。 我按照朋友给我发来的截图通过cURL构造出请求,并从我自己的Chrome调试器中将成功的请求导出为cURL。分别运行一次,结果如图: 左侧的是失败的请求,右侧是成功的请求。 发现原因在于失败的请求的响应头中没有包含access-control-allow-origin和access-control-allow-method这两个属性(图中右下刷红),所以这个Preflight请求所得到的结果相当于是无效的。 定位问题 接下来我们需要定位问题所在,我的服务器上的环境如下: 但是为了方便调试,其中每一个节点都是可以从公网访问的,因此我将其划分为三个场景,分别进行测试,下文中提到的“场景x”均对应下表中的编号。 场景: 编号 入口 upstream 1 https://api.app.com cloudflare:443->nginx:80>docker:8080->spring boot:8080 2 http://aws.app.com nginx:80>docker:8080->spring boot:8080 3 http://aws.app.com:8080 docker:8080->spring boot:8080 我使用cURL分别测试三种场景,结果如图 这样我们可以将问题定位到nginx。 但是回顾上面的第一次测试,我们可以发现nginx并不是影响问题的唯一变量。另一个变量是请求的origin和referer。 排除掉干扰因素Cloudflare和Docker后,我们将这个问题的变量控制在两个:请求是否经过nginx,以及客户端端口号是否为8080。 得到如下表格: 经过nginx 不经过nginx origin端口为8080 ❌ ✅ origin端口为8081 ✅ ✅ 分析nginx 我们着重分析经过nginx的这两个场景。 首先分析从浏览器到nginx的这段路程。 浏览器发起CORS […]
这学期的Object Oriented Design课程的项目我使用Docker来做打包和部署,过去我都是手动ssh到服务器上执行Docker命令来部署新版本。但是这种方式对CI系统则不太友好,我们的确可以让CI系统使用ssh登录到目标主机并执行Docker命令,但是这样就涉及到密码,密钥及sudo的安全性问题,(主要是不够优雅。 这一次我使用了Docker Remote Host来解决这个问题。Remote Host可以使我们无需ssh登录服务器,仅通过网络即可控制目标主机上的docker daemon。 首先我们需要了解Docker的命令是如何执行的 Docker主要分为两部分,daemon和client。 Docker daemon (dockerd) 监听Docker API的请求,并管理镜像,容器,网络,卷等Docker对象。daemon 也可以和其他Docker daemon通信来管理Docker服务。 Docker client (docker) 是与Docker引擎交互的主要方式。当使用类似于docker run的命令时,client将命令通过Docker API发送到dockerd。 一个Docker client可以控制多个Docker daemon,显然我们也可以使用本地的Docker的Docker client控制远程的Docker daemon。 Docker client与Docker daemon的交互可以通过unix socket或TCP socket。通常情况下使用的是unix socket(/var/run/docker.sock),如果要使用TCP socket则需要另外做一些配置。 开启Remote Host有三种方式:HTTP,HTTPS和SSH。其中最简单的办法是HTTP Socket。 HTTP 编辑/etc/docker/daemon.json,如果没有则新建一个。 编辑/etc/systemd/system/docker.service.d/override.conf ,如果没有则新建一个。 reload并重启服务 这时执行netstat -a应该可以看到Docker daemon在监听0.0.0.0:2375。 确保目标主机防火墙放行了TCP 2375端口,就可以尝试连接远程主机上的Docker daemon了。 如果能正确打印出目标主机的Docker daemon信息,说明成功。 TLS 由于HTTP是明文协议,并且直接暴露到公网,没有任何的保护,这会带来一些安全问题。所以这里我们使用TLS来保护HTTP。 通常情况下我们需要手动使用OpenSSL来签发证书,但是GitHub上有位朋友提供了一个Docker镜像,我们可以使用这个镜像来自动配置Docker的HTTP Socket及签发证书。 […]
前段时间我拿到了一个Take Home Project,其中一个题目的内容和在AWS VPC中规划子网(Subnet)有关。借此机会我们可以了解一下AWS的网络架构。 上图是一个典型的AWS网络架构,本文中我们将介绍上图中各组件的概念及用途。 区域(Region)与可用区(Availability Zone) 每个AWS区域是一个单独的地理区域,比如位于North Virginia的us-east-1和位于North California的us-west-1。每一个区域完全独立。 每个区域中包含若干可用区(图中橙色虚线),使用形如us-east-1a的标识。每个可用区都是独立的,但区域内的可用区通过低延迟链接相连。 Virtual Private Cloud VPC是AWS网络组件的重要组成部分。VPC使用户可以自行创建一个与AWS网络逻辑隔离的私有网络,并且控制整个私有网络的配置,包括IP地址分配,子网,路由表以及网关。我们可以将其理解为一个私有的局域网。VPC不能横跨区域,但可以横跨同一区域内的多个可用区。 当创建VPC时,默认的配置如下: When we create a default VPC, we do the following to set it up for you: Create a VPC with a size /16 IPv4 CIDR block (172.31.0.0/16). This provides up to 65,536 private IPv4 addresses. Create a size /20 […]
前文提到,我的秋季学期在无数简历石沉大海之后,以面挂了亚麻VO的惨淡结局收场。 我当时为了亚麻的VO改了回国的机票,所以回国的时间总共只有十几天。虽然我在回去之前发誓假期要好好刷题,不过大部分的时间都在外面浪,所以爆肝刷题计划失败( 虽然题没怎么刷,不过简历倒是投了不少。当时春招刚开启,每天都能看到有新的公司放出职位来。这次我在投简历的时候也不仅限于SDE了,DevOps,SRE相关的职位我也投了不少。 因为这期间投的大多是小公司,回复的速度比大厂快了很多。形式大多是先HR电面,再进行OA和技术电面。我在寒假期间收到了一个VO和1个HR电面,分别来自Ancestry和Shipt,再加上之前就已经约好的Naveego。相对来说都是比较冷门的公司,我把这些面试都排在了开学第一周。 我在一月初飞回美国之后在纽约玩了两天,在一天早上起来刷LinkedIn的时候看到了一个Quicken Loan的SDE职位,就顺手投了一个。令我没想到的是当天上午,在我坐在1线地铁上前往Battery Park的途中收到了HR发来的短信(是的,短信),和我约了HR电面,同样在开学第一周。 在纽约浪了两天之后回到雪城,开始准备第一周的一堆面试。 第一个电面是Quicken Loan的,公司位于底特律,是全美最大的房地产贷款公司。其实各个公司的HR电面内容都差不太多,大致是HR和candidate相互了解的一个过程。HR给我讲了不少关于这个职位的事情,技术栈感觉比JD上看到的要好一些,JD上列举了很多.NET相关内容,令人望而却步。 然后HR给我介绍了实习生的一些福利,比如管住宿,有shuttle之类的,然后问我对每小时工资的期望是多少。我当时有些懵,第一次见到在电面里问工资期望的,于是给了一个很大的范围,说10-30我都能接受。然后HR说我们的时薪是xx刀,你觉得ok吗?实际上这并不是一个有吸引力的薪资,毕竟学校食堂打工都有$13.75/hr。 通话结束之前HR说会把关于我的材料交给技术团队,然后在下个周三给我答复,然而一连鸽了两个周三之后,直到第二个周五终于接到电话,告诉我他们决定move forward with other candidates. 我:…… 第二个是Shipt的DevOps的HR电面。Shipt是一家做超市外送的公司,此前被Target收购了,DevOps Intern职位位于Birmingham, AL。电面过程中HR介绍了技术方面的很多信息,很有吸引力。 结束的时候HR给我发了一个Take Home Project,和AWS基础设施自动化相关。难度不算大,但是很有挑战性并且很考验自学能力。我从这个Take Home Project中也学到了不少东西,我之前对DevOps的理解更侧重于软件方面(测试,构建,交付等等),突然让我和基础设施和网络打交道还是有些懵逼的。在做Project的过程中提升了知识水平并且捡起了遗忘很久的计算机网络。 Project提交之后不久收到了VO邀请,不过那时候已经接了Ancestry的offer就没有继续面下去。 之后紧接着是Ancestry Performance Engineer Intern的VO,通过Zoom进行,有三个面试官,Performance Engineering组的Director和印度小哥在一个会议室,另一个全程没露面。 第一件事情是介绍自己过去的实习和项目经历等,我首先讲了过去的一些项目经历,包括PHP,Django的Web后端开发,和SQL相关的一些优化,还有DevOps以及运维相关的一些经历。然后提到了我是如何优化我的WordPress博客,这个博客已经运行超过5年了,我花了很大的精力才把它优化到现在的程度,涉及到前端,系统,网络等等。可惜的是刚开始有些紧张,很多东西没讲清楚。面试官结合我说的东西问了几个follow up,答得还算顺利。 然后面试官给我介绍了Performance Engineering具体都做些什么事情,以及和SDE的一些区别,接下来就是各种技术问题了,整个面试过程中没有Coding,不过内容覆盖很广,从前端到后端到操作系统到网络无所不含。就像在Facebook做SRE的朋友和我说的一样,SRE很看重知识的广度。不过这类问题的好处在于只要确实接触过,哪怕记不清细节了也可以答得八九不离十,不像算法题一样,会就是会,不会就是不会。 最后的提问环节我问了问关于公司的技术方面的内容,以及公司的的产品。我之前一直以为Ancestry是和23andMe一样的基因检测公司,但实际上Ancestry的产品更偏向于家谱分析,而不是像23andMe那样更侧重个体的数据。 面试结束前Director告诉我会在1-2周后给我答复。一周后收到offer,考虑之后决定接受。 最后一个是Naveego的SDE职位。这家公司早在去年12月初就已经给我发了HR电面的邀请,只不过可选的时间太少约到了1月中。 电话的主要内容是让我介绍了自己过去的项目和实习经历,对未来发展方向的计划等等,都是些很常规的BQ。我说完之后她就开始介绍他们的公司,技术栈等等。Naveego的技术栈很新,以Go,gRPC,还有一些微服务方面的技术为主,实习生所用的技术基本也是这些,具体工作内容大部分是开发一些内部工具之类的,确实和大厂相比会更有impact。最后的环节是我来提问,我问了一些关于他们的产品(类似于数据仓库)的一些内容,还有一些技术方面的东西。总而言之确实是一个很有吸引力的实习机会。 在最后HR说他一会会把第二个Coding Challenge发给我,同样也是Codewar上面的一道题,不过有两条额外的限制:一是必须用Golang完成,另一个是不能使用循环(for,while)。总体难度不大,但是很新奇。 我当天就把Coding Challenge写完并给HR发了邮件,等待两周之后收到拒信。其实被拒并不太意外,早在12月初的时候地里就已经有人发帖说实习职位已经招满了,在电面之后直接收到了拒信。 复盘 首先是申请阶段,美国这边暑期实习的时间线比国内要早很多,很多大厂在8-9月就会放出职位,对于很多大厂来说(尤其是Amazon和Google),一般只要及时内推通常都能拿到面试,这是个上岸的最佳时机。如果投的太晚基本上就很难被捞起来了。然而我当时并没有意识到这一点,所以错过了这个最佳时机。 其实我们这里的大多数人都是抱着到了美国再开始找实习的心态的。实际上这样是不可取的,因为等到了美国安顿下来,再动手准备简历,联系内推和刷题,基本上就已经错过大厂的最佳时机了。所以在到达美国之前就应该准备好的有: 一份基本可用的简历。因为简历基本上都是边改边投,也不需要一开始就十全十美,及时投比完美的简历更重要。 完整的LinkedIn Profile和尽可能多的Connections,方便联系内推。 Leetcode刷题量,越多越好。 女生在5-6月就要开始准备GHC了。 另外就是一个老生常谈的问题,通常来说内推的效果一定远远大于海投,然而面临的问题是如果找不熟悉的人内推(比如LinkedIn上的或者地里的),内推的过程可能会很长,甚至对方会完全没有回应。这样就会很耽误事情。 在我看来,如果是大厂,一定要找内推,对于Google,Amazon这样的大厂,只要及时内推基本都会有面试。而且相对来说大厂的内推也不会很难找。 对于小厂来说,内推可能不会有大厂那么好找,可以先试着联系内推,如果不行则及时海投。很多小厂完全就是拼手速,我之前见过一家公司,因为LinkedIn给我发的Job Alert晚了大约10个小时,那个职位就已经投了300+人了,何况这300+还仅仅是通过LinkedIn投的数量。 […]
这几天RSS feed上几乎被各种年终总结刷屏了,不过我没有写年终总结的习惯,就写写这半年在美国学习和找工的总结吧。 学习 我们专业的课程安排是每个人都是有相同的四门必修课(Data Structure & Algorithm, SoC Design, Object Oriented Design, Computer Architecture),在此基础上可以自由选择选修课,整个项目一共是30学分,也就是10门课。 国际学生在第一学期能且仅能选三门课,其中有两门(Data Structure & Algorithm, SoC Design)是被学校安排的。开学前我们曾经去问过SoC Design这门课能不能drop掉,等之后再选,被告知不行。最终我选的三门课是Data Structure & Algorithm, SoC Design, 和Database。 Roger Chen的Data Structure & Algorithm被往届学生评价为“值回票价的课程”。这门课确实很有用,尤其是对于我这样本科的数据结构基础打得并不扎实的学生来说,这门课覆盖了很多面试中高频但是之前没有深入了解过的内容,比如堆,树,等等。 不过这门课的load也确实大。这门课程一共六个作业,内容分别是链表merge sort,堆优化的prim和dijkstra,DFS/BFS,AVL,红黑树。前四个作业如果顺利的话平均能在3-5小时内搞定,最后两个作业基本上没有十几甚至20+小时是搞不定的。 另外期中和期末的考试也很硬核,期末考试的时候有两个题目分别是AVL和红黑树的操作(插入,删除等等)。我在复习的时候以为考试的时候一棵树最多也就10个节点,然而我看到题目就傻逼了,这两道题给出的树有30+个节点,要求做将近10个操作,我做题的时候光画树就画了好久。考完试之后一度感觉要挂了(核心课得C就要重修并且不能申请CPT),不过最后分数还不错,应该是curve了。但说实话,和学会30+个节点的红黑树操作相比,我宁可用这些时间多刷几道Leetcode( 另一门课必修课是Intro to SoC Design,这门课真的是一言难尽,作为一个纯软件背景的学生,每天上课都像是在听天书。好在有大佬carry加上考试不难,最后成绩还不错。 选修课我选的是Database Management System,当时是觉得已经被学校选了两门硬课了,需要选一个水课中和一下,不然担心第一学期会翻车。这门课虽然确实算是水课,但是收获很大。内容包括各种花式SQL查询,数据库设计,安全,Function,Stored Procedures等等。我之前用Laravel和Django都开发过不少项目,其中数据库的部分几乎都是使用ORM来获取数据,然后用一堆嵌套的for循环来处理数据,有时候就直接用一个text类型的字段来存JSON。上完这门课我才明白我之前使用数据库的方式有多么野蛮。 找工 相比之下找工的情况就要惨的多。目前的进展是海投及内推 100+家公司,其中7OA(白嫖不算在内),4面试,0offer。拿到面试的有下面几家: Riot Games System Engineer HR电面挂 最初了解到这家公司是在和朋友聊天,聊到有人投了他家正在做OA,于是回去在Handshake上海投了一下,同时投了SDE和System Engineer。我在投的当天才知道LOL是他家的。 很快来了OA,题目是四道种花题,难度不算太大,但是需要自己处理stdin的输入非常烦人。拳头是知名白嫖OA公司,但是抱着试一试的心态还是做了,testcase全过。 OA交上两周之后收到了HR的邮件,约了一个45分钟的phone […]
回国的飞机25号下午从JFK起飞,我买了当天凌晨1:20从雪城出发的灰狗,打算第二天早晨到纽约之后在中城闲逛一会再坐地铁去机场。
因为我之前去纽约就是坐的灰狗,当时感觉体验还是很不错的,而且我确认了当天没有雨或雪,也就没有太担心延误的问题。
我当天提前一个小时左右到了灰狗站,然而等到1点半车还没到,过了一会车站工作人员广播说1:20的车延误了,让这班车的乘客去柜台。柜台的黑叔叔跟我们说这班车从多伦多途经Buffalo开过来,但是不知道为什么他没有在这里停。所以如果要去纽约的话可以上2:15的那一班。
我都惊了,居然还有这种操作的?
最近一次Data Structure & Algorithm课程的作业是要求实现Dijkstra算法,并且其中的排序部分要使用最小堆来实现。 Dijkstra的基本思路如下 1. 初始时,S只包含起点s;U包含除s外的其他顶点,且U中顶点的距离为”起点s到该顶点的距离”[例如,U中顶点v的距离为(s,v)的长度,然后s和v不相邻,则v的距离为∞]。 2.从U中选出”距离最短的顶点k”,并将顶点k加入到S中;同时,从U中移除顶点。 3.更新U中各个顶点到起点s的距离。之所以更新U中顶点的距离,是由于上一步中确定了k是求出最短路径的顶点,从而可以利用k来更新其它顶点的距离;例如,(s,v)的距离可能大于(s,k)+(k,v)的距离。 4.重复步骤(2)和(3),直到遍历完所有顶点。 我们的作业要求是在步骤2中的“选出距离最短的顶点”要用堆来实现。也就是说,我们需要维护一个最小堆,里面包含有起点到各个尚未访问到的顶点的(目前为止的)最短距离。每一次更新各个顶点到起点的距离时,都要更新堆中所有顶点的值。这样,每次寻找距离最短的顶点时,直接取出堆的根即可,省去了每次都要遍历一遍来寻找最小值。 我们要求的实现逻辑是这样的:维护一个如下的routing table,其中存储了每个节点的id,是否已经访问过,到出发点的最短距离,和该节点在堆中对应的位置。每次访问图中的一个节点时,都要在routing table中更新该节点及其所有后继节点的距离和其他信息。 node 0 1 2 is_visited true true false cost 0 1 3 heap_position 0 1 2 我写完提交之后,被告知我的solution在小用例上部分正确,大用例上报vector越界的运行时错误。 那就开工吧! 我首先尝试了几个相对小的用例,但是无法复现问题。于是我用生成器生成了一个有100条边的图,果然vector越界的问题出现了。经过单步调试之后发现在图没有走完的时候,堆中的元素已经被取干净了。 又经过了长达数十分钟的单步调试,我终于发现问题在于生成的测试样例——其中一些节点只有出度而没有入度,也就是说,有些点是永远无法到达的,而我的逻辑是将所有点走完之后才能结束循环,因此堆中的元素会在循环跳出之前就消耗掉,导致vector越界。 我的解决方法十分粗暴——当堆中元素为空时即跳出循环,这样就不会有越界问题了。 然而事情并没有结束。 修改之后,我发现在小的用例上能够得出正确结果,而在稍大一些的用例上虽然不会有运行时错误,但是答案却是错的。最麻烦的事情是——由于用例过大不可能单步去调,所以完全没法定位问题出在哪。我绞尽脑汁想了一些奇奇怪怪的小型用例,但是还是无法复现问题。 于是我只能把程序拆解成若干部分,依次排查问题。 我仿照单元测试的方法,单独写了一个函数去调用堆的插入,删除,和修改这三个方法,并且通过随机生成巨大的测试样例来验证堆是否合法。经过一通测试还真的发现了问题,我发现从堆中移除和修改元素的方法都存在缺陷,有时会导致堆不合法。经过修改之后,和堆相关的所有方法终于可以确定可用了。 然而事情还是没有结束。当我满心欢喜地再次测试整个程序时,我发现虽然已经修正了一些错误,但是输出的答案依然没变。于是现在又进入到了死胡同。 由于和堆相关的算法都已经确认正确,根据福尔摩斯推理法,问题只能出在Dijkstra算法本身的实现上——即使看起来这里不应该出现问题。然而我把我实现的算法和课上的笔记和网上的资料仔细对比,发现算法实现并没有问题。 到目前为止,我所推测的所有可能性都已经被推翻,所以也只能单步调试了。我选了几个可能出现问题节点打了断点,然而还是没有发现问题。直到无意间,我发现在循环结束时,routing table中的一部分节点还没有被访问到。 经过一番检查发现,由于每次更新堆中节点的值的时候,堆中节点的位置会改变,然而routing table中记录的该节点在堆中的位置却没有改变。导致堆中一些节点的值被错误地更新而被提前取出。解决方法很简单,只要每次查找堆中元素时使用值查找而不是使用索引查找即可。只不过这个做法有些tricky,并且会降低一些性能,不过确实能够解决问题。
自从8月中来到雪城之后,虽然同在纽约州,但是和纽约市还有近300mi的距离。安排好入学和生活上的事情之后,我终于在9月初如愿去了纽约。 我乘坐灰狗巴士从雪城出发,一共大约5小时的车程。灰狗的舒适程度比我预想的要好不少,车上有WiFi,电源插座和卫生间。在路上看到对面的高速公路上的一辆装着大型机械的卡车,挂着Oversize Load的旗子,和美卡里面的Heavy Cargo Mod一模一样,太帅了。 大约中午12点的时候巴士到达了Port Authority的巴士站。车站位于曼哈顿中城的正中心的地下,出门之后的斜前方就是纽约时报的总部。纽约时报的总部原本位于时报广场,后来才迁到这里。 走几分钟就是时报广场。在时报广场旁边的三车道小路上,汽车在车道上行驶,自行车和滑板就在车流中穿行。曼哈顿的车流中私家的小汽车反而是少数,有至少2/3都是货车和出租车,甚至还有大卡车——是的,就是美国卡车模拟器里的那种大卡车。我简直不敢相信大卡车居然能在工作日的白天开进曼哈顿的中心区域。即使是北京这样糟糕的交通状况也完全无法和曼哈顿相比。另外我强烈建议“中国式过马路”改名为“纽约式过马路”。纽约市的人行红绿灯就像美国高速公路上的限速标志一样,完全是个摆设。 楼顶的红色H&M真的很抢镜,在无数纽约夜景的照片中看到过它。 我一直很不理解为什么曼哈顿的很多马路中间会有烟囱,后来房东告诉我曼哈顿的道路下方有蒸汽管道,以防止道路在冬天结冰。实在是太朋克了。 中午去附近吃了Shake Shack。整个店里人爆满,以至于完全没有地方坐。不过真的很好吃,可惜雪城附近没有。 在曼哈顿使用导航简直就是噩梦,因为高楼阻挡了GPS信号,经常会出现按照导航上的路线走到目的地之后发现这里并不是我要去的地方,再打开导航的时候,定位的蓝点直接在我眼皮底下漂移到了两个街区之外。 因为今天天气不好,我放弃了下午参观帝国大厦的计划,而是先去附近的Intrepid Museum。Intrepid Museum距离时报广场大约有5个街区左右的距离,有一条公交线路可以直达。 公交和地铁共用MTA的交通卡,所以我找了一个附近的地铁站,在机器上买了一张MTA卡。目前纽约的交通卡系统是1992年推出的,一直沿用到现在。这张MTA卡是磁条卡,卡本身只是一张薄薄的塑料,看起来很容易消磁( 不过MTA已经在一些车站开始测试OMNY系统——一种非接触式的支付系统。不过OMNY依赖的是非接触式的信用卡和借记卡,可能使用的是类似于Visa PayWave一类的技术,而不是像北京,上海,东京等城市使用专门发行的卡片。 现在我终于明白了为什么之前苹果的某次发布会上宣布Apple Pay将支持纽约和波特兰的地铁需要花费如此长的时间——因为这并不仅仅是将实体卡片移植到Apple Pay上,而是涉及到整个车站基础设施的改造。 坐公交车到了哈德逊河边的84号码头附近。开车的黑人小哥在我把交通卡插反的时候发出了类似救护车警报的声音( 雪城的公交车站大部分是以车站所在的两个街道来命名(比如University Pl & College Pl)。我本以为是因为雪城过于荒凉而无法使用像国内一样的方式来命名公交车站,然而到了纽约之后发现纽约的公交车站也都是以街道来命名的,一般是xx St & xx Ave.。相比雪城一小时一班的公交车,纽约的公共交通真是太方便了。 USS Intrepid是美国海军的一艘退役航母,在二战太平洋战场立下了显赫战功。退役后被改造为博物馆。在机库甲板有一个大屏幕,上面播放了从前的船员的回忆录。 战后,经过改装之后的USS Intrepid执行了一系列太空任务。下图的窗外是一个Gemini III的模型。 坐电梯向上一层就是飞行甲板。背后是被雾气笼罩的曼哈顿中城,幸好没去帝国大厦。 飞行甲板上陈列了一系列的飞机。我本来以为这些都是模型,但实际上是经过翻新之后的真飞机。 可变形后掠翼的F14 Tomcat,一直很喜欢这个机型。 蓝天使的FA18 Hornet 飞行甲板的后方有一个库房一样的建筑,这里保存着企业号航天飞机。准确地说,企业号其实是一架用于验证航天飞机设计的原型机,并没有搭载隔热板,雷达和引擎,因此不能进行太空飞行,只能由一架经过改装的波音747搭载起飞。 在哥伦比亚号号事故发生之后,调查团队曾经用企业号上的隔热板进行实验来调查事故原因。事实证明哥伦比亚号失事的原因是发射过程中从主油箱上掉下来的泡沫击穿了机翼前缘的隔热板,导致哥伦比亚号再入大气层时缺少隔热板的保护而被烧毁。 我现在还记得小学的时候看过一个关于哥伦比亚号的纪录片(大概是重返危机现场系列?),片中的调查人员使用“chicken gun”,一种大口径,使用压缩空气作为动力发射物体的机器向从企业号上拆下来的左翼前缘(Wing Leading Edge Section)发射泡沫碎片,证实了发射过程中击中哥伦比亚号机翼前缘的泡沫碎片有损坏隔热板的能力和可能性。 航母的舰桥和控制室。 从Intrepid Museum出来,坐公交去换地铁。我因为听串了报站提早了一站下车,当我走到我要去的车站的时候,公交车还在路上堵着( 纽约的地铁入口都超级小,这个应该已经算是很大的了。乘坐E线往Downtown方向到头就是Ground Zero和新World […]
在诸多人的安利下,趁着Labor Day的长周末去了趟New York State Fair。纽约的State Fair是全美国第一个大规模的农畜产品展,最早一届是在1841年,就是在雪城举办的。每年只开两周,今年是从八月22号开到九月3号,错过了只能再等下一年。 因为当天是本年度State Fair的倒数第三天,再加上是个周末,在高速上就开始堵车,堵了超过3mi的路程。路边有几个房车营地,里面停满了房车。恐怕真的有不少人是拖家带口开着房车过来的。 从10号门进入Fairground之后,首先看到的是一个牛棚。 我们在路上堵着的时候,同行的学长跟我说: “我去年来的时候,他们在牛棚里搭了个摄像头在YouTube上直播。他也不说话,就播那个牛。” “美国人真的是…他们就过来看看牛,看看猪赛跑,就觉得特别快乐。我去年来的时候,牛棚里边有一个看台,我去的时候就看见一个人坐在那看牛,等我下午走的时候他还在那看牛。” 我去YouTube搜了一下,果真今年还有牛棚的直播。(现在Fair已经结束了,但是YouTube上应该有录播。) 这边是奶牛棚子 往前走有一个小动物园,这里有卖胡萝卜或谷物,用来喂动物。 羊 草泥马 袋鼠 长颈鹿 动物园不大,一会就逛完了。出来以后是一片卖小吃的区域,首先看到的是油炸奥利奥。可能是美国人觉得奥利奥的热量还不够高,所以需要再油炸一下。太黑暗了,没敢吃。 最后点了个Shrimp Po Boy,还挺好吃的。 出来以后就到了游乐区域。 这面墙让我想起了小学英语书上的插图。 这是一个迷你过山车,这个车头的造型让我想起了之前一个视频博主拍的保定动物园。 15刀一次的微型摩天轮,告辞。 平地缆车——可能这个缆车的作用就是将游客从Fairground的一头运输到另一头。 这个项目是如果成功地在这个单杠上挂2分钟,则可以得到一个(超级丑的)毛绒玩具。 一个更微型的过山车 马戏。这个马戏准确地说应该是杂技,没有动物表演。可惜现场不让拍照。杂技真的很厉害,不过是真的土。 这里是比较有意思的一个部分,这里展出了从40年代到现在的每一种纽约州警察的巡逻车。旁边也有NY State Police的什么表演,但是人太多了,没有挤进去。 旁边是一小段废弃铁路,上面展出了几节比较有年代的火车车厢。 里面的内饰都是原汁原味的,除了一些必要的维修之外没有翻新过。这个车厢有种东方快车的那种感觉。 从火车展览出来之后,正好赶上4点的小猪赛跑。我因为没找到场地绕了点路,所以只赶上最后一场。恭喜这位名为Peter的小猪获得冠军。(点击播放视频) 但是这个土味集会,让100多万人从纽约州的各处远道而来(纽约州总人口1900万,雪城人口15万)。可能这就是美国人的快乐吧(
今天是我到达美国的整一个月,我终于把这篇鸽了好几周的文章写完了。 前文提到,经过近两年的准备和申请,我取得了雪城大学MSCE项目的录取。 8月14号,我从首都机场登上了飞往纽约的航班。经过13个小时的飞行,飞机在JFK机场停稳的时候已经将近3点了,飞往雪城的航班在两小时后起飞,于是只能在机场里一路狂奔,连一张照片都没来得及拍。幸运的是所有持F1签证的学生都被分流到一个单独的房间里,检查护照和I20等,平均每个人只用了不到一分钟。 美国的安检看起来很严格,和国内一样需要把液体和电子产品拿出来单独过机器,还需要脱掉鞋和外套。然而,如果注册了TSA Precheck,则不需要这一系列繁琐操作。总之不充值是不会变强的。 总而言之,一番折腾之后终于顺利到达了JetBlue的登机口。机型是巴航工业的E190,比320和737稍小一圈。 然而到了登机口才想起来,我在过完安检之后把iPad落在了安检机上。不过万幸的是我的iPad被收到了一个TSA工作人员那里,我当着他的面解锁iPad之后就还给我了。 大约一个多小时的飞行之后就到达了雪城机场,这里有个微小的航空博物馆。这个屏幕上在播放一个纪录片,讲的雪城之前的工业发展,包括公路,铁路,航空,制造等等。纽约上州曾经也是美国的工业中心,不过这些工业现在都衰落了,目前雪城占比最高的产业是教育和医疗。 第二天去国际学生中心Check In,是在图书馆地下的一个小房间。我在到达美国之前已经填过了一个表格,这个表格在提交之后会自动在学校的Orange Tracker,一个Jira服务器上创建一个Issue。当我打印好I94,去Check in时,对方直接用我的信息搜索到当时的Issue,并处理相关的信息,就算完成了报到。我实在是想不到Jira系统还可以这么用。 这个像城堡的建筑是Crouse College,建于1889年。目前属于College of Visual and Performing Art。 在Hall of Language前面有一个洛克比空难的纪念碑。在1988年12月21日,35名雪城大学学生在从伦敦飞往纽约的Pan Am 103航班炸弹袭击中遇难。 这个是学校Bookstore,虽然叫Bookstore,但是从书,文具,生活用品,衣服,学校周边,到电子设备,几乎什么都卖。 结束之后去附近的Chase银行开了户。万幸的是我去的比较早,只需要等20分钟。这个时候去开户的基本上都是刚刚到达的国际学生,对美国的银行不怎么了解,所以整个过程还是需要挺长时间的。接待我的工作人员给我讲了一堆的东西,例如checking和saving账户,手续费,如何进行国际转账,还有好多奇奇怪怪的增值服务,之后就是签署冗长的paperwork。虽然是叫paperwork,不过现在都是使用PDF电子签署的了,如果真的打印成纸质版恐怕会超级厚。整个过程大约花费了半小时左右。 由于Visa借记卡要现制卡,所以大约一周之后才能收到实体的卡片。不过从Chase APP里可以直接把卡片添加进Apple Pay,这样在卡片收到之前就可以用了。 Chase还提供了QuickPay功能,如果对方也拥有Chase银行的账户,只需要输入对方开户时的邮箱即可给对方转账。Chase的APP真的很好用,比国内一众银行APP高到不知道哪里去了。 下午的Seminar结束之后在图书馆摸了会鱼。这里的公用电脑都是使用学校的微软组织账户登陆系统,登陆后会自动关联学校的打印计费系统。 17号是公寓入住日。住了三天酒店之后终于可以搬进公寓了。我租住的公寓在学校以南大概一英里多一点的地方,我们打了个Lyft XL以便塞进三个人和5个行李箱。来的是一个黑人大叔开着一辆GMC全尺寸SUV。 我们到了公寓之后领了钥匙,一番检查之后发现几乎所有东西都是坏的,包括空调,洗衣机,烘干机,水龙头,卧室门锁等等。维护的小哥跟我说这个公寓是两年以前新建的,但今年才第一次全部租出去。所以我们这个unit之前是没有人住过的,也就没有人来帮我们测试这些设备是否可用。洗衣机花了两周时间才修好,两个维修小哥来了不下10次,都已经认识我了。 简单收拾了一下东西之后,出发去附近的Target采购。虽说是附近,但实际上是在8英里之外,而且这已经是最近的大型超市了。 只买了一些最必要的生活用品,不然太多了就搬不动了。Google Home Mini只要29美元,买爆。美国的物价越小件的物品越贵,很多小物件基本是和国内价格一样的数字但是把货币单位换成美元。所以29美元基本上相当于两个枕头的价格。 采购完之后去隔壁的Verizon门店办了手机卡。我在离开北京之前办了中国电信美洲的一美元套餐,打算先作为第一个月的临时号码使用。然而这个号码显然是一个重启号码,我在JFK落地还没下飞机的时候,就接到了中文语音的诈骗电话。之后的几天内我持续地接到美国各地的人打来的Facetime,而且美国人打电话实在太执着了,只要一挂掉就会再打过来,我只好接起来告诉对方你打错号码了。 中文互联网上关于美国的手机通讯方面的信息多少有些过时,很多文章给读者传递的信息是美国的电信运营商都是需要绑定若干年的合约,资费高,流量少,信号又差。可能若干年前的情况确实是这样,但实际上,自从T-Mobile推出了无合约的套餐之后,其他几大运营商也陆续在跟进,推出无合约的套餐。目前AT&T和Verizon的8G流量的Prepaid Plan的价格基本上维持在$30-40左右,并不比中国电信美洲卡贵很多,但是有远比电信美洲好的信号覆盖和服务。 回到公寓之后,维护小哥拿来了一个全新的门锁换上,然后告诉我他的同事一会会过来配置这个门锁。因为他们今天5点下班,如果5点之前完不成的话,就只能等到周一了。不过万幸的是他们确实在5点之前把门锁修好了。 晚上吃了一个Yelp评分2颗星的披萨店,说实话东西还是挺好吃的,只不过需要一直催,否则店员就会把订单忘掉。 周末的时候抽了半天时间去了Destiny USA,纽约州最大的购物中心。 虽说是纽约州最大,但是实际上商品,饮食和娱乐的丰富程度恐怕还不如朝阳大悦城。Macy’s和JCPenny里面的商品就乱七八糟地散落在货架上,顾客自己拿了商品去收银台结账,而且虽然是周末的下午,整个商场里都没什么人,很冷清。 说实话,美国的商品,尤其是服装的样式真的是让人一言难尽。H&M算是为数不多我能接受的服装品牌,不过我随便挑了几件试了一下,即使是XS号都大了些,看来以后得靠网购优衣库了。 不过Bestbuy的东西超级全,确实逛得很爽(不过好贵 第一周就这么结束了,下周就要正式开始上课了。
前一段时间,我将我使用的云存储从自托管的NextCloud切换到了OneDrive。就在前两天,我在使用Time Machine在两个macOS设备之间迁移数据时,OneDrive中大量的数据丢失,原本25G的数据仅剩下了3个G。 本文记录了该问题发生的过程,问题发生后的解决方案及可能导致这个问题的原因。 过程 我使用的macOS版本为10.14.5 (18F132) ,OneDrive客户端版本为19.062.0331.0010。 我的OneDrive上大约存储了23G的数据,为了节省硬盘空间,大部分的文件都使用文件随选释放了本地空间,只有少量数据存储在本地的磁盘上。 两天之前我购买了2019款MacBook Pro,并使用Time Machine迁移了旧设备的数据。迁移完成后,OneDrive一直处于正在同步状态,我认为是在进行重建索引一类的操作,所以没有特别在意。期间我收到了如下图所示的来自OneDrive的邮件,提示我从OneDrive删除了大量文件。 然而我检查了回收站之后发现回收站中最新的文件是我当天下午删除的一个文件夹,我以为是那个文件夹里的文件较多引起的误报,所以也没有特别在意。 过了大约一小时,同步终于完成了,然而历史操作里有大量的“已从OneDrive删除”的记录,我这时开始感到事情有些不太对劲了。检查文件夹之后发现,所有我使用文件随选释放的文件被从OneDrive云端删除,只留下空的文件夹。 我按照邮件中的指示检查了回收站,但是其中并没有那些丢失的文件。更糟糕的是,由于这些文件已经在本地被释放,所以Time Machine中也没有他们的备份。也就是说,这些文件在问题发生时仅存储在OneDrive上,而他们已经被删除了且无法从回收站找回。 解决方案 万幸的是,虽然这些文件没有出现在回收站中,但是OneDrive高级版提供了“还原OneDrive”的功能,可以将OneDrive的云端还原到之前某一时间点的状态。选择一个问题发生之前的时间点,并点击还原即可,还原文件需要花费一段时间。 不过这个功能需要Office365订阅,如果没有订阅Office365且不想订阅的话,可以使用Office365家庭版的一个月免费试用,记得在开始扣费之前取消订阅。 分析 通过现象很容易推测出,这些使用文件随选释放的文件在使用Time Machine备份及恢复的过程中丢失了某些元数据,导致这些文件被OneDrive客户端视为已从本地删除,因此删除了OneDrive云端上的副本。 文件随选(File On-demand)是一个近期才在macOS上推出的功能,仅支持macOS Mojave以上,且文件系统必须为APFS。然而Time Machine的备份磁盘使用的是HFS+文件系统。最初,我猜测文件随选功能使用了某些仅有APFS支持的特性,导致在使用HFS+的Time Machine中丢失了一些元数据。 为此我做了一个实验,我在OneDrive文件夹中创建了三个文件,分别设置为”保持在本地“,”本地可用“和“仅在线”三种状态,使用xattr命令观察其扩展属性。 从图中可以看出,“仅在线”的文件相比其他文件多了com.apple.metadata.com_apple_backup_excludeItem属性。也就是说,并非因为HFS+不支持某种属性而丢失了数据,而是这些文件根本就没有被备份到Time Machine中。因此,对于被迁移到新设备上的OneDrive客户端来说,这些文件就是被视为从本地删除了,因而连带着删除了OneDrive云端的副本。 总结 虽然这次事件只是虚惊一场,但是OneDrive团队对此负有不可推卸的责任。即使不能在技术角度规避这个问题,也应当在客户端或帮助文档的明显位置提示潜在的风险。 对于用户来说,即使云存储的可靠性比本地硬盘高很多,我们仍然应当意识到过于依赖云存储服务所隐含的风险。对于重要文件,无论任何情况下都应该保持有多份可用的备份,以防万一。
2019Fall的申请季基本结束了。对于各位CS玩家来说,今年申请季非常惨烈。在此记录一下我在这个申请季中的一些感想和踩过的一些坑。如果你也是一名在准备申请美国CS master的准留学生,希望这篇文章能够或多或少给你一些启发。 我的申请结果如下: AD Syracuse CE, WPI CS, Stevens CS, BU MET CS REJ USC CS General, NYU CS, UCI MCS, UCD CS, NEU CS, Rutgers CS, Stony Brook CS, UCSC CS, Syracuse CS 前期准备 前期的准备基本在于标准化成绩,GPA,各种竞赛实习,选校等等。 我从2017年暑假开始准备托福起一共考了三次,分别是100->101->108。总体来说还算顺利。相比之下,GRE考试就艰难了很多。我一共考了4次GRE,分数分别为153+160,153+166,149+162和150+164,呈现出非常迷的波动。然而AW则非常稳定,每一次都是3.5。 我从2018年寒假开始学习GRE,背单词,刷题,从零开始准备到首考大约经过了3个月的时间。4月份首考GRE出现了严重翻车:我理解错了一道数学题的意思,结果在一个题上卡了太长时间导致数学没有做完,只得了160。 我回来之后立即报了最近的考试。21天之后的二战获得还算满意的分数。不过Verbal一点提升都没有。虽然这个分数是够用了,但是还是有些不甘心。后来又报了10月和12月两场GRE。10月的那次发挥得极其糟糕,成绩比首考还差。12月底最后一次因为之前忙于文书和各种事情,几乎没有怎么准备,发挥得也一般。 几次考试下来,我觉得大量的练习对于取得好的托福成绩来说是个必要条件,但是仅仅靠题海战术短时突击很难出分,只能细水长流地培养。和托福相比,GRE难度更高,学习曲线比托福更陡峭,几乎所有人初学GRE时都是对着一整页一个都不认识的单词一脸懵逼,但是GRE反倒更容易通过题海战术突击出分数。 无论基础如何,花大量时间去练习都是不可避免的。所以我认为托福应该尽早开始准备,哪怕大一开始也不早。但是也要注意不要把战线拉得太长,否则会非常疲惫,以至于考试的时候可能都没法进入状态,我第四次GRE时就很明显有这个感觉。 我选校的方案是先确定冲刺学校,再按照后续的成绩确定主申和保底。8月开始选校,最开始定了两个冲刺学校(现在看来应该定位为彩票)USC和NYU。等后面刷分的成绩出来之后,再陆续确定剩余的学校。 申请 每年秋季的申请季实际上从10月就开始了,从11月开始就会有大量学生开始提交网申。我的初期申请过程无比魔幻——从我选择了SUNY Stony Brook开始。 我之前一直认为我的选校存在问题——除了冲刺之外基本就是保底,主申档的学校几乎没有,存在一个大断档(当时还没有选Rutgers,Stony Brook这些)。虽然老师给我推荐了好几个对于我可以作为主申的学校,比如Lehigh,UFL,UMiami等,但是出于各种原因我都没有选。所以我也一直在调查适合我的主申学校。 大约11月中的时候,我了解到了SUNY Stony Brook这所学校。这所学校各方面都不错,无论是专业排名,口碑,地理位置等都很不错,唯一的缺点就是综合排名低了些。录取的门槛也比较符合我的水平。但是这所学校的截止日期是近在咫尺的12月1日。 因为我对文书老师给我的初稿不太满意,我在考完11月24日的托福之后立即开始爆肝文书,为了赶上SBU的12.1的Deadline。然而在Deadline临近的时候,SBU闷声把Deadline改成了1.15,官网上原话是这样的: 说实话,那个时候文书老师写出来的文书实在是一团糟,尤其是CV存在很大的问题。所以我只能选择放弃赶12.1的DDL,把文书按照我自己的思路推翻重写。经过半个月的爆肝,12月中过后文书基本定稿。陆续提交剩余学校的网申。 一旦一所学校的文书定稿,后续的工作基本只有针对每所学校写一些个性化的内容,比如为什么选择这所学校,对某门课或某个老师的兴趣等等。这些工作我前前后后花了大约大半个月时间。说实话我并不知道这个大半个月时间花得值不值得,因为对于一些Rolling审理的学校来说,提前半个月提交就意味着更高的被录取机会。 文书在申请中的重要性是个玄学问题,我们无法知道录取委员会的黑箱中是如何审理学生的材料的。但是在GPA,GT成绩等硬实力已经无法改变的情况下,文书是唯一还可以改变的东西。对于包括科研,项目,实习,职业规划在内的软实力,很大程度上要靠文书来体现。所以我对于文书的看法是:一定要由自己来写初稿,哪怕写中文也可以,然后再交给文书老师来做翻译或润色。 […]
卿è§åæ¯ä¸ç§åæ²»(Divide&Conquer)çææ³ãå¨å°å¤§é®é¢å解为å°é®é¢çåæ²»è¿ç¨ä¸ï¼ä¿å对è¿äºå°é®é¢å·²ç»å¤ç好çç»æï¼å¹¶ä¾åé¢å¤çæ´å¤§è§æ¨¡çé®é¢æ¶ç´æ¥ä½¿ç¨è¿äºç»æã
éç¨äºå¨æè§åçé®é¢ï¼éè¦æ»¡è¶³æä¼åç»æåæ åææ§ä¸¤ç§æ åµã
卿è§åæéè¦ç两个è¦ç¹å¨äºæ¾å°ç¶æåç¶æè½¬ç§»æ¹ç¨ï¼ä¹å°±æ¯éæ¨å ³ç³»ã
妿ä¸è®°å½æ¯ä¸æ¥çç»æï¼å¨æè§åçæ¶é´å¤æåº¦ä¸Brute Forceæ å¼ã卿è§åå®è´¨ä¸æ¯ä¸ç§ä»¥ç©ºé´æ¢æ¶é´çæ¹æ³ï¼éè¿åå¨è¿ç¨ä¸æ¯ä¸æ¥éª¤çç¶ææ¥é使¶é´å¤æåº¦ã
举个æ å æä»¬ä»¥LeetcodeçClimbing Stairsé¢ç®ä¸ºä¾ï¼
å½åä½ç½®ä¸ºç¬¬i级å°é¶æ¶ï¼æä¸¤ç§éå¾å°è¾¾å½åçç¶æï¼
i-1级å°é¶èµ°ä¸æ¥èæ¥i-2级å°é¶èµ°ä¸¤æ¥èæ¥å æ¤ï¼å°è¾¾ç¬¬i级å°é¶çéå¾ä¸ºå°è¾¾ç¬¬i-1级å°é¶åå°è¾¾ç¬¬i-2级å°é¶çé徿°éä¹åãæä»¬å¯ä»¥å¾åºå¦ä¸çç¶æè½¬ç§»æ¹ç¨ï¼
``` dp[i] = dp[iâ1] + dp[iâ2]
```
æç §ä¸é¢çç¶æåä¸éå½ï¼å¾å°åå§ç¶æï¼
``` dp[3] = dp[1] + dp[2]
```
å°dp[1]è§ä¸ºç¬¬ä¸çº§å°é¶ï¼æä¸ç§éå¾å¯ä»¥å°è¾¾ï¼ä»0èµ°ä¸æ¥ï¼
å°dp[2]è§ä¸ºç¬¬äºçº§å°é¶ï¼æä¸¤ç§éå¾å¯ä»¥å°è¾¾ï¼ä»0èµ°ä¸¤æ¥æè
èµ°ä¸¤ä¸ªä¸æ¥ï¼
å æ¤åå§ç¶æå¦ä¸
``` dp[0] = 0 dp[1] = 1 dp[2] = 2
```
æç
§ç¶æè½¬ç§»æ¹ç¨æ±è§£dp[n]å³å¯ã
æçè§£æ³åç¬è®°å¦ä¸ï¼https://nyan.im/posts/4078.html#Climbing%20Stairs(Easy)
åèèµæ ç®æ³-卿è§å Dynamic Programming–ä»èé¸å°èé¸ – æå¾æçç¸ – CSDNå客
卿è§å · ç¬è¯é¢è¯ç¥è¯æ´ç
æè¿å¼å§å·leetcodeï¼å¨è¿éè®°å½ä¸ä¸åé¢çè¿ç¨ï¼ä¸å®ææ´æ°ã
GitHubï¼https://github.com/frankgx97/leetcode
Two Sum(Easy) https://leetcode.com/problems/two-sum/
Given an array of integers, return indices of the two numbers such that they add up to a specific target.
You may assume that each input would have exactly one solution, and you may not use the same element twice.
è§£æ³1ï¼æ´åæç´¢ é¢ç®æ¬èº«å¾ç®åï¼ä½¿ç¨ä¸¤ä¸ªfor循ç¯éåå³å¯ãéè¦æ³¨æçæ¯åä¸å ç´ ä¸è½ä½¿ç¨ä¸¤æ¬¡ï¼å æ¤å¨éåæ¶ï¼å å±å¾ªç¯éè¦ç´æ¥ä»å¤å±å¾ªç¯+1å¤å¼å§ï¼è䏿¯ä»å¤´å¼å§ã
Solutionï¼https://github.com/frankgx97/leetcode/blob/master/TwoSum.py
è§£æ³2ï¼åå¸è¡¨ æ°å»ºä¸ä¸ªåå¸è¡¨ï¼Pythonä¸ä½¿ç¨åå ¸å³å¯ï¼ï¼éåæ°ç»ï¼ä»¥æ°ç»ä¸çå¼ä¸ºåå¸è¡¨çé®ï¼ä»¥æ°ç»ä¸çé®ä¸ºåå¸è¡¨çå¼ï¼å°æ°ç»ä¸çå 容åå ¥åå¸è¡¨ã
å¨éåçåæ¶æ£æ¥(target-å½åéåå°çå¼)æ¯å¦åå¨åå¸è¡¨çé®ä¸ï¼å¦æåå¨åè¿ååå¸è¡¨ä¸çå¼åå½åéåå°çé®ãè¿æ ·å¤æåº¦ç±O(n^2)éä½è³O(n)ã
Solutionï¼https://github.com/frankgx97/leetcode/blob/master/TwoSum(Hashmap).py
Add Two Numbers(Medium) https://leetcode.com/problems/add-two-numbers/
You are given two non-empty linked lists representing two non-negative integers. The digits are stored in reverse order and each of their nodes contain a single digit. Add the two numbers and return it as a linked list.
You may assume the two numbers do not contain any leading zero, except the number 0 itself.
Example:
``` Input: (2 -> 4 -> 3) + (5 -> 6 -> 4) Output: 7 -> 0 -> 8 Explanation: 342 + 465 = 807.
```
è§£æ³ è¿ä¸ªé¢ç®å¾æææï¼è¾å ¥ä¸ºä¸¤ä¸ªä½¿ç¨é¾è¡¨å½¢å¼ç»åºçååºæ°åï¼è¦æ±å°è¿ä¸¤ä¸ªæ°åç¸å ï¼å¹¶å°ä¸¤è ä¹ååæ ·ä»¥ååºçé¾è¡¨å½¢å¼è¾åºã
ææåçè§£æ³æ¯éåé¾è¡¨ï¼å°ä¸¤ä¸ªæ°å读å为å符串ï¼å¹¶è½¬æ¢ææ´æ°æ±åï¼ç¶ååç»è£ æé¾è¡¨ï¼ä½æ¯è¿æ ·å¾æ²¡ææã
è§å¯é¢ç®å¯ä»¥åç°ï¼æä»¬å¯ä»¥ä½¿ç¨å æ³ç«å¼çæ¹æ³æ¥æ±è§£ã以é¢ç®ä¸ç»åºçexample为ä¾ï¼åæ¶éå两个é¾è¡¨ï¼
7->0->8éè¦æ³¨æå¨ä¸¤ä¸ªæ°ç»é½éå宿åï¼éè¦æ£æ¥è¿ä½æ è®°æ¯å¦ä¸ºçï¼å¦æä¸ºçåéè¦å¨å¼å¤´è¡¥ä¸ä¸ä¸ª1ã
Solutionï¼https://github.com/frankgx97/leetcode/blob/master/AddTwoNumbers.py
Valid Parentheses(Easy) https://leetcode.com/problems/valid-parentheses
Given a string containing just the characters (, ), {, }, [ and ], determine if the input string is valid.
An input string is valid if:
Note that an empty string is also considered valid.
è§£æ³ å¾ç»å ¸çä¸éé¢ãé¢ç®ç»åºä¸ä¸ªå å«åç§æ¬å·çå符串ï¼è¦æ±æ£éªè¿äºæ¬å·æ¯å¦æ°å½å°éåã
æçæ¹æ³æ¯ä½¿ç¨ä¸ä¸ªæ ï¼éåå符串ãå½éå°å·¦æ¬å·æ¶ï¼å°ç¬¦å·å ¥æ ï¼å½éå°å³æ¬å·æ¶ï¼æ£æ¥æ é¡¶å ç´ æ¯å¦ä¸ºç¸åºçå·¦æ¬å·ã妿æ¯ï¼å°æ é¡¶å ç´ åºæ ï¼å¦æä¸æ¯åç´æ¥è¿åfalseã
è¿ä¸ªé¢ç®æå 个åéè¦æ³¨æï¼
Solutionï¼ https://github.com/frankgx97/leetcode/blob/master/ValidParentheses.py
Merge Two Sorted Lists(Easy) https://leetcode.com/problems/merge-two-sorted-lists
Merge two sorted linked lists and return it as a new list. The new list should be made by splicing together the nodes of the first two lists.
Example:
``` Input: 1->2->4, 1->3->4 Output: 1->1->2->3->4->4
```
è§£æ³1ï¼è¯»åé¾è¡¨è³æ°ç»ä¹åæåº
è¿ä¸ªé¢ç®ææ¬æ¥æ³åAdd Two Numbers䏿 ·åæ¶éå两个é¾è¡¨ï¼ç¶åå°è¾å°çé£ä¸ä¸ªä¼å
å å
¥ç»æé¾è¡¨ä¸ã使¯è¿ç§æ¹æ³æ æ³å¤ç类似äº1->5->6, 1->3->4çè¾å
¥ãæä»¥æä½¿ç¨äºæ¯è¾å¼±æºçæ¹æ³ï¼å°ä¸¤ä¸ªé¾è¡¨åå«éåï¼å°å
容åå
¥ä¸ä¸ªæ°ç»ï¼æåºåç»è£
æé¾è¡¨è¾åºã
éè¦æ³¨æçæ¯OJä¼ç»åºå 个è¾å ¥ä¸ºNoneçæç«¯çç¨ä¾ï¼æ³¨æå¤çè¿ç§æ åµã
Solutionï¼https://github.com/frankgx97/leetcode/blob/master/MergeTwoSortedLists.py
è§£æ³2ï¼å¨å
¶ä¸ä¸æ¡é¾è¡¨çåºç¡ä¸æåº
ä»¥ç¬¬ä¸æ¡é¾è¡¨l1为åºç¡ãéåé¾è¡¨l2 ä¸çå
ç´ æ¶ï¼éåé¾è¡¨l1 ï¼å°l2 ä¸å½åçå
ç´ æå
¥è¿l1 ä¸çåéä½ç½®ãç±äºè¾å
¥çé¾è¡¨æ¯å·²ç»æè¿åºçï¼æä»¥ä¸éè¦æ¯æ¬¡æå
¥å让æéåå°é¾è¡¨å¤´ã
Solution: https://github.com/frankgx97/leetcode/blob/master/MergeTwoSortedLists(LinkedList).py
Container With Most Water(Medium) https://leetcode.com/problems/container-with-most-water/
Given n non-negative integers a1, a2, …, an , where each represents a point at coordinate (i, ai). n vertical lines are drawn such that the two endpoints of line i is at (i, ai) and (i, 0). Find two lines, which together with x-axis forms a container, such that the container contains the most water.
Note: You may not slant the container and n is at least 2.
è§£æ³ æç®åçæ¹å¼æ¯æ´åæç´¢ï¼å¦ä¸ä»£ç å®ç°çæ´åæç´¢æ¶é´å¤æåº¦ä¸ºO(n^2)ãå®ä» è½éè¿ä¸äºç®åçç¨ä¾ï¼å¨ä¸äºè¶ é¿ï¼é¿è¾¾ä¸å±åï¼çç¨ä¾ä¸åä¼TLEï¼æä»¥éè¦æ³åæ³ä¼åã
``` class Solution: def maxArea(self, height: List[int]) -> int: left = 0 right = 0 max=0 for left in range(0,len(height)): for right in range(left+1, len(height)): area = (right - left) * min(height[left], height[right]) if area > max: max = area return max
```
æèèè¿ä»æ°ç»ä¸å¼æå¤§çä½ç½®å¼å§ï¼ä»å¤§å°å°æç´¢ï¼ä½æ¯è¿ç§åæ³ä¼¼ä¹æ²¡æåæ³éä½å¤æåº¦ï¼è䏿¾ç¶æ æ³å¤çä¸äºæç«¯ç¨ä¾ï¼æ¯å¦ä¸ä¸ªé墿éåçåºåã
ææååèçæ¡ä¸çè§£æ³ï¼è®¾ç½®åå«ä½äºæ°ç»å·¦å³ä¸¤ç«¯ç两个æéï¼è®¡ç®ä¸¤ä¸ªæéææåçä½ç½®çé¢ç§¯ãç¶åå°ä¸¤ä¸ªæéå½ä¸æå¯¹åºçå¼è¾å°çä¸ä¸ªåæ°ç»ä¸å¤®ç§»å¨ã
Solution:https://github.com/frankgx97/leetcode/blob/master/ContainerWithMostWater.py
ï¼æªè§£å³ï¼3Sum(Medium) https://leetcode.com/problems/3sum
Given an array nums of n integers, are there elements a, b, c in nums such that a+ b + c = 0? Find all unique triplets in the array which gives the sum of zero.
Note:
The solution set must not contain duplicate triplets.
Example:
``` Given array nums = [-1, 0, 1, 2, -1, -4],
A solution set is: [ [-1, 0, 1], [-1, -1, 2] ] ```
è§£æ³1ï¼ç¸åæ°(TLE) é¢ç®è¦æ±ä¸ä¸ªæ°åä¹å为0ï¼ä¹å°±æ¯è¯´ï¼è¦ä½¿ä¸¤ä¸ªæ°ä¹åçäºç¬¬ä¸ä¸ªæ°çç¸åæ°ãè¿æ ·å°±æé®é¢è½¬å为2Sumé®é¢ã
ç±äºé¢ç®è¦æ±ç»æä¸ä¸è½å å«éå¤çä¸å ç»ãå æ¤ï¼æ¯æ¬¡å°ä¸å ç»æ·»å å°ç»ææ°ç»ä¹åå å°ä¸å ç»æåºï¼å¹¶å¤ææ¯å¦å·²ç»æç¸åçä¸å ç»å¨ç»æä¸ã
Solution: https://github.com/frankgx97/leetcode/blob/master/3Sum(tle).py
è§£æ³2: DFS (TLE) å°è¯ä½¿ç¨DFSï¼ä½æ¯è¶ æ¶ã
Solution: https://github.com/frankgx97/leetcode/blob/master/3Sum(dfs-tle).py
Letter Combination of a Phone Number(Medium) https://leetcode.com/problems/letter-combinations-of-a-phone-number/
Given a string containing digits from 2-9 inclusive, return all possible letter combinations that the number could represent.
A mapping of digit to letters (just like on the telephone buttons) is given below. Note that 1 does not map to any letters.
Example:
Input: "23"
Output: ["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"].
è§£æ³ é¦å æä»¬æé ä¸ä¸ªåå ¸ï¼å°æ°åååæ¯çæ å°åå ¥ï¼å¹¶åå§åä¸ä¸ªæ°ç»ä½ä¸ºåæ¾ç»æçéåãéåè¾å ¥çå符串ã
å¨ç¬¬ä¸è½®è¿ä»£æ¶ï¼å°ç¬¬ä¸ä¸ªæ°åæå¯¹åºçä¸ä¸ªï¼æå个ï¼åæ¯æ¨å ¥éåãå ¶åçæ¯ä¸è½®è¿ä»£ï¼é½ä»éåä¸ååºç¬¬ä¸ä¸ªå符串ï¼ä¾æ¬¡è¿½å å½åæ°åæå¯¹åºçä¸ä¸ªï¼æå个ï¼åæ¯åï¼æ¨å ¥éå°¾ãè¿ä»£å®æåå³å¯å¾å°ç»æã妿䏿³ä½¿ç¨éåçè¯ï¼å¯ä»¥å¨æ¯æ¬¡è¿ä»£ä¸å°ç»ææ°ç»å¤å¶ä¸ºä¸ä¸ªä¸´æ¶æ°ç»æ¥ä»£æ¿ã
Solution: https://github.com/frankgx97/leetcode/blob/master/LetterCombinationOfAPhoneNumber.py
PalindromeNumber(Easy) https://leetcode.com/problems/palindrome-number
Determine whether an integer is a palindrome. An integer is a palindrome when it reads the same backward as forward.
Example 1:
``` Input: 121 Output: true
```
Example 2:
**Input:** -121
**Output:** false
**Explanation:** From left to right, it reads -121. From right to left, it becomes 121-. Therefore it is not a palindrome.
è§£æ³1ï¼å符串 å¾ç»å ¸çåææ°é¢ç®ãæç®åçæ¹æ³æ¯å°åç¬¦ä¸²è½¬æ¢ææ°ç»ï¼éåæ°ç»çåæ¶å°è¿ä¸ªæ°åçååé¨åè¿æ ï¼ç¶åæ¯è¾æ é¡¶æ°ååå½åæ°åæ¯å¦ç¸åãéè¦æ³¨æåºå奿°ä½æ°æå¶æ°ä½æ°çæ åµã
ææ£å¨æèä¸å°æ°åè½¬æ¢æå符串çå®ç°æ¹æ³ã
Solution: https://github.com/frankgx97/leetcode/blob/master/PalindromeNumber.py
Remove n-th Node From End of List(Medium) https://leetcode.com/problems/remove-nth-node-from-end-of-list/
Given a linked list, remove the n-th node from the end of list and return its head.
Example:
``` Given linked list: 1->2->3->4->5, and n = 2.
After removing the second node from the end, the linked list becomes 1->2->3->5. ```
è§£æ³1ï¼ä¸¤æ¬¡éå æè¯å¾ç¨ä¸æ¬¡é忥åï¼ä½æ¯å¤±è´¥äºï¼æä»¥å å°è¯ä¸¤æ¬¡éåã
ç¬¬ä¸æ¬¡å
éåæ´ä¸ªé¾è¡¨ï¼å¾å°èç¹çæ°éï¼ç¬¬äºæ¬¡éåæ¶ï¼å°length-n-1 å¤çèç¹çnextæåcurrent.next.next å³å¯ã使¯è¿ç§æ
嵿 æ³å¤çheadèç¹å³ä¸ºè¢«ç§»é¤èç¹çæ
åµï¼æä»¥éè¦åç¬å¤çä¸ä¸ã
Solution: https://github.com/frankgx97/leetcode/blob/master/RemoveNthNodeFromEndofList.py
è§£æ³2ï¼ä¸æ¬¡éå è¿ç§æ¹æ³éè¦çºç²ä¸ç¹ç©ºé´æ¥æ¢åæ¶é´ã
é¦å
åå§åä¸ä¸ªç©ºå表lstï¼å¨éåé¾è¡¨çåæ¶å°æ¯ä¸ªèç¹åå
¥å表ã
å¨ Python è¯è¨ä¸ï¼æ 论ä»ä¹æ°æ®ç±»åï¼é½æ¯æç §å¼ç¨èµå¼ã举个æ åï¼
``` Python 3.7.1 (default, Dec 14 2018, 13:28:58) [Clang 4.0.1 (tags/RELEASE_401/final)] :: Anaconda, Inc. on darwin Type "help", "copyright", "credits" or "license" for more information.
class Node: ... val = 0 ... next = None ... node = Node() lst = [] lst.append(node) id(lst[0]) 4350381864 id(node) 4350381864 ```
æä»¬å¯ä»¥çåºç±äºå表çå ç´ ånodeæåäºåä¸å°åãä¹å°±æ¯è¯´ï¼å表ä¸åå¨çæ¯é¾è¡¨èç¹çå¼ç¨(Reference)ï¼æä½å表ä¸å ç´ å³ä¸ºæä½é¾è¡¨èç¹ã
宿忾å°lst[len(lst)-n-1] ï¼å°nextæålst[len(lst)-n-1].next.next ã
使¯è¿æ ·æ æ³å¤ç头èç¹å³ä¸ºè¢«ç§»é¤èç¹çæ åµï¼æ°ç»ä¼è¶çï¼éè¦ç¹æ®å¤çï¼
卿°ç»ç´¢å¼len(lst)-n-1 < 0çæ
åµä¸ï¼ç´æ¥å°headèµå¼ä¸ºhead.next å³å¯ã
Solution: https://github.com/frankgx97/leetcode/blob/master/RemoveNthNodeFromEndofList(OnePass).py
è§£æ³3ï¼ä½¿ç¨èæå¤´èç¹ï¼Dummy Headï¼ç䏿¬¡éå ä¸é¢ä¸¤ä¸ªè§£æ³é½åå¨ä¸ä¸ªé®é¢ï¼éè¦ç¹æ®å¤ç头èç¹çæ åµã
å¨ä½¿ç¨é¾è¡¨æ¶ï¼æä»¬æ æ³åæä½å ¶ä»èç¹é£æ ·å»æä½å¤´èç¹ãä¾å¦è¦ç§»é¤å¤´èç¹æ¶ï¼æä»¬æ æ³å°å¤´èç¹çåä¸ä¸ªèç¹çnextç´æ¥æåå ¶ä¸ä¸ä¸ªèç¹ââå 为头èç¹çåä¸ä¸ªèç¹æ ¹æ¬ä¸åå¨ï¼å æ¤å¿ 须对è¿ç§æ åµç¹æ®å¤çã
忥æäºè§£å°äºèæå¤´èç¹ï¼Dummy Headï¼çæ¹æ³ââå¨çæ£ç头èç¹ï¼headï¼åé¢è¿æ¥ä¸ä¸ªèæå¤´èç¹ï¼dummyï¼ï¼è¿æ ·ä¸æ¥å°±ä½¿headè½å¤åå ¶ä»èç¹ä¸æ ·è¢«æä½ãå ³äºDummy Headçæ´å¤è®¨è®ºè¯·åèï¼æä»¬ä»ä¹æ¶åéè¦ç» linked list å ä¸ dummy head | Yang Liu’s blog
åå°é¢ç®ä¸æ¥ãæ¬è§£æ³æ¯å¨è§£æ³2çåºç¡ä¸ä¼åèæãæå¨å¼å§éåä¹åå¨headä¹åè¿æ¥äºä¸ä¸ªdummyï¼ç¶åæ§è¡å ¶ä»çé»è¾ã彿ä½å®æåï¼è¿ådummy.nextå³å¯ã
Solution: https://github.com/frankgx97/leetcode/blob/master/RemoveNthNodeFromEndofList(OnePasswithDummyHead).py
è§£æ³4: 使ç¨ä¸¤ä¸ªæéç䏿¬¡éå æ¬è§£æ³æ¯å¨è§£æ³3çåºç¡ä¸ä¼åèæã
å®ä¹currentåop两个æéï¼åå§ä½ç½®å为dummyãcurrentå å¼å§éåé¾è¡¨ï¼opææ¶åçå¨åå°ãå½currentç§»å¨å°ç¬¬n+1个èç¹æ¶ï¼opå¼å§ç§»å¨ï¼ä¸currentä¿æn+1çè·ç¦»ã
å½currentç§»å¨å°é¾è¡¨æ«å°¾æ¶ï¼opå°å ¶å½ååççèç¹çnext设置为op.next.nextå³å¯ã
Solution: https://github.com/frankgx97/leetcode/blob/master/RemoveNthNodeFromEndofList(OnePasswithTwoPointers).py
Search Insert Position(Easy) https://leetcode.com/problems/search-insert-position/
Given a sorted array and a target value, return the index if the target is found. If not, return the index where it would be if it were inserted in order.
You may assume no duplicates in the array.
Example 1:
``` Input: [1,3,5,6], 5 Output: 2
```
Example 2:
**Input:** [1,3,5,6], 2
**Output:** 1
è§£æ³ï¼äºåæ¥æ¾ è¿ä¸ªé¢çèµ·æ¥æ¯ä¸ªç®åçäºåæ¥æ¾ï¼ä½æ¯çæ£çé¾ç¹æ¯æ¾å°å½targetä¸å卿¶æå ¥è¿æ°ç»çä½ç½®ãè§£å³æ¹æ³æ¯å½äºåæ¥æ¾ç»æåè¿å左侧æéææåçèç¹ ã
以æçè§£æ³ä¸ºä¾ï¼
class Solution:
def searchInsert(self, nums: List[int], target: int) -> int:
lo = 0
hi = len(nums) - 1
while lo <= hi:
mid = (lo + hi)//2
if target == nums[mid]:
return mid
if target >= nums[mid]:
lo = mid + 1
else:
hi = mid - 1
return lo
æä»¬ä½¿ç¨nums=[1,3,5,6], target=2 为ä¾ï¼è§å¯ä¸ä¸ä¸è¿°ä»£ç è¿è¡çè¿ç¨ã
è¿æä¸ä¸ªnums=[1,3,5,6], target=7 çç¨ä¾ã
æä»¬å¯ä»¥çåºï¼å½target卿°ç»ä¸ä¸å卿¶ï¼å¨å¾ªç¯ç»æålo åhi ä¸å®ä¼æååä¸èç¹ãè¿ä¸ªè§£éï¼C++ O(logn) Binary Search that handles duplicate – LeetCode Discuss 说æäºä¸ºä»ä¹å½å¾ªç¯ç»æålo ä¸å®æåæä»¬éè¦çä½ç½®ã
(1) At this point, low > high. That is, low >= high+1
(2) From the invariant, we know that the index is between [low, high+1], so low <= high+1. Following from (1), now we know low == high+1.(3) Following from (2), the index is between [low, high+1] = [low, low], which means that low is the desired index
Therefore, we return low as the answer. You can also return high+1 as the result, since low == high+1
Solution: https://github.com/frankgx97/leetcode/blob/master/SearchInsertPosition.py
Search In Rotated Sorted Array(Medium) https://leetcode.com/problems/search-in-rotated-sorted-array/
Suppose an array sorted in ascending order is rotated at some pivot unknown to you beforehand.
(i.e., [0,1,2,4,5,6,7] might become [4,5,6,7,0,1,2]).
You are given a target value to search. If found in the array return its index, otherwise return -1.
You may assume no duplicate exists in the array.
Your algorithm’s runtime complexity must be in the order of O(log n).
Example 1:
``` Input: nums = [4,5,6,7,0,1,2], target = 0 Output: 4
```
Example 2:
**Input:** nums = [4,5,6,7,0,1,2], target = 3
**Output:** -1
è§£æ³ ä¸¥æ ¼æ¥è¯´è¿ä¸ªæè·¯æ¯ä¸ç¬¦åé¢ç®è¦æ±çï¼å 为é¢ç®è¦æ±O(logN)çå¤æåº¦ãä¸è¿å®é ä¸OJ对æ¶é´çè¦æ±æ²¡æé£ä¹ä¸¥æ ¼ï¼çè³O(N)çè§£æ³ä¹å¯ä»¥éè¿ã
æ³è¦å®ç°O(logN)çå¤æåº¦ï¼å°±åªè½åºäºäºåæ¥æ¾æ¥ä¼åãæéç¨çæ¹æ³æ¯å ä»å·¦ä¾§å¼å§éååè¡¨ï¼æé´å¦æéå°ç®æ å ç´ åç´æ¥è¿åãå¾ éåè¿é¡¶ç¹ï¼ä¹å°±æ¯æ°ç»è¢«ç¿»è½¬çä½ç½®ï¼åï¼å¯¹ååé¨åæ§è¡äºåæ¥æ¾ãä½è¯´å®è¯ï¼å¯¹æ§è½çæåä¼¼ä¹ååæéã
Solution: https://github.com/frankgx97/leetcode/blob/master/SearchInRotatedSortedArray.py
Climbing Stairs(Easy) https://leetcode.com/problems/climbing-stairs/
You are climbing a stair case. It takes n steps to reach to the top.
Each time you can either climb 1 or 2 steps. In how many distinct ways can you climb to the top?
Note: Given n will be a positive integer.
Example 1:
``` Input: 2 Output: 2 Explanation: There are two ways to climb to the top. 1. 1 step + 1 step 2. 2 steps
```
Example 2:
**Input:** 3
**Output:** 3
**Explanation:** There are three ways to climb to the top.
1. 1 step + 1 step + 1 step
2. 1 step + 2 steps
3. 2 steps + 1 step
è§£æ³1ï¼DFS+åæº¯ï¼TLEï¼ åå 天ç»äºææç½äºDFSååæº¯ï¼ä¸çå°è¿ä¸ªé¢å°±å ´å²å²å°ç¨DFS+åæº¯å»åãäºå®è¯æ…è½ååºæ¥ï¼çæ¡ä¹æ¯å¯¹çï¼ä¸è¿ç¡®å®æ ¢å¾æç¹è¿å亅
ä¸å¾æ¯è¾å ¥ä¸º35æ¶çæ§è¡æ¶é´ï¼ç¨äº49ç§ã
Solution: https://github.com/frankgx97/leetcode/blob/master/ClimbingStairs(dfs).py
è§£æ³2ï¼å¨æè§å å ³äºå¨æè§å请åèè¿ç¯æç« ï¼å¨æè§åç®æ³ â Frank’s Weblog
æå°è¯äºä¸¤ç§å®ç°æ¹å¼ï¼å ¶ä¸éå½è½å¤å¾åºæ£ç¡®çæ¡ï¼ä½æ¯è¾å ¥35æ¶ç¨äºå¤§çº¦4ç§ï¼å¯¼è´OJè¶ æ¶ï¼ä½¿ç¨å¾ªç¯åè½å¤é¡ºå©ACã
Solutionsï¼
éå½ï¼https://github.com/frankgx97/leetcode/blob/master/ClimbingStairs(recursive).py
循ç¯ï¼https://github.com/frankgx97/leetcode/blob/master/ClimbingStairs(iterative).py
Subsets(Medium) https://leetcode.com/problems/subsets/
Given a set of distinct integers, nums, return all possible subsets (the power set).
Note: The solution set must not contain duplicate subsets.
Example:
**Input:** nums = [1,2,3]
**Output:**
[
[3],
[1],
[2],
[1,2,3],
[1,3],
[2,3],
[1,2],
[]
]
è§£æ³ï¼DFS è¿é颿¯ä¸ä¸ªå ¸åçæ·±åº¦ä¼å æç´¢ï¼ç±»ä¼¼äºä¸ä¸ªç®åççCombination Sumãä¸Combination Sumä¸åçæ¯è¿ä¸ªé¢ç®ä¸å 许éå¤å ç´ ï¼æä»¥é彿¶ä¼ å ¥çindexåæ°è¦+1ã
æå¨ç¨Pythonåè¿é颿¶å¯è½LeetcodeçOJåºäºä»ä¹bugï¼å¨ä¸ä¸ªç¨ä¾ä¸ä¼å¾å°å®å ¨ä¸å¯è½åºç°çè¾åºï¼Playgroundé忝æ£å¸¸çï¼ãæåç¨C++æç §å®å ¨ç¸åçæè·¯éæ°åäºä¸éï¼é¡ºå©éè¿ã
æ´æ°ï¼ææç½ä¸ºä»ä¹ä¼åºç°ä¸é¢çé误äºã卿§è¡ä¸åçæµè¯ç¨ä¾æ¶ï¼Solutionç±»ä¸ç屿§ï¼æååéï¼ä¸ä¼è¢«æ´æ¹æéæ¯ï¼å¯¼è´å䏿¬¡çç»æä¸å å«äºå䏿¬¡çç»æã请尽éé¿å 使ç¨Solutionç±»ç屿§æ¥å卿°æ®ï¼å¦æä¸å®è¦è¿ä¹åï¼è®°å¾å¨å ¥å£å½æ°å¤éæ°åå§åè¿ä¸ªåéã
Solutions:
C++: https://github.com/frankgx97/leetcode/blob/master/Subsets.cpp
Python: https://github.com/frankgx97/leetcode/blob/master/Subsets.py
Permutations(Medium) https://leetcode.com/problems/permutations/
Given a collection of distinct integers, return all possible permutations.
Example:
**Input:** [1,2,3]
**Output:**
[
[1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
[3,2,1]
]
è§£æ³ï¼DFS
è¿éé¢çç®æ æ¯çæä¸ä¸ªæ°ç»çå
¨æåï¼ç±»ä¼¼C++ä¸çnext_permutation ã
è¿éé¢ä¹æ¯ä¸ªå ¸åDFSï¼åSubsetsåCombination Sum类似ï¼ä½¿ç¨éç¨è§£æ³å³å¯éè¿ãä¸åçæ¯è¿ä¸ªé¢ç®ä¸å 许éå¤çå ç´ ï¼å¹¶ä¸è¦å¾å°å ¨é¨çæåï¼å°±ä¸è½åCombination Sum䏿 ·æ¯æ¬¡å°candidatesçç´¢å¼+1ã
æä½¿ç¨äºä¸ä¸ªå表ï¼è®°å½å·²ç»æç´¢è¿çç´¢å¼ï¼å½å¾ªç¯å°å·²ç»åå¨äºå表ä¸çç´¢å¼æ¶å³è·³è¿ãé¿å åºç°éå¤çå ç´ ã
Solution: https://github.com/frankgx97/leetcode/blob/master/Permutations.py
Find the Duplicate Number(Medium) https://leetcode.com/problems/find-the-duplicate-number/
Given an array nums containing n + 1 integers where each integer is between 1 and n(inclusive), prove that at least one duplicate number must exist. Assume that there is only one duplicate number, find the duplicate one.
Example 1:
``` Input: [1,3,4,2,2] Output: 2
```
Example 2:
**Input:** [3,1,3,4,2]
**Output:** 3
Note:
è§£æ³1: è¿ä¸ªé¢ç®çä¼¼å¾ç®åï¼å®å ¨ä¸åæ¯Mediumçé¾åº¦ãå®é ä¸é¢ç®æä¸å°éå æ¡ä»¶ï¼æ¯å¦æ°ç»ä¸è½æ¹å¨ï¼åªè½ä½¿ç¨constantçï¼O(1)çé¢å¤åå¨ç©ºé´ççã
æéç¨çæ¹æ³æ¯ä½¿ç¨ä¸ä¸ªæ°ç»ä½ä¸ºåå¸è¡¨ï¼å¨éåæ°ç»ç忶夿å½åå ç´ æ¯å¦å¨è¡¨ä¸ï¼å¦ææåè¿åãè¯¥æ¹æ³æ¶é´å¤æåº¦ä¸ºO(n)ï¼ä¸è¿ç±äºä¸´æ¶æ°ç»æ¯å¨æçï¼ä¸ç¬¦åç¬¬äºæ¡éå è¦æ±ã
Solutionï¼https://github.com/frankgx97/leetcode/blob/master/FindTheDuplicateNumber.py
Move Zeroes(Easy) https://leetcode.com/problems/move-zeroes/
Given an array nums, write a function to move all 0‘s to the end of it while maintaining the relative order of the non-zero elements.
Example:
**Input:** [0,1,0,3,12]
**Output:** [1,3,12,0,0]
Note:
è§£æ³1(WA): ææå¼å§å°è¯ä½¿ç¨ä¸é¢çä»£ç æ¥è§£çï¼
class Solution:
def moveZeroes(self, nums: List[int]) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
for i in range(len(nums)):
if nums[i] == 0:
nums = nums[:i] + nums[i+1:] + [0]
è¿ä»½ä»£ç è½ç¶è½å¤printåºæ£ç¡®çæ¡ï¼ä½æ¯OJ读åå°çè¾åºåæ¯åè¾å ¥å®å ¨ä¸æ ·ã
åå å¨äºPythonä¸çåéå¹¶éç´æ¥æå该åéå åä¸çå°åï¼èæ¯ç±»ä¼¼äºä¸ä¸ªæ ç¾ãå½è¯¥åéè¢«éæ°èµå¼åï¼åéææåçå åå°åå°±æ¹åäºãæç¨ä¸é¢ç代ç åäºä¸ªå®éªï¼å¨æ¯ä¸ªå¾ªç¯çif䏿å°åºnumsåéçå°åï¼å¾å°å¦ä¸çç»æï¼
å¯ä»¥çåºï¼è½ç¶æä¸ç´å¨å¯¹numsåéè¿è¡æä½ï¼ä½å®é ä¸numsææåçå°åå·²ç»è¢«æ¹åäºï¼æ æ³è¢«OJæ£ç¡®è¯»åã
è§£æ³2: è¿ä¸ªæ¹æ³æ¯è¾å¸¸è§ï¼è½ç¶ä»£ç çèµ·æ¥ä¸å¤ªä¼é ã
å¤§è´æè·¯æ¯æéåæ°ç»ï¼ä¸æ¦éå°0åå°åé¢çå ¨é¨å ç´ å¾åç§»å¨ä¸ä½ï¼ç¶åå°æ°ç»çæåä¸ä¸ªå ç´ è®¾ä¸º0ãéè¦æ³¨æçç¹æ®æ 嵿ï¼
Solution:https://github.com/frankgx97/leetcode/blob/master/MoveZeroes.py
Hamming Distance(Easy) https://leetcode.com/problems/hamming-distance/
The Hamming distance between two integers is the number of positions at which the corresponding bits are different.
Given two integers x and y, calculate the Hamming distance.
Note:
0 ⤠x, y < 231.
Example:
``` Input: x = 1, y = 4
Output: 2
Explanation: 1 (0 0 0 1) 4 (0 1 0 0) â â
The above arrows point to positions where the corresponding bits are different.
```
两个çé¿å符串ä¹é´çæ±æè·ç¦»ï¼è±è¯ï¼Hamming distanceï¼æ¯ä¸¤ä¸ªå符串对åºä½ç½®çä¸åå符ç个æ°ãæ¢å¥è¯è¯´ï¼å®å°±æ¯å°ä¸ä¸ªåç¬¦ä¸²åæ¢æå¦å¤ä¸ä¸ªå符串æéè¦æ¿æ¢çå符个æ°ã
æ±æè·ç¦» – ç»´åºç¾ç§ï¼èªç±çç¾ç§å ¨ä¹¦
è§£æ³1ï¼å符串 è¿æ¯ä¸ªç¬¨åæ³ï¼å°ä¸¤ä¸ªæ°è½¬æ¢ä¸ºäºè¿å¶ï¼å¹¶å¨è¾å°çä¸ä¸ªåé¢è¡¥0ãéå两个å符串ï¼è®¡ç®ç¸åä½ç½®ä¸ä¸¤è ä¸åçæ°éã
Solutionï¼https://github.com/frankgx97/leetcode/blob/master/HammingDistance.py
è§£æ³2ï¼å¼æè¿ç® æ ¹æ®æ±æè·ç¦»çå®ä¹ï¼å¾ææ¾å¯ä»¥ä½¿ç¨å¼æè¿ç®æ¥è§£å³ãé¦å 计ç®xåyç弿cãç¶å计ç®cçäºè¿å¶ä¸å«æ1çæ°éå³å¯ã
Solutionï¼https://github.com/frankgx97/leetcode/blob/master/HammingDistance(xor).py
Rotate Image(Medium) https://leetcode.com/problems/rotate-image/
You are given an n x n 2D matrix representing an image.
Rotate the image by 90 degrees (clockwise).
Note:
You have to rotate the image in-place, which means you have to modify the input 2D matrix directly. DO NOT allocate another 2D matrix and do the rotation.
Example 1:
``` Given input matrix = [ [1,2,3], [4,5,6], [7,8,9] ],
rotate the input matrix in-place such that it becomes: [ [7,4,1], [8,5,2], [9,6,3] ]
```
Example 2:
``` Given input matrix = [ [ 5, 1, 9,11], [ 2, 4, 8,10], [13, 3, 6, 7], [15,14,12,16] ],
rotate the input matrix in-place such that it becomes: [ [15,13, 2, 5], [14, 3, 4, 1], [12, 6, 8, 9], [16, 7,10,11] ] ```
è§£æ³ï¼
[1,2,3], [7,4,1],
[4,5,6], -> [8,5,2],
[7,8,9], [9,6,3],
è§å¯è¿ä¸¤ä¸ªç©éµï¼æä»¬å¯ä»¥å¾åºå¦ä¸çåæ åæ¢ã
OLD Matrix NEW Matrix
( 0 , 0 ) -> ( 0 , 2 )
( 0 , 1 ) -> ( 1 , 2 )
( 0 , 2 ) -> ( 2 , 2 )
( 1 , 0 ) -> ( 0 , 1 )
( 1 , 1 ) -> ( 1 , 1 )
( 1 , 2 ) -> ( 2 , 1 )
( 2 , 0 ) -> ( 0 , 0 )
( 2 , 1 ) -> ( 1 , 0 )
( 2 , 2 ) -> ( 2 , 0 )
å¾å°å ¬å¼å¦ä¸ï¼
new\_matrix[j][n-i-1] = old\_matrix[i][j]
æéæ©çæ¹æ³éè¦çºç²ä¸ç¹ç©ºé´ï¼é¦å 对è¾å ¥çmatrixæ·±æ·è´ï¼èµå¼ç»å¦ä¸ä¸ªåéï¼ç¶åæç §ä¸é¢çå ¬å¼å¯¹matrixéæ°èµå¼å³å¯ã
Solution: https://github.com/frankgx97/leetcode/blob/master/RotateImage.py
Single Number(Easy) https://leetcode.com/problems/single-number/
Given a non-empty array of integers, every element appears twice except for one. Find that single one.
Note:
Your algorithm should have a linear runtime complexity. Could you implement it without using extra memory?
Example 1:
**Input:** [2,2,1]
**Output:** 1
è§£æ³1ï¼åå¸è¡¨ æä½¿ç¨äºä¸ä¸ªåå¸è¡¨åå¨åè¡¨ä¸æ°ååºç°çé¢çãå ¶ä¸é®ä¸ºæ°åï¼å¼ä¸ºåºç°ç次æ°ãç¬¬ä¸æ¬¡éåç»æåï¼æç §é®éååå¸è¡¨ï¼æ¾åºå ¶ä¸å¼ä¸º1çé®å³å¯ã
Solution: https://github.com/frankgx97/leetcode/blob/master/SingleNumber.py
Min Stack(Easy) https://leetcode.com/problems/min-stack/
Design a stack that supports push, pop, top, and retrieving the minimum element in constant time.
Example:
``` MinStack minStack = new MinStack(); minStack.push(-2); minStack.push(0); minStack.push(-3); minStack.getMin(); --> Returns -3. minStack.pop(); minStack.top(); --> Returns 0. minStack.getMin(); --> Returns -2.
```
è§£æ³ï¼ ææåºç¡çæ°æ®ç»æã
Solution: https://github.com/frankgx97/leetcode/blob/master/MinStack.py
Majority Element https://leetcode.com/problems/majority-element/
Given an array of size n, find the majority element. The majority element is the element that appears more than â n/2 â times.
You may assume that the array is non-empty and the majority element always exist in the array.
Example 1:
**Input:** [3,2,3]
**Output:** 3
è§£æ³1ï¼åå¸è¡¨ 使ç¨ä¸ä¸ªåå ¸åå¨éåå°çæ°ååå ¶åºç°æ¬¡æ°ï¼å®æåéå该åå ¸çé®ï¼è¿åå¼å¤§äºn/2çé®ã
Solutionï¼ https://github.com/frankgx97/leetcode/blob/master/MajorityElement.py
è§£æ³2ï¼æ©å°æç¥¨ç®æ³
è¿æ¯ä¸ä¸ªæåºæ¬çæ©å°æç¥¨é®é¢ï¼æ¾åºä¸ç»æ°ååºåä¸åºç°æ¬¡æ°å¤§äºæ»æ°1/2çæ°åãæ©å°æç¥¨ç®æ³æ¯åºäºè¿ä¸ªäºå®ï¼æ¯æ¬¡ä»åºåééæ©ä¸¤ä¸ªä¸ç¸åçæ°åå é¤æï¼æç§°ä¸ºâæµæ¶âï¼ï¼æåå©ä¸ä¸ä¸ªæ°åæå 个ç¸åçæ°åï¼å°±æ¯åºç°æ¬¡æ°å¤§äºæ»æ°ä¸åçé£ä¸ªã
Solutionï¼https://github.com/frankgx97/leetcode/blob/master/MajorElement(Boyer-Moore-Voting).py
Reverse Linked List(Easy) https://leetcode.com/problems/reverse-linked-list
Reverse a singly linked list.
Example:
``` Input: 1->2->3->4->5->NULL Output: 5->4->3->2->1->NULL
```
è§£æ³1:å¾ªç¯ éåè¾å ¥çé¾è¡¨ï¼å¨ç»è¿æ¯ä¸ä¸ªèç¹æ¶ï¼å°å½åèç¹çnextæåï¼å¹¶å°å ¶nextæååä¸ä¸ªèç¹å³å¯ï¼ä»¥æ¤ç±»æ¨ã
Solution: https://github.com/frankgx97/leetcode/blob/master/ReverseLinkedList(iterative).py
Find All Numbers Disappeared in an Array(Easy) https://leetcode.com/problems/find-all-numbers-disappeared-in-an-array/
Given an array of integers where 1 ⤠a[i] ⤠n (n = size of array), some elements appear twice and others appear once.
Find all the elements of [1, n] inclusive that do not appear in this array.
Could you do it without extra space and in O(n) runtime? You may assume the returned list does not count as extra space.
Example:
``` Input: [4,3,2,7,8,2,3,1]
Output: [5,6] ```
è§£æ³1:éå(TLE) éå1å°len(nums)+1åºé´å çæ°åï¼å¦æè¢«éåå°çæ°åå¨è¾å ¥æ°ç»ä¸ä¸åå¨ï¼åå°å ¶å å ¥ç»ææ°ç»ä¸ã
Solution: https://github.com/frankgx97/leetcode/blob/master/FindAllNumbersDisappearedInAnArray(TLE).py
è§£æ³2:éå è¿ä¸ªæ¹æ³å©ç¨äºéåçç¹æ§ãé¦å å°è¾å ¥æ°ç»è½¬æ¢ä¸ºéåï¼å ¶ä¸éå¤çå ç´ å°è¢«èªå¨å»é¤ãç¶ååå§åä¸ä¸ªèå´ä¸º1å°len(num)çrangeï¼åæ ·è½¬æ¢ä¸ºéåãå°ä¸¤ä¸ªéåç¸åï¼å³å¯å¾å°å ¶ä¸ç¼ºå¤±çå ç´ ã
Solution: https://github.com/frankgx97/leetcode/blob/master/FindAllNumbersDisappearedInAnArray.py
Binary Tree Inorder Traversal(Easy) https://leetcode.com/problems/binary-tree-inorder-traversal/
Given a binary tree, return the inorder traversal of its nodes’ values.
Example:
``` Input: [1,null,2,3] 1 \ 2 / 3
Output: [1,3,2] ```
è§£æ³1:éå½ æç®åçä¸åºéåã
Solution: https://github.com/frankgx97/leetcode/blob/master/BinaryTreeInorderTraversal.py
Intersection of Two Linked Lists(Easy) https://leetcode.com/problems/intersection-of-two-linked-lists/
Write a program to find the node at which the intersection of two singly linked lists begins.
For example, the following two linked lists:
begin to intersect at node c1.
è§£æ³ï¼ è¿éé¢ç®ææå¼å§è¯çå éåç¬¬ä¸æ¡é¾è¡¨ï¼å¹¶ä½¿ç¨ä¸ä¸ªæ°ç»å卿¯ä¸ä¸ªèç¹çå¼ç¨ãç¶åå¨éåç¬¬äºæ¡é¾è¡¨æ¶ä¾æ¬¡å¯¹æ¯æ¯å¦ç¸åã使¯è¿ä¸ªæ¹æ³è¿äºæ´åï¼OJä¼è¶ æ¶ã
å 为两个é¾è¡¨é¿åº¦å¯è½ä¸åï¼å¯ä»¥å åå«éå两个é¾è¡¨ï¼å¾å°ä¸¤ä¸ªé¾è¡¨çé¿åº¦ãå°é¿çä¸ä¸ªæªçè³ä¸ççé¾è¡¨ä¸æ ·é¿ï¼ç¶åä»ä¸¤ä¸ªé¾è¡¨å¤´åæ¶å¼å§éåï¼è¿å两个æéç¸éçä½ç½®å³å¯ã
Solution: https://github.com/frankgx97/leetcode/blob/master/IntersectionofTwoLinkedLists.py
Path Sum III(Easy) https://leetcode.com/problems/path-sum-iii/
You are given a binary tree in which each node contains an integer value.
Find the number of paths that sum to a given value.
The path does not need to start or end at the root or a leaf, but it must go downwards (traveling only from parent nodes to child nodes).
The tree has no more than 1,000 nodes and the values are in the range -1,000,000 to 1,000,000.
Example:
``` root = [10,5,-3,3,2,null,11,3,-2,null,1], sum = 8
10
/ \
**5** **-3**
/ ** * 3 2 11 / \ * 3 -2 1
Return 3. The paths that sum to 8 are:
è§£æ³1:DFS 使ç¨éå½å¯¹è¿æ£µäºåæ è¿è¡æ·±åº¦ä¼å éåï¼æ¾åºè½å¤ååºç®æ æ°åçèç¹ãç±äºèç¹ä¹åå¹¶ä¸ä¸å®ä»æ çæ ¹é¨å¼å§è®¡ç®ï¼æä»¥éè¦ä»æ çæ¯ä¸ä¸ªèç¹é½å¼å§ä¸æ¬¡éå½ã
Solution: https://github.com/frankgx97/leetcode/blob/master/PathSumIII.py
https://github.com/frankgx97/leetcode/blob/master/PathSumIII(alt).py
Daily Temperature(Easy) https://leetcode.com/problems/daily-temperatures/
Given a list of daily temperatures T, return a list such that, for each day in the input, tells you how many days you would have to wait until a warmer temperature. If there is no future day for which this is possible, put 0 instead.
For example, given the list of temperatures T = [73, 74, 75, 71, 69, 72, 76, 73], your output should be [1, 1, 4, 2, 1, 1, 0, 0].
è§£æ³1:Brute Force(TLE) æå°è¯äºéå½å循ç¯ä¸¤ç§åæ³ï¼é½å¨ä¸ä¸ªè¶ é¿ç¨ä¾ä¸è¶ æ¶äºãæ´åè§£æ³çå¤æåº¦æ¯O(n^2)ï¼æ¾ç¶æ¯ä¸è¡çã
Solution: https://github.com/frankgx97/leetcode/blob/master/DailyTemperture(recursive,%20tle).py
è§£æ³2:åè°æ è¿ä¸ªæ¹æ³å¨éåè¾å ¥å表çåæ¶ç»´æ¤ä¸ä¸ªåè°éåçæ ã妿å½åå ç´ å°äºæ é¡¶å ç´ ï¼åå°å½åå ç´ å ¥æ ï¼å¦æå½åå ç´ å¤§äºæ é¡¶å ç´ ï¼åå°æ 䏿æå°äºå½åå ç´ çå ç´ åºæ ãæ¯ä¸ªæ å å ç´ ä¸æ¯å ¶å¤§çå ç´ çè·ç¦»å³ä¸ºæ å å ç´ ä¸å½åå ç´ çç´¢å¼ä¹å·®ãæ³è¦æ´è¯¦ç»å°äºè§£åè°æ åå ¶åºç¨å¯ä»¥åèè¿ç¯æç« ï¼https://zhuanlan.zhihu.com/p/26465701ã
Solution: https://github.com/frankgx97/leetcode/blob/master/DailyTemperture.py
Best Time to Buy and Sell Stock(Easy) https://leetcode.com/problems/best-time-to-buy-and-sell-stock/
Say you have an array for which the ith element is the price of a given stock on day i.
If you were only permitted to complete at most one transaction (i.e., buy one and sell one share of the stock), design an algorithm to find the maximum profit.
Note that you cannot sell a stock before you buy one.
Example 1:
**Input:** [7,1,5,3,6,4]
**Output:** 5
**Explanation:** Buy on day 2 (price = 1) and sell on day 5 (price = 6), profit = 6-1 = 5.
Not 7-1 = 6, as selling price needs to be larger than buying price.
è§£æ³1:Brute Force Solution: https://github.com/frankgx97/leetcode/blob/master/BestTimetoBuyandSellStock(BruteForce).py
è§£æ³2: Kadaneç®æ³ Solution: https://github.com/frankgx97/leetcode/blob/master/BestTimetoBuyandSellStock(kadane).py
è§£æ³3:åè°æ Solution: https://github.com/frankgx97/leetcode/blob/master/BestTimetoBuyandSellStock(monostack).py
Maximum Subarray(Easy) https://leetcode.com/problems/maximum-subarray/
Given an integer array nums, find the contiguous subarray (containing at least one number) which has the largest sum and return its sum.
Example:
**Input:** [-2,1,-3,4,-1,2,1,-5,4],
**Output:** 6
**Explanation:** [4,-1,2,1] has the largest sum = 6.
è§£æ³1:åè°æ
é¦å
æä»¬éåæ´ä¸ªæ°ç»ï¼å°array[i]èµå¼ä¸ºarray[0:i]䏿æå
ç´ ä¹åãä¹å°±æ¯è¯´ï¼æ¯ä¸ªèç¹çå¼ä¸ºä»æ°ç»å¤´å°å½åèç¹çæææ°åä¹åã
è¿æ ·ï¼ä¸æ iåjä¹é´çååºåä¹åå°±çäºarray[j] - array[i]ãå¯»æ¾æå¤§ååºåä¹åçé®é¢å°±è¢«è½¬æ¢ä¸ºéåæ°ç»ä¸ææå
ç´ ï¼å¹¶å¨å
¶å·¦ä¾§æ¾å°ä¸ä¸ªæå°çæ°ï¼ä½¿ä¸¤è
ä¹å·®æå¤§çé®é¢ã
å¯»æ¾æå°çæ°å½ç¶å¯ä»¥ä½¿ç¨min()æ¥è§£å³ï¼ç¶èè¿ç§æ¹æ³è¿äºæ´åï¼æ æ³æ»¡è¶³é¢ç®çæ¶é´è¦æ±ã为äºå®ç°æ´ä½çå¤æåº¦ï¼æä»¬ä½¿ç¨ä¸ä¸ªéå¢çåè°æ æ¥å®ç°ã
åè°é墿 çæ ¸å¿ä¸ºå¨æ¯æ¬¡è¿ä»£ä¸å¤æå½åå ç´ æ¯å¦å¤§äºæ é¡¶å ç´ ã
妿æ¯ï¼åå°å½åå ç´ è¿æ ãæ¤æ¶ï¼å½åå ç´ æ¯ç®åä¸ºæ¢æå¤§çå ç´ ï¼æ åºå ç´ æ¯ç®åä¸ºæ¢æå°çå ç´ ã两è ä¹å·®å³æ¯ç®åä¸ºæ¢æå¤§çååºåä¹åã
妿å¦ï¼åå°æ 䏿æå¤§äºå½åå ç´ çå ç´ åºæ ï¼åå°å½åå ç´ è¿æ ã
以示ä¾ç¨ä¾ä¸ºä¾ï¼åè°æ çååè¿ç¨è¯·åèï¼https://gist.github.com/frankgx97/63de78420e2aae98a82a27f53b8108cb
Solution: https://github.com/frankgx97/leetcode/blob/master/MaximumSubarray(monostack).py
è§£æ³2: Kadaneç®æ³ Kadane ç®æ³æ¯å¨å¨æè§åçåºç¡ä¸çè¿ä¸æ¥ä¼åãå¨å¨æè§åç®æ³ â Frankâs Weblogæä¸ï¼æä»¬æå°å¨æè§åçæ ¸å¿å¨äºæ¾åºç¶æåç¶æä¹é´çè½¬ç§»å ³ç³»ã
å¨Kadaneç®æ³ä¸ï¼æ¯æ«æå°æ°ç»ä¸çä¸ä¸ªæ°å ç´ å¯è§ä¸ºä¸ç§ç¶æï¼ç¶æä¹é´ç转移æä¸¤ç§æ åµï¼
max = sum(array[0:i-1]) + array[I]max = array[i]两è ä¹ä¸è¾å¤§çä¸ä¸ªä¸ºå±é¨çæä¼è§£ï¼å¨ææçå±é¨æä¼è§£ä¸æå¤§çä¸ä¸ªå³ä¸ºå ¨å±æä¼è§£ã
Solution: https://github.com/frankgx97/leetcode/blob/master/MaximumSubarray(kadane).py
åèèµæï¼å¾ªç¯å表ä¸çæå¤§ååºååé®é¢ | å§ç»
WordBreak(Medium) https://leetcode.com/problems/word-break/
Given a non-empty string s and a dictionary wordDict containing a list of non-empty words, determine if s can be segmented into a space-separated sequence of one or more dictionary words.
Note:
Example 1:
**Input:** s = "leetcode", wordDict = ["leet", "code"]
**Output:** true
**Explanation:** Return true because "leetcode" can be segmented as "leet code".
è§£æ³1: éå(WA) æå¨éåå符串çåæ¶ä½¿ç¨ä¸ä¸ªé忥æååç¬¦ï¼æ¯è½®è¿ä»£ä¸å°å符串ä¸ç第ä¸ä¸ªåç¬¦å ¥éï¼å¹¶å¤æéå䏿æèç¹æç»æçå符串æ¯å¦åå¨äºwordDictä¸ã
使¯è¿ç§æ¹æ³çé®é¢å¨äºä¸è½å¤çâaaaaaaaâ, [âaaaaâ,âaaaâ]è¿æ ·çç¨ä¾ãæ¯å½æ ä¸åå¤ä¸ä¸ªaæ¶ï¼å°±ä¼å¹é
å°dictä¸çaaaèåºéãçéåç»æåå°±ä¼å©ä¸ä¸ä¸ªaå¨éåä¸ï¼å¯¼è´çæ¡é误ã
è§£æ³2: DFS(TLE) 使ç¨DFSæç´¢å符串ä¸è½å¤å¹é çå符串ãç¶èè¿æ®µä»£ç å¨çæ¡ä¸ºFalseçç¨ä¾ä¸åå ¶ç¼æ ¢ï¼è¿ç¤ºä¾ç¨ä¾é½ä¼è¶ æ¶ã
Solution: https://github.com/frankgx97/leetcode/blob/master/WordBreak(dfs-tle).py
è§£æ³3: DP
æä»¬å°æ¯æ¬¡è¿ä»£è§ä¸ºä¸ä¸ªç¶æï¼ä½¿ç¨ä¸ä¸ªå¤§å°ä¸ºs+1çå表dpå卿¯ä¸ªç¶æçç»æï¼sä¸dpç对åºå
³ç³»å¦ä¸ï¼
```
s: l e e t c o d e index: 0 1 2 3 4 5 6 7 8 dp: 1 0 0 0 1 0 0 0 1 ```
å ¶ä¸ï¼dpçåå§ç¶æå ¨é¨ä¸ºFalse; dp[0]为True; dp[i]çå¼éè¦ä»¥ä¸å 个æ¡ä»¶æ¥å¤æï¼
Solution: https://github.com/frankgx97/leetcode/blob/master/WordBreak(dp).py
åèèµæï¼Simple DP solution in Python with description – LeetCode Discuss
House Robber(Easy) https://leetcode.com/problems/house-robber/
You are a professional robber planning to rob houses along a street. Each house has a certain amount of money stashed, the only constraint stopping you from robbing each of them is that adjacent houses have security system connected and it will automatically contact the police if two adjacent houses were broken into on the same night.
Given a list of non-negative integers representing the amount of money of each house, determine the maximum amount of money you can rob tonight without alerting the police.
Example 1:
**Input:** [1,2,3,1]
**Output:** 4
**Explanation:** Rob house 1 (money = 1) and then rob house 3 (money = 3).
Total amount you can rob = 1 + 3 = 4.
è§£æ³ï¼DP æ¯å°ä¸ä¸ªæ¿åé¢åï¼æ¢å«è æä¸¤ä¸ªé项
iiå½å«åªéæ©é项1æ¶ï¼æå³çä»ä¸è½æ¢å«i-1æ¿åï¼ä½æ¯å¯ä»¥æ¢å«i-2æ¿åï¼å½éæ©é项2æ¶ï¼æå³çä»å¯ä»¥æ¢å«å
æ¬i-1å¨å
çæææ¿åã
å æ¤é®é¢è½¬å为æ¯è¾ä¸é¢ä¸¤è åªä¸ªæ¶çæ´å¤§ï¼
å¾å°ç¶æè½¬ç§»æ¹ç¨å¦ä¸ï¼
r(i) = max(r(i-2)+nums[i], r(i-1))
å¦æä½¿ç¨éå½ï¼é彿¹æ³æ¯èªé¡¶åä¸çï¼ä¹å°±æ¯ä»r(len(nums)-1)å¼å§åä¸éå½ï¼ç´å°r(0)å¤ãå¾ªç¯æ¹æ³ä¸éå½ç¸åï¼å¾ªç¯ä»æ°ç»å·¦ä¾§å¼å§ï¼ç´å°éåå®è¾å ¥æ°ç»åç»æã
Solutions:
循ç¯ï¼https://github.com/frankgx97/leetcode/blob/master/HouseRobber(iterative).py
éå½ï¼https://github.com/frankgx97/leetcode/blob/master/HouseRobber(recursive-tle).py
Number of Islands(Medium) https://leetcode.com/problems/number-of-islands/
Given a 2d grid map of '1's (land) and '0's (water), count the number of islands. An island is surrounded by water and is formed by connecting adjacent lands horizontally or vertically. You may assume all four edges of the grid are all surrounded by water.
Example 1:
``` Input: 11110 11010 11000 00000
Output: 1 ```
è§£æ³1: Flood Fill 顾åæä¹ï¼Flood Fillå°±æ¯æ«æç©éµä¸çæ¯ä¸ä¸ªèç¹ï¼å¦æè¯¥èç¹æ¯éå°ï¼å计æ°åå°å½åèç¹æ·¹æ²¡ï¼å¹¶åå¨å´å个æ¹åé彿§è¡ã
Flood Fillå¯ä»¥åDFSæBFSæ¥å®ç°ï¼è¿é使ç¨çæ¯DFSã
Solution: https://github.com/frankgx97/leetcode/blob/master/NumberofIslands(floodfill).py
åèèµæ: Flood fill – Wikipedia
Maximal Square(Medium) https://leetcode.com/problems/maximal-square
Given a 2D binary matrix filled with 0’s and 1’s, find the largest square containing only 1’s and return its area.
Example:
``` Input: 1 0 1 0 0 1 0 1 1 1 1 1 1 1 1 1 0 0 1 0
Output: 4 ```
è§£æ³1: äºç»´DP é¦å åå§åä¸ä¸ªåmatrixç¸å大å°çç©éµdpï¼å°matrixä¸ä¸º”1″å¤èµå¼ä¸º1, matrixä¸ä¸º”0″å¤èµå¼ä¸º0ãç¨äºåå¨DPè¿ç¨çæ¯ä¸ä¸ªç¶æã
卿¨ªçºµåæ ç1,len(matrix)èå´å
éåmatrixï¼å½matrix[i][j]为”1″æ¶ï¼ä»dp[i-1][j] , dp[i][j-1] , dp[i-1][j-1] ä¸åæå°çä¸ä¸ªï¼+1åèµå¼ç»dp[i][j] ã
ç±æ¤å¾å°ç¶æè½¬ç§»æ¹ç¨
dp[i][j] = min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1]) + 1
Solution: https://github.com/frankgx97/leetcode/blob/master/MaximalSquare(dp).py
Top K Frequent Elements(Medium) https://leetcode.com/problems/top-k-frequent-elements/
Given a non-empty array of integers, return the k most frequent elements.
Example 1:
``` Input: nums = [1,1,1,2,2,3], k = 2 Output: [1,2]
```
Example 2:
**Input:** nums = [1], k = 1
**Output:** [1]
Note:
è§£æ³1ï¼æ¡¶æåº é¦å éè¦éåæ´ä¸ªæ°ç»ï¼å¾å°ä¸ä¸ªä»¥æ°å为é®ï¼ä»¥è¯¥æ°ååºç°ç次æ°ä¸ºå¼çåå¸è¡¨ã
æä»¬è¦å¾å°åºç°é¢çæé«çå 个å ç´ ï¼å°±éè¦å¯¹åå¸è¡¨çå¼è¿è¡æåºãè¿éæä»¬ä½¿ç¨ç®åççæ¡¶æåºï¼æ¡¶çæ°éçäºæ°ç»ç大å°ã
便¬¡éååå¸è¡¨çé®ï¼å°é®iåå¨å°ç¬¬map[i]个桶ä¸ãä¹å°±æ¯è¯´ï¼è¿ä¸ªæ¡¶ä»¥æ°ååºç°çé¢ç为索å¼ï¼æ¡¶ä¸åå¨é¢çæå¯¹åºçæ°åãç±äºé¢ç®è¦çæ¯åºç°é¢çæé«çk个æ°åï¼æä»¥ä»ç´¢å¼æå¤§çæ¡¶å¤åçéåï¼å³å¯å¾å°åºç°é¢çæå¤§çæ°åã
Solution: https://github.com/frankgx97/leetcode/blob/master/TopKFrequentElements.py
Queue Reconstruction by Height(Medium) https://leetcode.com/problems/queue-reconstruction-by-height/
Suppose you have a random list of people standing in a queue. Each person is described by a pair of integers (h, k), where h is the height of the person and k is the number of people in front of this person who have a height greater than or equal to h. Write an algorithm to reconstruct the queue.
Note:
The number of people is less than 1,100.
Example
``` Input: [[7,0], [4,4], [7,1], [5,0], [6,1], [5,2]]
Output: [[5,0], [7,0], [5,2], [6,1], [4,4], [7,1]]
```
è§£æ³1ï¼WAï¼ æç §é¢ç®çé»è¾ï¼æä»¬å¯ä»¥å¾å°å¦ä¸ä»£ç ï¼è¿æ®µä»£ç éåæ°ç»ä¸æ¯ä¸ä¸ªå ç´ ï¼æç §âå ¶åæ¹æå¤å°ä¸ªå¤§äºçäºèªèº«çå ç´ âçååå°å½åå ç´ æ¾è¿éå½çä½ç½®ä¸ã使¯è¿ç§æ¹æ³ä¼å¸¦æ¥ä¸ä¸ªé®é¢ï¼å¨å½åç¶æä¸ï¼æå ç´ çä½ç½®æ¯ç¬¦åè¦æ±çï¼ç¶èå½å ¶ä»çå ç´ ç§»å¨ä¹åï¼å ¶ä½ç½®å°±ä¸ç¬¦åé¢ç®è¦æ±äºã
def reconstruct(people):
for i in range(len(people)):
p = people.pop(i)
count = 0
flag = 0
for j in range(len(people)):
if count == p[1]:
people.insert(j, p)
flag = 1
break
elif people[j][0] >= p[0]:
count += 1
if not flag:
people.append(p)
return people
è§£æ³2 æåæ¥å¨è®¨è®ºåºçå°äºè¿ä¸ªè§£æ³ãé¦å 对æ°ç»è¿è¡æåºï¼ä»¥äºå ç»ç第ä¸ä¸ªæ°å为åéåºæåºï¼å¨æ¤åºç¡ä¸ï¼ä»¥äºå ç»ç第äºä¸ªæ°å为åååºæåºã
以é¢ç®ç示ä¾ç¨ä¾ä¸ºä¾
[[7,0], [4,4], [7,1], [5,0], [6,1], [5,2]]
æåºåå¾å°ä¸é¢çæ°ç»ï¼
[[7, 0], [7, 1], [6, 1], [5, 0], [5, 2], [4, 4]]
ç¶å使ç¨insertæ¹æ³ï¼ä»¥äºå ç»ä¸ç第äºä¸ªæ°å为索å¼ï¼å°æ¯ä¸ªäºå ç»æå ¥å°ç»ææ°ç»çç¸åºä½ç½®å³å¯ã
Solution: https://github.com/frankgx97/leetcode/blob/master/QueueReconstructionbyHeight.py
Search a 2D Matrix(Medium) https://leetcode.com/problems/search-a-2d-matrix/
Write an efficient algorithm that searches for a value in an m x n matrix. This matrix has the following properties:
Example 1:
**Input:**
matrix = [
[1, 3, 5, 7],
[10, 11, 16, 20],
[23, 30, 34, 50]
]
target = 3
**Output:** true
è§£æ³: äºåæ¥æ¾ ç±äºæ°ç»å¨æ¨ªçºµä¸¤ä¸ªç»´åº¦ä¸é½æ¯å·²ç»æåºå¥½äºï¼æä»¥å¯ä»¥å 纵å使ç¨ä¸æ¬¡äºåæ¥æ¾ï¼å®ä½å°ç®æ 弿坹åºçè¡ï¼å横å使ç¨ä¸æ¬¡äºåæ¥æ¾ã
卿£å¸¸æ åµä¸ï¼æä»¬ä½¿ç¨å¦ä¸ç代ç è¿è¡äºåæ¥æ¾ï¼
def bin_search(data_list, val):
low = 0 # æå°æ°ä¸æ
high = len(data_list) - 1 # æå¤§æ°ä¸æ
while low <= high:
mid = (low + high) // 2 # ä¸é´æ°ä¸æ
if data_list[mid] == val: # 妿ä¸é´æ°ä¸æ çäºval, è¿å
return mid
elif data_list[mid] > val: # 妿valå¨ä¸é´æ°å·¦è¾¹, ç§»å¨high䏿
high = mid - 1
else: # 妿valå¨ä¸é´æ°å³è¾¹, ç§»å¨low䏿
low = mid + 1
return # valä¸åå¨, è¿åNone
卿¨ªå使ç¨äºåæ¥æ¾æ¶ï¼å¦ææ¥æ¾ç®æ ä¸åå¨ï¼ç´æ¥è¿åFalseå³å¯ã使¯å¨çºµåæ¥æ¾æ¶ï¼æä»¬éè¦æ¾å°æ¥æ¾ç®æ æå¨çè¡ã
ä¹å°±æ¯è¯´ï¼å½çºµåæ¥æ¾ä¸å°ç®æ å ç´ æ¶ï¼åè¿åç®æ é¢è®¡æå¨ä½ç½®å·¦ä¾§çå ç´ ã卿²¡ææ¾å°ç®æ å ç´ çæ åµä¸rightæéæåçæ¯è¾å°çå ç´ ãè¿årightæå¯¹åºçæ°å¼å³å¯ã
Solution: https://github.com/frankgx97/leetcode/blob/master/Searcha2DMatrix.py
Remove Element(Easy) https://leetcode.com/problems/remove-element
Given an array nums and a value val, remove all instances of that value in-place and return the new length.
Do not allocate extra space for another array, you must do this by modifying the input array in-place with O(1) extra memory.
The order of elements can be changed. It doesn’t matter what you leave beyond the new length.
Example 1:
``` Given nums = [3,2,2,3], val = 3,
Your function should return length = 2, with the first two elements of nums being 2.
It doesn't matter what you leave beyond the returned length. ```
è§£æ³ï¼ è¿ä¸ªé¢ç®è¦æ±in-placeï¼æä»¥éè¦å¨è¾å ¥å表çå¼ç¨ä¸è¿è¡æä½ãé¾ç¹å¨äºå¨å¾ªç¯ä¸ç§»é¤å表å ç´ ï¼å 为Pythonçforå¾ªç¯æ¬è´¨ä¸æ¯ä¸ä¸ªç±»ä¼¼äºforeachçè¿ä»£å¨ï¼å¦æå¨å¾ªç¯è¿ç¨ä¸ç§»é¤äºå ç´ ï¼åä¼é ææ°ç»è®¿é®è¶çã
æç §ä¸é¢é¾æ¥ææä¾çæ¹æ¡ï¼æä»¬å¨è¿ä»£æ¶ä½¿ç¨æ°ç»åçæ¥å®ç°ã
python – How to remove items from a list while iterating? – Stack Overflow
for i in nums[:]:
do_something()
Solution: https://github.com/frankgx97/leetcode/blob/master/RemoveElement.py
Combinations(Medium) https://leetcode.com/problems/combinations/
è§£æ³ï¼DFS é¢ç®è¦æ±å¾åºä»æ°å1-nä¸éåºk个æ°åçå ¨é¨ç»åãè¿éé¢ç®åCombination Sumæä¸äºç¸ä¼¼ï¼é½å¯ä»¥ç¨DFSæ¥è§£çã
Solution: https://github.com/frankgx97/leetcode/blob/master/Combinations.py
Counting Bits(Medium) https://leetcode.com/problems/counting-bits/
è§£æ³ï¼ç餿³è½¬æ¢äºè¿å¶
è¿ä¸ªé¢ç®åªè¦æè¾å
¥çæ°åç´æ¥è½¬æ¢æäºè¿å¶ï¼å¹¶è®¡ç®å
¶ä¸1çæ°éå³å¯ãå¨Pythonä¸åªéè¦ä½¿ç¨bin()彿°å³å¯å°åè¿å¶æ´æ°è½¬æ¢æäºè¿å¶ï¼ä½æ¯è¿æ ·é¢ç®å°±æ²¡ä»ä¹æä¹äºï¼å æ¤ææåäºä¸ä¸ªè½¬æ¢äºè¿å¶ç彿°ã
åè¿å¶è½¬äºè¿å¶çç®æ³è¯·åèå¦ä½ä»åè¿å¶è½¬æ¢ä¸ºäºè¿å¶ã
Solution: https://github.com/frankgx97/leetcode/blob/master/CountingBits.py
Sqrt(x)(Medium) https://leetcode.com/problems/sqrtx
Implement int sqrt(int x).
Compute and return the square root of x, where x is guaranteed to be a non-negative integer.
Since the return type is an integer, the decimal digits are truncated and only the integer part of the result is returned.
Example 1:
``` Input: 4 Output: 2
```
Example 2:
**Input:** 8
**Output:** 2
**Explanation:** The square root of 8 is 2.82842..., and since
the decimal part is truncated, 2 is returned.
è§£æ³ï¼äºåæ¥æ¾ è¿éé¢ç®å¯ä»¥è¢«è§ä¸ºä»å·²æåºç1-xçåºå䏿¾åºå¹³æ¹ä¸ºxçé£ä¸ä¸ªæ°ã便¬¡éåçéåº¦ä¼æ¯è¾æ ¢ï¼å æ¤ä½¿ç¨äºåæ¥æ¾ã妿æ¾å°äºï¼åè¿åå½åçmidï¼å¦ææ²¡æï¼åè¿årightã
å¦å¤ï¼é常æ åµä¸ï¼æç´¢çèå´æ¯1-xãæä»¬å¯ä»¥ææç´¢çåå§èå´è®¾ä¸º1-x//2æ¥èçä¸äºæ¶é´ãè¿æ ·ä¼é æ1è¿ä¸ªä¾å¤æ åµï¼æä»¥åç¬å¯¹è¾å ¥ä¸º1çç¨ä¾è¿å1å³å¯ã
Solution: https://github.com/frankgx97/leetcode/blob/master/Sqrt(x).py
SimplifyPath(Medium) https://leetcode.com/problems/simplify-path/
Given an absolute path for a file (Unix-style), simplify it. Or in other words, convert it to the canonical path.
In a UNIX-style file system, a period . refers to the current directory. Furthermore, a double period .. moves the directory up a level. For more information, see: Absolute path vs relative path in Linux/Unix
Note that the returned canonical path must always begin with a slash /, and there must be only a single slash / between two directory names. The last directory name (if it exists) must not end with a trailing /. Also, the canonical path must be the shortest string representing the absolute path.
Example 1:
``` Input: "/home/" Output: "/home" Explanation: Note that there is no trailing slash after the last directory name.
```
Example 2:
``` Input: "/../" Output: "/" Explanation: Going one level up from the root directory is a no-op, as the root level is the highest level you can go.
```
Example 3:
``` Input: "/home//foo/" Output: "/home/foo" Explanation: In the canonical path, multiple consecutive slashes are replaced by a single one.
```
Example 4:
``` Input: "/a/./b/../../c/" Output: "/c"
```
Example 5:
``` Input: "/a/../../b/../c//.//" Output: "/c"
```
Example 6:
**Input: "**/a//b////c/d//././/.."
**Output: "**/a/b/c"
è§£æ³ï¼
è¿ä¸ªé¢ç®ç»åºäºä¸ä¸ªå¯è½å
嫿.ï¼..ï¼ä»¥åéå¤çææ ç䏿 åUNIXè·¯å¾ï¼è¦æ±å°è¾å
¥çè·¯å¾è½¬æ¢ä¸ºæ åçUNIXè·¯å¾ã
é¦å
å°è¾å
¥å符串使ç¨split()彿°æç
§åææ åå²ï¼åå²å®æåææçææ å³è¢«å»é¤ãåå§åä¸ä¸ªå表ï¼åæ¶éååå²åçææå
ç´ ï¼å¦æä¸ºç©ºææ¯. å忽ç¥ï¼å¦ææ¯.. åç§»é¤æ°ç»ä¸çæåä¸ä¸ªå
ç´ ï¼å¦ææ¯å
¶ä»å
容åå°å
ç´ æ·»å å°å表尾é¨ã
éå宿åï¼å¦ææ 为空ï¼åç´æ¥è¿å/ ï¼å¦åå°åè¡¨ä¸æ¯ä¸ªå
ç´ å ä¸/ å¹¶è¿åã
Solution: https://github.com/frankgx97/leetcode/blob/master/SimplifyPath.py
Unique Paths(Medium) https://leetcode.com/problems/unique-paths/
A robot is located at the top-left corner of a m x n grid (marked ‘Start’ in the diagram below).
The robot can only move either down or right at any point in time. The robot is trying to reach the bottom-right corner of the grid (marked ‘Finish’ in the diagram below).
How many possible unique paths are there?
Above is a 7 x 3 grid. How many possible unique paths are there?
Note: m and n will be at most 100.
Example 1:
``` Input: m = 3, n = 2 Output: 3 Explanation: From the top-left corner, there are a total of 3 ways to reach the bottom-right corner: 1. Right -> Right -> Down 2. Right -> Down -> Right 3. Down -> Right -> Right
```
Example 2:
**Input:** m = 7, n = 3
**Output:** 28
è§£æ³ï¼äºç»´DP è¿æ¯ä¸ä¸ªå ¸åçäºç»´å¨æè§åã卿è§åçæ ¸å¿å¨äºæ¾åºå ¶ç¶æåç¶æè½¬ç§»æ¹ç¨ã
é¦å åå§åä¸ä¸ªm x nçç©éµï¼ç©éµä¸çæ¯ä¸æ ¼ç¨äºåå¨ä»å¼å§ä½ç½®å°å½åä½ç½®çéå¾çæ°éãç±äºé¢ç®è§å®æºå¨äººåªè½å峿åä¸èµ°ï¼æä»¥ç¬¬ä¸æå第ä¸å䏿¯ä¸æ ¼çå¼å为1ã
æ¥çæä»¬åç°ï¼æ¯ä¸æ ¼çå¼ä¸ºå ¶å·¦è¾¹ä¸æ ¼çå¼å ä¸ä¸è¾¹ä¸æ ¼çå¼ãå æ¤å¾å°ç¶æè½¬ç§»æ¹ç¨ï¼
dp[i][j] = dp[i-1][j] + dp [i][j-1]
è¿æ ·æä»¬å¯ä»¥ç»§ç»å¡«åºæ´ä¸ªç©éµï¼å®æåè¿åå³ä¸è§çå¼å³å¯ã
Solution: https://github.com/frankgx97/leetcode/blob/master/UniquePaths.py
Partition List(Medium) https://leetcode.com/problems/partition-list/
Given a linked list and a value x, partition it such that all nodes less than x come before nodes greater than or equal to x.
You should preserve the original relative order of the nodes in each of the two partitions.
Example:
**Input:** head = 1->4->3->2->5->2, *x* = 3
**Output:** 1->2->2->4->3->5
è§£æ³ï¼ é¢ç®è¦æ±å°é¾è¡¨åæå°äºå大äºçäºxç两个é¨åï¼å¹¶ä¸ä¸æ¹å忬ç顺åºãè¿æ¬èº«æ¯ä¸ä¸ªæ®éçé¾è¡¨æä½ï¼ä½æ¯å½è¢«ä¿®æ¹çæ¯é¾è¡¨å¤´èç¹æ¶ï¼ä¼å¸¦æ¥ä¸äºéº»ç¦ãå æ¤å¨å¼å§ä¹åå ç»åé¾è¡¨ç头é¨è¿æ¥ä¸ä¸ªèæå¤´èç¹(dummy head)ï¼ç¶ååæç §åæ¬çæè·¯è¿è¡æä½ã
Solution: https://github.com/frankgx97/leetcode/blob/master/PartitionList.py
Reverse Linked List II(Medium) https://leetcode.com/problems/reverse-linked-list-ii/
Reverse a linked list from position m to n. Do it in one-pass.
Note: 1 ⤠m ⤠n ⤠length of list.
Example:
**Input:** 1->2->3->4->5->NULL, *m* = 2, *n* = 4
**Output:** 1->4->3->2->5->NULL
è§£æ³ï¼ è¿ä¸ªé¢ç®æ¯Reverse Linked Listçå 强çï¼è¦æ±åªç¿»è½¬ç»å®åºé´å çé¨åãæä»¬é¦å åå§å4个æé start, end, left, rightï¼åå«ä»£è¡¨ç¿»è½¬çå¼å§åç»æçä½ç½®ï¼ä»¥å翻转åºé´çä¹å¤ç左侧åå³ä¾§çèç¹ãå½å®æç¬¬ä¸æ¬¡éåä¹åï¼æä»¬å¯ä»¥å°è¿äºæé齿å对åºçä½ç½®ã
ç¶åæä»¬åç¬å¯¹éè¦ç¿»è½¬çåºé´è¿è¡æä½ï¼è¿æ ·å°±æé®é¢è½¬å为äºåReverse Linked Listç¸åçé®é¢ãå¨ç¿»è½¬çåºé´å ï¼å¨æ¯æ¬¡å¾ªç¯ä¸ç»´æ¤prev,currentåtempä¸ä¸ªæéãé¦å å°current.nextæåè³tempï¼ç¶åå°current.nextæåprevï¼åå°prevèµå¼ä¸ºcurrentï¼æåå°currentæåä¹åæåçtempãéå¤è¿äºæ¥éª¤ç´å°ç¿»è½¬åºé´ç»æã
宿åå°left.nextæåendï¼å°start.nextæårightå³å¯éæ°ä¸²èµ·æ´ä¸ªé¾è¡¨ã
è¿ä¸ªé¢ç®æä¸¤ç§ç¹æ®æ åµï¼ä¸ç§æ¯é¾è¡¨çä»å¤´å°å°¾é½éè¦ç¿»è½¬çæ åµï¼è¿ç§æ åµåªéè¦å¤ç好边çï¼é¿å åºç°ç©ºæéå³å¯ãå¦ä¸ç§æ¯månç¸çï¼å³ä¸éè¦ç¿»è½¬ï¼ç´æ¥è¿å忬çé¾è¡¨å³å¯ã
Solution: https://github.com/frankgx97/leetcode/blob/master/ReverseLinkedListII.py
Restore IP Addresses(Medium) https://leetcode.com/problems/restore-ip-addresses/
Given a string containing only digits, restore it by returning all possible valid IP address combinations.
Example:
**Input:** "25525511135"
**Output:** ["255.255.11.135", "255.255.111.35"]
è§£æ³ï¼DFS è¿ç§ç±»åçé¢ç®å¾æ¾ç¶æ¯DFSï¼åªä¸è¿è¿éé¢ç®ç夿æ¡ä»¶æ´å¤æä¸äºãå¦å¤éè¦ç¹å«æ³¨æå¤çå¤ä¸ª0éå çæ åµã
Jump Game(Medium) https://leetcode.com/problems/jump-game/
Given an array of non-negative integers, you are initially positioned at the first index of the array.
Each element in the array represents your maximum jump length at that position.
Determine if you are able to reach the last index.
Example 1:
``` Input: [2,3,1,1,4] Output: true Explanation: Jump 1 step from index 0 to 1, then 3 steps to the last index.
```
Example 2:
**Input:** [3,2,1,0,4]
**Output:** false
**Explanation:** You will always arrive at index 3 no matter what. Its maximum
jump length is 0, which makes it impossible to reach the last index.
è§£æ³ï¼DP çå°è¿ä¸ªé¢ç®ç¬¬ä¸ååºæ¯DFSãä½å®é ä¸ï¼é¢ç®åªè¦æ±å¾åºæ¯å¦æå¯è¡çè·¯å¾ï¼ä¸éè¦ç»åºè¿ä¸ªè·¯å¾å ·ä½æ¯ä»ä¹ãæä»¥å¯ä»¥ä½¿ç¨DPè§£å³ã
é¦å åå§åä¸ä¸ªæ°ç»memoç¨äºä¿åå¯¹åºæ°ç»ä¸æ¯å¦æè·¯å¾å¯ä»¥å°è¾¾è¿ä¸ªç¹ãå æ¤å°memo[0]设为1ï¼å ¶ä½ä¸º0ãç¶åéånumsæ°ç»ï¼ä»memoä¸å¯¹åºä½ç½®å¼å§åånums个ä½ç½®è®¾ä¸º1ï¼ä»¥æ¤ç±»æ¨ç´å°æ°ç»ç»å°¾ã妿memoçæåä¸ä¸ªä½ç½®ä¸º1ï¼åè¿åTrueï¼åä¹è¿åFalseã
Solution: https://github.com/frankgx97/leetcode/blob/master/JumpGame.cpp
Merge K Sorted Lists(Hard) Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity.
Example:
**Input:**
[
1->4->5,
1->3->4,
2->6
]
**Output:** 1->1->2->3->4->4->5->6
è§£æ³ï¼ ?è¿æ¯æå¨Leetcodeä¸åç第ä¸éHardï¼ä¸è¿çèµ·æ¥ä¹æ²¡æé£ä¹é¾ï¼è¿ä¸ªé¢ç®æ¯ä¹åçMerge 2 Sorted Listsçå级çï¼ä¸è¿å ä¸ºè¿æ¬¡é¾è¡¨çæ°ç®ä¸å®ï¼æä»¥ä¼ç¨å¾®é¾ä¸äºã
æä»¬é¦å åæä¸ä¸è¾å ¥çæ°æ®ç»æãè¾å ¥çç»ææ¯ä¸ä¸ªå表ï¼å ¶ä¸åè¡¨çæ¯ä¸ä¸ªå ç´ é½æ¯ä¸ä¸ªæéï¼æåæ¯ä¸ªé¾è¡¨ç头èç¹ã
æç §æ¯ä¾ï¼æä»¬é¦å åå§åä¸ä¸ªé¾è¡¨èç¹ä½ä¸ºç»æé¾è¡¨çdummy headãç¶åè¿å ¥ä¸ä¸ªå¾ªç¯ã
å¨å¾ªç¯ä¸ï¼æä»¬éè¦æ¾å°å个é¾è¡¨å½åèç¹ä¸æå°çä¸ä¸ªï¼å°ä¸ä¸ä¸ªé¾è¡¨çnextæéæåè¿ä¸ªèç¹ãç¶åå°è¿ä¸ªèç¹æå¨çé¾è¡¨çæéå³ç§»ä¸ä¸ªä½ç½®ï¼è¿æ ·å·²ç»è¢«å å ¥ç»æçèç¹å°±ä¸ä¼å被æ¯è¾ã妿è¿ä¸ªèç¹å·²ç»æ¯é¾è¡¨ç»ç¹ï¼åå°è¯¥æéä»å表ä¸ç§»é¤ï¼å¹¶å¼å§ä¸ä¸å¾ªç¯ï¼ç´å°lists为空ã
Solution: https://github.com/frankgx97/leetcode/blob/master/MergekSortedLists.py
Insert Delete GetRandom O(1)(Medium) https://leetcode.com/problems/insert-delete-getrandom-o1/
è¿éé¢ç®è¦æ±æä»¬å®ç°ä¸ä¸ªæ°æ®ç»æï¼å ¶ä¸å å«æå ¥ï¼å é¤åéæºè¿åä»»æä¸ä¸ªå¼ãå ¶ä¸æ¯ä¸ªæä½çå¤æåº¦é½éè¦ä¸ºO(1)ã
è¿ä¸ªé¢ç®æ¬èº«å¹¶ä¸é¾ï¼ä½æ¯éè¦æä»¬äºè§£Pythonä¸åé¡¹æ°æ®ç»ææä½çå¤æåº¦ã
æä½¿ç¨ä¸ä¸ªdictç»æä½ä¸ºä¸»è¦çåå¨ç»æã对äºinsertæä½ï¼æä»¬é¦å éè¦å¤å®è¦æå ¥çè¿ä¸ªå ç´ æ¯å¦å·²ç»å¨dictçkeyéï¼å¦æå·²ç»åå¨åç´æ¥è¿åFalseï¼å¦æä¸åå¨ï¼ä»¥è¯¥å ç´ ä¸ºé®ï¼æå ¥dictä¸ï¼å¼ä¸ºä»»æãå ¶ä¸dict使ç¨inæä½ç¬¦çå¤æåº¦ä¸ºO(1)ï¼dictçgetåsetå¤æåº¦å为O(1).
对äºremoveæä½ï¼æè·¯åinsert类似ã大è´ä¸ºå 夿è¦ç§»é¤çå ç´ æ¯å¦åå¨ï¼å¦ææ¯ï¼åæè¯¥å ç´ popæï¼å¦åè¿åFalseã
对äºgetRandomï¼é¦å æä»¬éè¦è·åå°dict䏿ækeyçæ°éï¼ä½¿ç¨len()å³å¯ï¼ç¶åçæä¸ä¸ªè¯¥èå´å çéæºæ°ã
ç¶åéè¦å¨dictä¸çæækeyä¸è¿åéæºæ°å¯¹åºçé£ä¸ªkeyãç±äºdictçkeysä¸è½ç´æ¥ç´¢å¼ï¼æä»¥éè¦å ædictçæækeys使ç¨list()è½¬æ¢ælistç±»åãè¿éåççä¸ç¹æ¯æä¸æ¸ æ¥list(dict)çå¤æåº¦ï¼æä»¥è½ç¶è¿ä¸ªè§£æ³è½è¿OJï¼ä½æä¸æ¸ æ¥è¿ä¸ªè§£æ³æ¯å¦ç¬¦åé¢ç®è¦æ±ã
Solution: https://github.com/frankgx97/leetcode/blob/master/InsertDeleteGetRandomO(1).py
åèèµæ https://wiki.python.org/moin/TimeComplexity
https://www.ics.uci.edu/~pattis/ICS-33/lectures/complexitypython.txt
â¦å¾ ç»â¦
æåæ¥ä½¿ç¨çGitæç®¡è½¯ä»¶æ¯Gogsï¼è¿ææè¿ç§»å°äºGitLabãGitLabé¤äºGitæç®¡ä¹å¤ï¼è¿å æ¬CI/CDï¼Registryï¼Pagesçä¸ç³»ååè½ãOmnibus Installer使GitLabçå®è£ åé ç½®ç®åäºå¾å¤ï¼ä½æ¯è¦ä½¿ç¨æ´å¤çåè½ï¼ä»ç¶éè¦æå·¥é ç½®ä¸äºæå¡ã
æ¬æè®°è¿°äºä½¿ç¨Dockeré¨ç½²GitLabåå¼å¯CI/CDï¼Registryï¼Pagesçåè½çæ¥éª¤åé ç½®ã
ç¡¬ä»¶éæ± 宿¹ç»åºçç¡¬ä»¶éæ±æ¯1Core CPU+ 512MB RAM + 1.5GB SWAPæ¯è¿è¡GitLabçæä½è¦æ±ãæä½¿ç¨çæ¯Digital Oceanç5$/æç1C1Gå®ä¾ï¼å¼å¯äº4GçSwapã
GitLabæ¬ä½ Omnibuså å å«äºGitLabä¾èµä¸ç³»åç软件åç»ä»¶ï¼å¦ruby, rails, Sidekiq, PostgreSQLçãå¨Dockerä¸ï¼è¿äºæå¡é½è¿è¡å¨åä¸ä¸ªå®¹å¨ä¸ã容å¨ä¸å·²ç»å å«äºnginxï¼æ éæå¨é ç½®nginxåå代çã使ç¨å¦ä¸çcomposeæä»¶å¯å¨GitLabã
``` web: image: 'gitlab/gitlab-ce:latest' restart: always hostname: 'git.example.com.' environment: GITLAB_OMNIBUS_CONFIG: | external_url 'https://git.example.com' ports: - '80:80' - '443:443' - '22:22' volumes: - './data/config:/etc/gitlab' - './data/logs:/var/log/gitlab' - './data/data:/var/opt/gitlab'
```
å½external_urlåæ°è¢«è®¾ç½®ä¸ºä»¥https://æå¤´æ¶ï¼GitLabé»è®¤ä½¿ç¨Letâs Encryptç¾åè¯ä¹¦ãå½httpåhttps被æ´é²å°æ åç«¯å£æ¶ï¼Let’s Encryptä¼éè¿http challengeæ¹å¼æ§è¡èªå¨ç¾åã
å½å®¹å¨å¯å¨åï¼GitLabçåºæ¬åè½å°±å¯ä»¥ä½¿ç¨äºã
GitLab Runner 宿¹ææ¡£ï¼ Install GitLab Runner | GitLab
GitLabå®è£
宿åï¼CI/CDå·²ç»é»è®¤å¯ç¨ï¼ä½æ¯æææå»ºåä¼ä¿æå¨stuckç¶æï¼å 为GitLabå®ä¾è¿æ²¡ææ³¨åä»»ä½å¯ç¨çRunnerï¼å³GitLab CIçæå»ºæ§è¡å¨ã
Runnerä¸å å«å¨omnibuså®è£ å ä¸ï¼éè¦åç¬å®è£ ãRunnerä¸éè¦åGitLabå®ä¾å®è£ å¨å䏿å¡å¨ä¸ã
é ç½® 宿¹ææ¡£ï¼ Registering Runners | GitLab
é¦å æä»¬éè¦æ³¨åä¸ä¸ªRunnerï¼ä½¿å ¶çæé ç½®æä»¶ã使ç¨ç®¡çåè´¦å·ç»å½GitLabå®ä¾ï¼å¨https://your.gitlab.instance/admin/runnersä¸è·å¾å®ä¾URLåæ³¨å令çã
å¨ç¨äºå®è£ Runnerçæå¡å¨ä¸æ§è¡ä¸é¢çå½ä»¤ï¼å¨è¿æé´GitLabå®ä¾éè¦ä¿æå¼å¯ã
``` docker run --rm -t -i -v ./runner/config:/etc/gitlab-runner gitlab/gitlab-runner register
```
ç³»ç»ä¼å¼¹åºä¸é¢å 个é®é¢ï¼
.gitlab-ci.yml䏿²¡ææå®åºç¡éåæ¶é»è®¤ä½¿ç¨çéåï¼æä½¿ç¨ubuntuãè¿è¡
宿åï¼å¨composeæä»¶ä¸æ·»å å¦ä¸å
容ï¼ç¶ådocker-compose downå¹¶upã
``` runner: image: gitlab/gitlab-runner:latest volumes: - './runner/config:/etc/gitlab-runner' # é ç½®æä»¶ç®å½ - '/var/run/docker.sock:/var/run/docker.sock' # 宿主æºçDocker Socket
```
Docker Registry 宿¹ææ¡£ï¼GitLab Container Registry administration | GitLab
æä»¬æä¸¤ç§æ¹æ¡æ¥é¨ç½²Registryã
使ç¨ä¸GitLabå®ä¾ç¸åçååï¼ä¸åçç«¯å£ å¨gitlab.rbä¸ä¿®æ¹ä¸é¢ä¸è¡å³å¯ã1234端å£å¯ä»¥èªå·±ä»»æè®¾ç½®ï¼ä½æ¯éè¦é¿å¼5000å5001以é¿å å²çªï¼åæ¶è®°å¾å¨composeæä»¶ä¸æ´é²ç¸åºç端å£ã
``` registry_external_url 'https://gitlab.example.com:1234'
```
使ç¨ä¸GitLabå®ä¾ä¸åçååï¼ç¸åçç«¯å£ å¨gitlab.rbä¸ä¿®æ¹å¦ä¸ä¸è¡ã
``` registry_external_url 'https://registry.gitlab.example.com'
```
æå¨ä½¿ç¨èªå¨ç¾åæ¶éå°äºé®é¢ï¼æä»¥æéæ©èªå·±ä½¿ç¨acme.shç¾åè¯ä¹¦ãç¾å宿åå°è¯ä¹¦åç§é¥æä»¶æ·è´å°gitlabé ç½®ç®å½ï¼æç´æ¥éè¿Dockeræ°æ®å·æè½½ã
å¦æä½ è¯å¾å°acme.shç¾åçè¯ä¹¦éè¿Dockeræè½½ï¼ç±äºacme.shç¾åçè¯ä¹¦è·¯å¾ä¸å¸¦ææå·ï¼Docker Composeå¨è¯å«è·¯å¾æ¶ä¼åºç°é®é¢ãå æ¤æå»ºè®®å°æ´ä¸ª.acme.shç®å½æè½½è¿å®¹å¨ï¼å¦æä½ 没æä¿æ¤å ¶ä»éGitLabè¯ä¹¦çéæ±ï¼ï¼è䏿¯æè½½è·¯å¾ä¸å¸¦ææå·çè¯ä¹¦æä»¶ã
妿è¯ä¹¦æä»¶ååååä¸ç¸åï¼éè¦ä¿®æ¹gitlab.rb
``` registry_nginx['ssl_certificate'] = "/etc/gitlab/ssl/certificate.pem" registry_nginx['ssl_certificate_key'] = "/etc/gitlab/ssl/certificate.key"
```
GitLab CI
使ç¨GitLab CIæå»ºDockeréå
å¨åºäºDocker ExecutorçRunnerä¸æå»ºDockeréåï¼Runneréè¦è¿è¡å¨ç¹ææ¨¡å¼ï¼Privileged Modeï¼ãå¨Runnerçconfig.tomlä¸ï¼å°privilegedæ¹ä¸ºtrue以å¼å¯ç¹ææ¨¡å¼ã
GitLab CI Token
å¨Pipelineä¸ç»å½GitLabèªå¸¦çContainer Registryæ¶ï¼å½ä»¥gitlab-ci-tokenç¨æ·ç»å½æ¶ï¼å¯ç å°èªå¨ä»¥ç¯å¢åé$CI_JOB_TOKENè¢«ä¼ å
¥ï¼æ é设置é¢å¤çç¯å¢åéã
.gitlab-ci.yml
``` image: docker:stable
services: - docker:dind
variables: CONTAINER_IMAGE: registry.example.cn/$CI_PROJECT_PATH
before_script: - docker info
build: stage: build script: - docker build -t $CONTAINER_IMAGE:$CI_COMMIT_REF_NAME . - docker login -u gitlab-ci-token -p $CI_JOB_TOKEN registry.example.cn - docker push $CONTAINER_IMAGE:$CI_COMMIT_REF_NAME
```
GitLab Pages 宿¹ææ¡£ï¼ GitLab Pages administration | GitLab
GitLab Pagesåæä»¬å¹³æ¶ä½¿ç¨çGitHub Pagesæä¸äºä¸åï¼GitHub Pagesç´æ¥æç®¡gitä»åºä¸çéæHTMLç½ç«ï¼èGitLab Pagesåä¾èµäºGitLab CIå°gitä»åºä¸ç代ç ï¼å¯ä»¥æ¯çº¯HTMLï¼ä¹å¯ä»¥æ¯HexoæJekyll项ç®ï¼éè¿Pipelineæå»ºåæç®¡ã
æ¡ä»¶ 使ç¨GitHubéè¦å¦ä¸æ¡ä»¶ï¼
é
ç½®
ä¿®æ¹gitlab.rbï¼å³å¯å¯ç¨Pagesã
``` pages_external_url 'https://example.io'
```
Letâs Encryptèªå¨ç¾åä¸éç¨äºGitLab Pagesï¼å æ¤æä»¬ä½¿ç¨acme.shèªè¡ç¾åä¸ä¸ªéé 符SSLè¯ä¹¦ï¼å¹¶ä¿®æ¹é ç½®ã
``` pages_nginx['redirect_http_to_https'] = true pages_nginx['ssl_certificate'] = "/etc/gitlab/ssl/pages-nginx.crt" pages_nginx['ssl_certificate_key'] = "/etc/gitlab/ssl/pages-nginx.key"
```
宿忧è¡reconfigure
ä½¿ç¨ æä»¬å¯ä»¥éè¿å éä¸ä¸ªç¤ºä¾é¡¹ç®ï¼å¦https://gitlab.com/pages/jekyllï¼æ¥çæGitLab Pagesç使ç¨ã
å½ä»åºè¢«æ´æ°æ¶ï¼GitLab CIå°æç
§æµæ°´çº¿æ§è¡æå»ºï¼å¹¶å°çæçéæHTMLæä»¶è¾åºå°/publicã
妿æå»ºæåï¼å³å¯éè¿https://username.yourgitlab.io/project访é®ã
TroubleShooting
妿Pipelineæ§è¡æåï¼ä½æ¯è®¿é®Pages页颿¶æ¥åº502é误ï¼åæ¶æ¥å¿ä¸åºç°running the daemon as unprivileged userï¼è¯·åèä¸é¢çè§£å³æ¹æ¡ï¼
https://gitlab.com/gitlab-org/gitlab-pages/issues/129
æä»¥å使ç¨çä»£ç æç®¡ç³»ç»æ¯Gogsï¼è¿ææåæ¢å°äºæ´ç¥åçä»£ç æç®¡ç³»ç»GitLabãå æ¤ï¼æéè¦å°åæ¥æç®¡å¨Gogså®ä¾ä¸ç项ç®è¿ç§»å°GitLabå®ä¾ã
GitLab䏿ä¾äºä¸äºè¿ç§»å·¥å ·ï¼å ¶æ¯æçå¹³å°å¦ä¸å¾ï¼
å ¶ä¸ï¼Giteaæ¯Gogsçä¸ä¸ªåæ¯çæ¬ï¼æè¯çéè¿Giteaé项ä»Gogså¯¼å ¥é¡¹ç®ï¼è½ç¶GitLabè½å¤æ£å¸¸ååºGogsä¸ç项ç®å表ï¼ä½æ¯å¨å¯¼å ¥æ¶åä¼å¼å500é误ã
ç»é è¯»ææ¡£å¾ç¥ï¼GogsåGitLabåæä¾äºæä½ä»£ç ä»åºçAPIãå æ¤ï¼æå¯ä»¥ç¼åä¸ä¸ªPythonèæ¬æ¥è¿ç§»é¡¹ç®ã
å®ç° æºä»£ç ï¼https://github.com/frankgx97/migrate-gogs-to-gitlab
è·åGogsä¸çä»åºå表 宿¹ææ¡£ï¼ docs-api/Repositories at master · gogs/docs-api
å¨Gogsä¸ä½¿ç¨GET /user/reposæ¥å£å³å¯è·å¾å½å已认è¯ç¨æ·ï¼éè¿ä¸ªäººæä½ä»¤ç认è¯ï¼ææè¯»åçææä»åºä¿¡æ¯ãå
¶ä¸æä»¬éè¦çåæ®µæ¯
full_nameï¼å½¢å¦user/repoçä»åºå
¨ådescriptionï¼ä»åºæè¿°clone_urlï¼HTTPSçä»åºurlå¯¼å ¥ä»åºè³GitLab 宿¹ææ¡£ï¼ Projects API | GitLab
å¨GitLabä¸ä½¿ç¨POST /projectså³å¯å建æ°ä»åºãæä»¬éè¦ä¼ å
¥å¦ä¸çåæ°ï¼
nameï¼ä»åºåç§°descriptionï¼ä»åºæè¿°visibilityï¼è®¾ç½®ä»åºçå¯è§æ§ï¼å¯éçé项æprivate, internalåpublicimport_urlï¼å
å«ä¸ªäººæä½ä»¤ççGogsä»åºURLï¼å½¢å¦https://806f09c786f04bf8663hf02h9c4696ebf96bfb49@your.gogs.com/foo/bar.git使ç¨
ä¿®æ¹sample.pyï¼å¡«å
¥GogsåGitLabå®ä¾çURLï¼æä½ä»¤çå第30è¡çGogsååï¼å¹¶è¿è¡sample.pyå³å¯ã
å¨ä½¿ç¨Dockeré¨ç½²åºç¨æ¶ï¼Dockeræ¨èçæ¹å¼æ¯å°åºç¨åå ¶æä¾èµçæå¡ï¼MySQLï¼Redisçï¼å使ç¨Dockeré¨ç½²ï¼å¹¶éè¿linkæèªå®ä¹ç½ç»ç¸è¿æ¥ã使¯ï¼å½åºç¨æä¾èµçæå¡è¢«å®è£ å¨å®¿ä¸»æºä¸æ¶ï¼æä»¬éè¦è®©å®¹å¨ä¸çåºç¨è½å¤è®¿é®å°é¨ç½²å¨å®¿ä¸»æºä¸çæå¡ãæ¬æå°ä»ç»å®ç°è¿ä¸ç®ççå ç§æ¹æ¡ï¼å¹¶åæå ¶ä¼ç¼ºç¹ã
é¦å æä»¬éè¦äºè§£ä¸äºå ³äºDockerç½ç»çåºç¡ç¥è¯ã
Dockerç½ç» Dockeræä¾äº5ç§ç½ç»ç±»åï¼è¿éä»ç»å ¶ä¸å¸¸è§ç两ç§ï¼bridgeåhost
Bridge
Bridgeæ¯Dockeré»è®¤ä½¿ç¨çç½ç»ç±»åãå¦å¾ï¼ç½ç»ä¸çææå®¹å¨å¯ä»¥éè¿IPäºç¸è®¿é®ãBridgeç½ç»éè¿ç½ç»æ¥å£docker0 ä¸ä¸»æºæ¡¥æ¥ï¼å¯ä»¥å¨ä¸»æºä¸éè¿ifconfig docker0æ¥çå°è¯¥ç½ç»æ¥å£çä¿¡æ¯ã
Host Host模å¼ä¸ï¼å®¹å¨çç½ç»æ¥å£ä¸ä¸å®¿ä¸»æºç½ç»é离ãå¨å®¹å¨ä¸çå¬ç¸åºç«¯å£çåºç¨è½å¤ç´æ¥è¢«ä»å®¿ä¸»æºè®¿é®ãhostç½ç»ä» æ¯æLinuxã
æ¹æ¡
æ¹æ¡1ï¼ä½¿ç¨host模å¼
éè¿docker run å¯å¨å®¹å¨æ¶å å
¥--net=host åæ°ï¼æå¨composeæä»¶ä¸æå®network_mode: "host" ï¼ä¾å¦ï¼
version: '3'
services:
foo:
container_name: "foo"
image: "foo/bar"
ports:
- "8000:8000"
network_mode: "host"
restart: always
è¯¥åæ°æå®è¯¥å®¹å¨ä½¿ç¨hostç½ç»æ¨¡å¼ï¼å æ¤ä¹æ éæ å°ç«¯å£ã
ä¼ç¹
缺ç¹
æ¹æ¡2ï¼ä½¿ç¨docker0ç½ç»çé»è®¤ç½å ³å°å å¨é»è®¤çbridge模å¼ä¸ï¼docker0ç½ç»çé»è®¤ç½å ³å³æ¯å®¿ä¸»æºãå¨Linuxä¸ï¼docker0ç½ç»é常ä¼åé ä¸ä¸ª172.17.0.0/16çç½æ®µï¼å ¶ç½å ³é常为172.17.0.1ï¼macOSä¸çç½æ®µå为192.168.65.0/24ï¼ç½å ³ä¸º192.168.65.1ãå¨å®¹å¨ä¸ä½¿ç¨è¯¥IPå°åå³å¯è®¿é®å®¿ä¸»æºä¸çåç§æå¡ã
éè¦æ³¨æçæ¯ï¼è¿ç§æ åµä¸ï¼ç»ç±docker0ç½æ¡¥èæ¥çæµéä¸ç»è¿å®¿ä¸»æºçæ¬å°åç¯ï¼å æ¤éè¦å°å®¿ä¸»æºä¸çåºç¨ï¼MySQLï¼Redisçï¼é 置为çå¬0.0.0.0ã
ä¼ç¹
缺ç¹
æ¹æ¡3ï¼Dockeræä¾çæå宿主æºçDNS
macOSçDockeræä¾äºä¸ä¸ªæå宿主æºçåådocker.for.mac.host.internal ãå¨éè¦è®¿é®å®¿ä¸»æºæå¡æ¶ä½¿ç¨æ¤ååå³å¯ãå
¶å®ç°åçæäººè¿è¡äºå¦ä¸ç ç©¶ï¼
Understanding the ‘docker.for.mac.localhost’ behavior – Docker Desktop for Mac – Docker Forums
ä¼ç¹
缺ç¹
æ¹æ¡4ï¼å¨å®¹å¨ä¸è·å宿主æºå°å å¨DockerfileçCMDé¨åæ·»å å¦ä¸ä¸æ¡å½ä»¤ï¼
ip -4 route list match 0/0 | awk â{print $3 âhost.docker.internalâ}â >> /etc/hosts
ip -4 route list match 0/0 å½ä»¤ä¼ååºå½åç³»ç»çé»è®¤ç½å
³ï¼å¹¶å°host.docker.internal ååè§£æè³å®ã
请注æipå½ä»¤å¹¶ä¸ä¸å®ééåé带ï¼å¦ææ²¡æçè¯ï¼ä½¿ç¨apt install iproute2 å®è£
ã
ä¼ç¹
缺ç¹
åèèµæ é ç½® docker0 ç½æ¡¥ · Docker ââ ä»å ¥é¨å°å®è·µ
networking – What is the relation between docker0 and eth0? – Stack Overflow
Dockerçæ¡¥æ¥ç½ç»æ¯æä¹å·¥ä½ç | æç¨åºåæ¹åä¸ç
Docker â add host.docker.internal on linux
docker 跨主æºç½ç»ï¼overlay ç®ä» | Cizixs Write Here