轮转数组
要点:三次反转
java
class Solution {
public void rotate(int[] nums, int k) {
int n = nums.length;
k = k%n;
reverse(0,n-k-1,nums);
reverse(n-k,n-1,nums);
reverse(0,n-1,nums);
}
public void reverse(int left, int right, int[] nums){
while(left < right){
int temp = nums[left];
nums[left] = nums[right];
nums[right] = temp;
left++;
right--;
}
}
}
跳跃游戏
要点:维护最大能达到的位置
java
class Solution {
public boolean canJump(int[] nums) {
int n = nums.length;
int zuiyuan = 0;
//记录最远的可以的位置就行
for(int i = 0; i < n; i++){
//先判断这个点能不能到
if(i > zuiyuan){
return false;
}
//更新
int temp = i+nums[i];
zuiyuan = Math.max(zuiyuan,temp);
//剪枝
if(zuiyuan >= n-1){
return true;
}
}
return true;
}
}
跳跃游戏 II
要点:
方法1:dp
java
class Solution {
public int jump(int[] nums) {
//dp
int n = nums.length;
int[] dp = new int[n];
for(int i = 0 ; i < n ; i++){
dp[i] = Integer.MAX_VALUE;
}
dp[0] = 0;
for(int i = 0; i < n; i++){
int maxl = i+nums[i];
if(nums[i] == 0){
continue;
}
for(int j = i+1 ; j <=maxl && j < n; j++){
dp[j] = Math.min(dp[j], dp[i]+1);
}
}
return dp[n-1];
}
}
方法2:灵神的造桥法
java
class Solution {
public int jump(int[] nums) {
int ans = 0;
int curEnd =0;
int nextEnd = 0;
for(int i = 0; i < nums.length-1; i++){
nextEnd = Math.max(nextEnd, i+nums[i]);
if(i == curEnd){
curEnd = nextEnd;
ans++;
}
}
return ans;
}
}
核心题:TCP 怎么保证可靠传输?
面试官为什么这么问?
这是一道可以让你把课本知识系统输出的题。我要看你能不能有条理地说出确认应答、超时重传、滑动窗口、流量控制、拥塞控制这几个核心机制,最好还能简要解释每一点。
希望听到怎样的回答:
- 确认应答与序列号:每个字节有编号,接收方回复 ACK 告知期望的下一个字节号,确保数据按序无损到达。
- 超时重传:发送方等待 ACK 超时,会自动重传数据包。
- 滑动窗口:不一一确认,发送方可连续发送窗口内的多个包,提高吞吐;接收方返回窗口大小,控制发送速率(流量控制)。
- 流量控制 :接收方在 ACK 中携带
rwnd窗口大小,防止发送太快撑爆接收方缓存。 - 拥塞控制:慢启动、拥塞避免、快重传、快恢复,防止网络拥塞。
- 最好能举一个例子:比如你下载文件时网络抖了一下,TCP 就靠重传机制保证了文件完整。
候选人 :
好的,TCP 的可靠性是通过一系列机制协同工作来实现的。我按从基础到进阶的顺序,把这些机制串起来讲清楚。
第一,确认应答与序列号------可靠性的基石。
TCP 把要发送的数据看成一个个字节,给每个字节都分配一个唯一的序列号。接收方每收到一批数据,就会回复一个 ACK 报文,里面带着确认号,意思是"我期待的下一个字节的序列号"。比如接收方收到序列号 1-1000 的数据,就回复 ACK,确认号为 1001,告诉发送方"1001 之前的数据我都收到了,你从 1001 开始发"。
这个机制看似简单,但它是 TCP 可靠性最根本的保障。通过序列号和确认号的持续握手,发送方能够精确知道哪些数据已经安全到达,哪些还没有。
第二,超时重传------兜底手段。
网络是不稳定的,数据包可能在传输过程中丢失,ACK 也可能丢失。发送方每发一个数据包,都会启动一个重传定时器。如果在定时器超时之前没有收到对应的 ACK,发送方就认为这个包丢了,自动重传这段数据。
超时时间 RTO 并不是一个固定值,而是根据网络状况动态计算出来的。TCP 会持续统计 RTT(往返时间)的加权平均值和偏差,当网络抖动变大时,RTO 相应变大;网络平稳时,RTO 变小。这种自适应重传策略既能在网络差时保证可靠,又不会在网络好时浪费等待时间。
重传的触发方式其实有两种。 除了超时重传,TCP 还有快速重传机制。发送方不需要傻等定时器超时,如果连续收到三个相同的 ACK(比如接收方反复说"我期待 1001"),说明序号 1001 之后的数据已经到达了接收方,但 1001 这个包丢了。这时发送方不等定时器到期,立刻重传丢失的那个包。两种重传互补,一个为绝对兜底而设,一个为快速响应连续丢包而设。
第三,滑动窗口------从"一问一答"到"流水线"传输。
如果发送方每发一个包就等 ACK 再发下一个,这就是停等协议,效率极低。TCP 用滑动窗口机制解决了这个问题。
发送方维护一个发送窗口,窗口内的数据可以一次性连续发出,不需要等前面的确认。接收方也维护一个接收窗口,缓存乱序到达的数据。发送方收到 ACK 后,窗口向前滑动,继续发送新的数据。
滑动窗口把一问一答的串行模式,变成了连续发送的流水线模式,吞吐量得到了质的提升。窗口大小不是固定的,由流量控制和拥塞控制共同调节。
第四,流量控制------保护接收方。
接收方的处理能力是有限的,如果发送方发得太快,接收方的缓冲区会被塞满,新数据装不进去只能丢弃,这就失去了可靠传输的意义。
TCP 的做法是:接收方在每次 ACK 报文中,都会在头部携带一个窗口大小字段(rwnd),告诉发送方"我的接收缓冲区还剩多少空间"。发送方据此调整发送窗口,保证在途数据不会超过接收方的处理能力。如果接收方缓冲区快满了,rwnd 变小,发送方就减速;如果接收方处理得快,rwnd 变大,发送方就加速。
如果 rwnd 变成 0,发送方会停止发送数据,但会定期发送窗口探测报文,防止接收方后来清出空间后发的更新 rwnd 的报文丢失导致死锁。
第五,拥塞控制------保护网络。
流量控制是端到端的,保护的是接收方;拥塞控制则是发送方和网络之间的,保护的是整个网络的承载能力。
TCP 假设数据包丢失不仅仅是因为物理错误,更可能是因为网络中的某个路由器缓冲区满了,开始丢包。一旦出现拥塞还大量发包,只会雪上加霜。拥塞控制通过四种策略动态调整发送速率:
- 慢启动:连接刚开始时,拥塞窗口 cwnd 设为很小的值,每收到一个 ACK 指数翻倍增长,快速探测网络的可用带宽。
- 拥塞避免:当 cwnd 达到慢启动门限 ssthresh 后,改为线性增长,每经过一个 RTT 加一,温和逼近极限。
- 快重传:收到三个重复 ACK 不等定时器,立刻重传。
- 快恢复:发生快重传后,不降到零重新慢启动,而是把 cwnd 减半,然后从减半后的位置进入拥塞避免,快速恢复到合理区间。
最终发送窗口的大小,是流量控制的 rwnd 和拥塞控制的 cwnd 两者的最小值。既要考虑接收方能承受多少,也要考虑网络能承受多少。
第六,结合一个具体场景串起来。
比如你在下载一个文件时,网络突然抖了一下。
发送方连续发了一组包。其中 1001-2000 的包丢了,其余数据包陆续到达了接收方。接收方每收一个后续包,都回复 ACK,确认号都是 1001,意思是"1001 这块数据我还没收到,我一直期待它"。
发送方连续收到了三个相同确认号 1001 的 ACK,快速重传机制被触发,不等定时器超时,直接重传 1001-2000 这个包。接收方终于收到缺失的包,回复一个更大的确认号,滑动窗口继续前滚,文件完整下载完成。
如果重传的包还是丢了,超时重传会作为兜底机制接管,保证数据最终一定送达。
总结一句话:TCP 的可靠性靠的是五层机制协同------序列号与确认应答建立丢失感知、超时重传兜底补救、滑动窗口开启流水线传输、流量控制匹配收发速率、拥塞控制顺应网络状态。每个机制各司其职,共同构建了 TCP 可靠传输的完整体系。
碎碎念:后续会更新每天学习的八股和算法 题,开始准备秋招的第34天。努力连续更新100天!以后每天就按,秋招项目【java +agent】,科研,必做项目,算法,八股,锻炼身体来总结。
总结:得多动脑子
1.算法要系统过一遍晚上】1h
2.秋招项目,【java】开始实际看业务,1/6;无
【agent 】还在学,整理cc,无,
3.科研要跑一下,
4.检测项目也得总结文档,
6.背八股,无
7.锻炼身体,无
反思:睡到中午,然后下午加班,晚上玩手机,没学习,确实还是要保持学习习惯和健康作息