目录
选择题
1.
当发生拥塞时,TCP 有个机制,会从慢开始进行发送。
1. 把慢开始的门限值设为当前窗口的一半,即 ssthresh = 1/2 * 8 = 4 KB
2. 把拥塞窗口 cwnd 设置为 1 个最大报文段 MSS 大小(1KB)。
3. 再次从慢启动阶段开始。
开始慢启动 cwnd = 1 KB,之后呈指数增长。
经过 1 个 RTT,cwnd = 1 * 2 = 2KB
经过 2 个 RTT,cwnd = 2 * 2 = 4KB,此时达到了门限值 ssthresh,之后进入拥塞避免阶段(线性增长)
经过 3 个 RTT,cwnd = 4 + 1 = 5KB,由于题目说一直没发生超时,cwnd 会一直线性增长到接收窗口大小。
......
经过 8 个 RTT,cwnd = 10KB,因为发送端不能超过接收端的 10KB,此后拥塞窗口一直保持 cwnd = 10KB
发送窗口大小 = min(接收窗口 ,拥塞窗口) = 10KB
2.
子网掩码是 255.255.252.0 (11111111.11111111.11111100.00000000) 网段号为连续的 1,主机号为连续的 0,则有 10 位是主机号。
180.80.77.55(10110100.01010000.01001101.00110111),前 22 位是网段号,因为是发送广播分组,所以求的是广播地址,广播的主机号是全 1,所以 180.80.77.55 所在的广播地址就是 180.80.010011 (10110100.01010000.010011),主机号是 11.11111111,所以广播地址就是 180.80.79.255。
3.
当一台设备知道对方的 IP 地址,不知道对方的 MAC 地址时,启动 ARP,发送 ARP Request 请求广播包到其他主机,收到广播包的主机查看,只有该请求的设备才会单播回答 ARP Reply 响应包(会携带目标主机的 MAC 地址)。所以 ARP 协议本质上是用 IP 地址查询 MAC 地址的协议。
4.
IP 层不对数据部分进行差错校验的原因:
因为网络层是“尽最大努力完整的传输数据包”,差错检测已由数据链路层实现,网络层没必要再进行一次校验。
优点是因为不负责差错检测和纠错,所以可获得较高的传输性能。
而缺点则是因为网络层不负责差错检测,那么错误检测只能在传输层或应用层被发现,使纠错的时间增加了。
5.
除了 TCP 是有连接可靠的协议,其他都是无连接不可靠的协议。
6.
ICMP 控制报文协议,它是 TCP/IP 协议族的一个子协议,用于在 IP 主机,路由器之间传递控制消息。
ICMP 底层协议使用 IP 协议。
7.
三次握手:
回复 ACK 和 SYN,所以 ACK 和 SYN 都是 1。
由于甲在 SYN 数据报中的序号为 (seq = 11220),所以主机乙给主机甲回复的确认序号是 ack = 11221。
乙发送的 SYN + ACK 数据包的序号 seq 可以从任意位置开始。
8.
网络层提供的是无连接不可靠的数据包服务(IP 协议)。
9.
发送窗口上限值 = min(接收窗口,拥塞窗口) = (2000, 4000) = 2000。
第二次甲给乙发送 MSS = 1000,甲没有收到乙的确认,所以此时甲要把第二个报文段缓存下来,最后能发送 2000 - 1000 = 1000。
10.
一个 IP 的组成部分为:网络号 + 子网号 + 主机号
而原来只有 24 位是网络号,现在子网掩码变成了 255.255.255.248,则发现现在有 29 位是网络号,比原来增加了 5 位,所以最大子网数 = 2^5 = 32,而主机号则有 3 位,就是 2^3 = 8,除去网段号(全0)和广播号(全1) 还剩下 6 个。
编程题
1. 五子棋
这道题不难,就是直接遍历棋盘的每个元素,如果该元素是棋子,那就写一个方法来看看从该元素位置开始,分别计算横向,竖向,对角线,斜对角线的棋子个数,如果其中有棋子个数大于等于 5 个的,则说明该棋盘有五子连珠,输出 Yes,然后返回,如果遍历完棋盘都没有输出 Yes,那就直接输出 No。
代码实现:
// 冗余代码较多版
import java.util.*;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
// 注意 hasNext 和 hasNextLine 的区别
while (in.hasNext()) { // 注意 while 处理多个 case
int n = 20;
char[][] arr = new char[n][n];
for (int i = 0; i < n; i++) {
String tmp = in.next();
arr[i] = tmp.toCharArray();
}
print(arr, n);
}
}
public static void print(char[][] arr, int n) {
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
// 如果是棋子,就得看看有没有五子连珠
if (arr[i][j] != '.') {
int count = getCount(arr, i, j, arr[i][j]);
if (count >= 5) {
System.out.println("Yes");
return;
}
}
}
}
System.out.println("No");
}
public static int getCount(char[][] arr, int x, int y, char sign) {
// 往横向,竖向,对角线方向走看看是否五子连珠
int max = -1;
int cx = x;
int cy = y;
// 横向 x,y - 1
int count = 1;
while (cy >= 1 && arr[cx][cy - 1] == sign) {
count++;
cy--;
}
cy = y; // x,y + 1
while (cy + 1 < 20 && arr[cx][cy + 1] == sign) {
count++;
cy++;
}
if (max < count) {
max = count;
}
cx = x;
cy = y;
count = 1;
// 竖向
while (cx >= 1 && arr[cx - 1][cy] == sign) {
count++;
cx--;
}
cx = x;
while (cx + 1 < 20 && arr[cx + 1][cy] == sign) {
count++;
cx++;
}
if (max < count) {
max = count;
}
cx = x;
cy = y;
count = 1;
// 对角线
while (cx >= 1 && cy >= 1 && arr[cx - 1][cy - 1] == sign) {
count++;
cx--;
cy--;
}
cx = x;
cy = y;
while (cx + 1 < 20 && cy + 1 < 20 && arr[cx + 1][cy + 1] == sign) {
count++;
cx++;
cy++;
}
if (max < count) {
max = count;
}
// 斜对角线
cx = x;
cy = y;
count = 1;
while (cx >= 1 && cy + 1 < 20 && arr[cx - 1][cy + 1] == sign) {
count++;
cx--;
cy++;
}
cx = x;
cy = y;
while (cx + 1 < 20 && cy >= 1 && arr[cx + 1][cy - 1] == sign) {
count++;
cx++;
cy--;
}
if (max < count) {
max = count;
}
return max;
}
}
// 优化版
import java.util.*;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
// 注意 hasNext 和 hasNextLine 的区别
while (in.hasNext()) { // 注意 while 处理多个 case
int n = 20;
char[][] arr = new char[n][n];
for (int i = 0; i < n; i++) {
String tmp = in.next();
arr[i] = tmp.toCharArray();
}
print(arr, n);
}
}
public static void print(char[][] arr, int n) {
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
// 如果是棋子,就得看看有没有五子连珠
if (arr[i][j] != '.') {
int count = getCount(arr, i, j, arr[i][j]);
if (count >= 5) {
System.out.println("Yes");
return;
}
}
}
}
System.out.println("No");
}
public static int getCount(char[][] arr, int x, int y, char sign) {
// 往横向,竖向,对角线方向走看看是否五子连珠
int max = -1;
// 横向 (x,y-1) (x,y+1)
int count = getTheDirectionCount(arr, x, y, sign, 0, -1) + getTheDirectionCount(arr, x, y, sign, 0, 1) + 1;
if (max < count) {
max = count;
}
// 竖向 (x-1,y) (x+1,y)
count = getTheDirectionCount(arr, x, y, sign, -1, 0) + getTheDirectionCount(arr, x, y, sign, 1, 0) + 1;
if (max < count) {
max = count;
}
// 对角线 (x-1,y-1) (x+1,y+1)
count = getTheDirectionCount(arr, x, y, sign, -1, -1) + getTheDirectionCount(arr, x, y, sign, 1, 1) + 1;
if (max < count) {
max = count;
}
// 斜对角线 (x-1,y+1) (x+1,y-1)
count = getTheDirectionCount(arr, x, y, sign, -1, 1) + getTheDirectionCount(arr, x, y, sign, 1, -1) + 1;
if (max < count) {
max = count;
}
return max;
}
public static int getTheDirectionCount(char[][] arr, int x, int y, char sign, int i, int j) {
int count = 0;
while (x + i < 20 && x + i >= 0 && y + j < 20 && y + j >= 0 && arr[x + i][y + j] == sign) {
count++;
x += i;
y += j;
}
return count;
}
}
2. Emacs计算器
思路:计算后缀表达式(逆波兰表达式),我们可以利用栈来实现。遍历字符串,遇到数字就将数字入栈,如果遇到了运算符,那就出栈两次得到两个数字,但是要注意,先出栈的是右操作数,后出栈的是左操作数,顺序不能乱,然后再根据运算符来计算两个数字的结果,再将结果入栈,到了最后,栈里就只有一个数字,输出该数字即可。
代码实现:
import java.util.*;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
// 注意 hasNext 和 hasNextLine 的区别
while (in.hasNextInt()) { // 注意 while 处理多个 case
int n = in.nextInt();
in.nextLine();
String str = in.nextLine();
int ans = emacs(n, str);
System.out.println(ans);
}
}
public static Integer emacs(int n, String str) {
Stack<Integer> stack = new Stack<>();
String option = "+-*/";
String[] num = str.split(" ");
for (int i = 0; i < num.length; i++) {
String s = num[i];
if (option.contains(s)) {
// 如果是操作符,则出栈出两次得到 num2 和 num1,
// 然后计算出结果,再把结果入栈
int num2 = stack.pop();
int num1 = stack.pop();
int ret = 0;
switch (s) {
case "+":
ret = num1 + num2;
break;
case "-":
ret = num1 - num2;
break;
case "*":
ret = num1 * num2;
break;
case "/":
ret = num1 / num2;
break;
}
stack.push(ret);
} else {
// 是数字就入栈
stack.push(Integer.valueOf(s));
}
}
return stack.pop();
}
}