实习面试试题及答案
一面
自我介绍,可实习时间,在校情况;
略
手撕算法:
下面两题为经典算法题,在剑指Offer和LeetCode中均有原题
青蛙跳台阶,一次可以跳1个台阶或者2个台阶,问跳完N阶台阶总共有几种跳法?(递归和非递归)
详细解答请参考:剑指offer第二版-10_2青蛙跳台阶
/**
* 递归实现
*
* @param n
* @return
*/
public int jumpFloor(int n) {
if (n == 1 || n == 2) {
return n;
} else {
return jumpFloor(n - 1) + jumpFloor(n - 2);
}
}
/**
* 非递归实现
*/
public int jumpFloor1(int n) {
int preOne = 1;
int preTwo = 2;
int result = 0;
if (n == 1 || n == 2) {
return n;
} else {
for (int i = 3; i <= n; i++) {
result = preOne + preTwo;
preOne = preTwo;
preTwo = result;
}
return result;
}
}
按照顺时针的顺序,逐层遍历并打印N阶方阵;
private static void printMatrix(int[][] data) {
if (data == null) {
return;
}
if (data.length == 0 || data[0].length <= 0) {
return;
}
int rowLen = data.length;
int colLen = data[0].length;
int row = 0, col = 0, round = 0;
while (rowLen - 2 * row > 1 && colLen - 2 * col > 1) {
for (; col < colLen - round; col++) {
System.out.print(data[row][col]);
System.out.print("\t");
}
for (col = col - 1, row = row + 1; row < rowLen - round; row++) {
System.out.print(data[row][col]);
System.out.print("\t");
}
for (row = row - 1, col = col - 1; col > round; col--) {
System.out.print(data[row][col]);
System.out.print("\t");
}
for (; row > round; row--) {
System.out.print(data[row][col]);
System.out.print("\t");
}
row++;
col++;
round++;
}
// 如果行数与列数中较小的那个是偶数,则能组成完整的环,在while中就完成了遍历
if (rowLen - 2 * row == 0 || colLen - 2 * col == 0) {
System.out.println();
}
// 如果行数与列数中较小的是行数,且是奇数,则还需补充访问一行
if (rowLen - 2 * row == 1) {
for (; col < colLen - round; col++) {
System.out.print(data[row][col]);
System.out.print("\t");
}
System.out.println();
}
// 如果行数与列数中较小的是列数,且是奇数,则还需补充访问一列
if (colLen - 2 * col == 1) {
for (; row < rowLen - round; row++) {
System.out.print(data[row][col]);
System.out.print("\t");
}
System.out.println();
}
}
TCP/IP协议;
TCP 的特性
- TCP 提供一种面向连接的、可靠的字节流服务
- 在一个 TCP 连接中,仅有两方进行彼此通信。广播和多播不能用于 TCP
- TCP 使用校验和,确认和重传机制来保证可靠传输
- TCP 给数据分节进行排序,并使用累积确认保证数据的顺序不变和非重复
- TCP 使用滑动窗口机制来实现流量控制,通过动态改变窗口的大小进行拥塞控制
IP 协议
IP 协议位于 TCP/IP 协议的第三层——网络层。与传输层协议相比,网络层的责任是提供点到点(hop by hop)的服务,而传输层(TCP/UDP)则提供端到端(end to end)的服务
TCP三次握手,四次挥手;
所谓三次握手(Three-way Handshake),是指建立一个 TCP 连接时,需要客户端和服务器总共发送3个包。
三次握手的目的是连接服务器指定端口,建立 TCP 连接,并同步连接双方的***和确认号,交换 TCP 窗口大小信息。在 socket 编程中,客户端执行 connect() 时。将触发三次握手。
-
第一次握手(SYN=1, seq=x):
客户端发送一个 TCP 的 SYN 标志位置1的包,指明客户端打算连接的服务器的端口,以及初始序号 X,保存在包头的***(Sequence Number)字段里。
发送完毕后,客户端进入 SYN_SEND 状态。
-
第二次握手(SYN=1, ACK=1, seq=y, ACKnum=x+1):
服务器发回确认包(ACK)应答。即 SYN 标志位和 ACK 标志位均为1。服务器端选择自己 ISN ***,放到 Seq 域里,同时将确认序号(Acknowledgement Number)设置为客户的 ISN 加1,即X+1。 发送完毕后,服务器端进入 SYN_RCVD 状态。
-
第三次握手(ACK=1,ACKnum=y+1)
客户端再次发送确认包(ACK),SYN 标志位为0,ACK 标志位为1,并且把服务器发来 ACK 的序号字段+1,放在确定字段中发送给对方,并且在数据段放写ISN的+1
发送完毕后,客户端进入 ESTABLISHED 状态,当服务器端接收到这个包时,也进入 ESTABLISHED 状态,TCP 握手结束。
三次握手的过程的示意图如下:
TCP 的连接的拆除需要发送四个包,因此称为四次挥手(Four-way handshake),也叫做改进的三次握手。客户端或服务器均可主动发起挥手动作,在 socket 编程中,任何一方执行 close() 操作即可产生挥手操作。
-
第一次挥手(FIN=1,seq=x)
假设客户端想要关闭连接,客户端发送一个 FIN 标志位置为1的包,表示自己已经没有数据可以发送了,但是仍然可以接受数据。
发送完毕后,客户端进入 FIN_WAIT_1 状态。
-
第二次挥手(ACK=1,ACKnum=x+1)
服务器端确认客户端的 FIN 包,发送一个确认包,表明自己接受到了客户端关闭连接的请求,但还没有准备好关闭连接。
发送完毕后,服务器端进入 CLOSE_WAIT 状态,客户端接收到这个确认包之后,进入 FIN_WAIT_2 状态,等待服务器端关闭连接。
-
第三次挥手(FIN=1,seq=y)
服务器端准备好关闭连接时,向客户端发送结束连接请求,FIN 置为1。
发送完毕后,服务器端进入 LAST_ACK 状态,等待来自客户端的最后一个ACK。
-
第四次挥手(ACK=1,ACKnum=y+1)
客户端接收到来自服务器端的关闭请求,发送一个确认包,并进入 TIME_WAIT状态,等待可能出现的要求重传的 ACK 包。
服务器端接收到这个确认包之后,关闭连接,进入 CLOSED 状态。
客户端等待了某个固定时间(两个最大段生命周期,2MSL,2 Maximum Segment Lifetime)之后,没有收到服务器端的 ACK ,认为服务器端已经正常关闭连接,于是自己也关闭连接,进入 CLOSED 状态。
四次挥手的示意图如下:
最近在看什么书或者在学什么知识;
略
根据最近所学的提几个问题;
略
你有什么想问的;
略
二面
自我介绍;
略
项目;
略
在校情况,可实习时间;
略
Https和Http的区别;
HTTP和HTTPS的基本概念
-
HTTP:是互联网上应用最为广泛的一种网络协议,是一个客户端和服务器端请求和应答的标准(TCP),用于从WWW服务器传输超文本到本地浏览器的传输协议,它可以使浏览器更加高效,使网络传输减少。
-
HTTPS:是以安全为目标的HTTP通道,简单讲是HTTP的安全版,即HTTP下加入SSL层,HTTPS的安全基础是SSL,因此加密的详细内容就需要SSL。
-
HTTPS协议的主要作用可以分为两种:一种是建立一个信息安全通道,来保证数据传输的安全;另一种就是确认网站的真实性。
HTTP与HTTPS有什么区别?
HTTP协议传输的数据都是未加密的,也就是明文的,因此使用HTTP协议传输隐私信息非常不安全,为了保证这些隐私数据能加密传输,于是网景公司设计了SSL(Secure Sockets Layer)协议用于对HTTP协议传输的数据进行加密,从而就诞生了HTTPS。
HTTPS加密、加密、及验证过程,如下图所示:
简单来说,HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,要比http协议安全。
HTTPS和HTTP的区别主要如下:
- https协议需要到ca申请证书,一般免费证书较少,因而需要一定费用。
- http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议。
- http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
- http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。
SSL的加密方式;
SSL采用一种叫做公开**加密(Public-Key cryptography)的加密处理方式
公开**加密方式很好地解决了共享密码加密的困难。公开**加密使用一对非对称的**。一把叫做私有**(private key),另一把叫做public key 。顾名思义,私有**不能让任何人知道,而公有**则可以随意发布 ,任何人都可以获得。使用公有钥匙加密方式,发送密文的一方使用对方的公有**进行加密处理,对方收到被加密的信息后,在使用自己私有的钥匙进行解密,这种方式,不需要发送用来解密的私钥,也不必担心**被攻击者窃听而盗走。另外,要想根据密文和公开**,恢复到信息原文是异常困难的,因为解密加密工程就是对离散对数进行求值,这并非轻而易举的就能办到的,退一步讲,如果能对一个非常大的整数做到快速地因式分解,那么****还是存在希望的。但是目前的技术来看是不太现实的。
其他加密方式;
比如HTTPS采用的混合加密方式
https采用共享**机密和公开**加密两者并用的混合加密机制,若**能够实现安全的交换,那么有可能会考虑仅适用公开**加密通信。但是公开**加密与共享**加密相比,其处理速度要慢。所以充分利用两者的优势,将方法组合起来用来通信,在交换密码环节使用公开**加密方式,之后建立通信交换报文阶段则使用共享**方式。
CA证书的作用;
它的作用就是提供证书(即服务器证书,由域名、公司信息、***和签名信息组成)加强服务端和客户端之间信息交互的安全性,以及证书运维相关服务。
TCP为什么是三次握手,四次挥手?
TCP作为一种可靠传输控制协议,其核心思想:既要保证数据可靠传输,又要提高传输的效率,而用三次恰恰可以满足以上两方面的需求!
而TCP是全双工通信的
- 第一次挥手 因此当主动方发送断开连接的请求(即FIN报文)给被动方时,仅仅代表主动方不会再发送数据报文了,但主动方仍可以接收数据报文。
- 第二次挥手 被动方此时有可能还有相应的数据报文需要发送,因此需要先发送ACK报文,告知主动方“我知道你想断开连接的请求了”。这样主动方便不会因为没有收到应答而继续发送断开连接的请求(即FIN报文)。
- 第三次挥手 被动方在处理完数据报文后,便发送给主动方FIN报文;这样可以保证数据通信正常可靠地完成。发送完FIN报文后,被动方进入LAST_ACK阶段(超时等待)。 (4)第四挥手 如果主动方及时发送ACK报文进行连接中断的确认,这时被动方就直接释放连接,进入可用状态。
TCP滑窗机制的作用;
滑动窗口机制主要用于TCP 的流量控制。流量控制(flow control)就是让发送方的发送速率不要太快,既要让接收方来得及接收,也不要使网络发生拥塞。
滑动窗口协议原理:对所有数据帧按顺序赋予编号,发送方在发送过程中始终保持着一个发送窗口,只有落在发送窗口内的帧才允许被发送;同时接收方也维持着一个接收窗口,只有落在接收窗口内的帧才允许接收。
开始的时候窗口比较小,然后开始增长直到有错误发生时为止;-窗口的滑动依赖于网络性能。通过调整发送方窗口和接收方窗口的大小可以实现流量控制,就象通过阀门控制水流速度一样。也就是说 TCP协议通过滑动窗口来实现流量控制和差错控制以至于实现可靠传输。
解释:TCP滑动窗口技术通过动态改变窗口大小来调节两台主机间数据传输。每个TCP/IP主机支持全双工数据传输,因此TCP有两个滑动窗口:一个用于接收数据,另一个用于发送数据。TCP使用肯定确认技术,其确认号指的是下一个所期待的字节。
假定发送方设备以每一次三个数据包的方式发送数据,也就是说,窗口大小为3。发送方发送***为1、2、3的三个数据包,接收方设备成功接收数据包,用***4确认。发送方设备收到确认,继续以窗口大小3发送数据。当接收方设备要求降低或者增大网络流量时,可以对窗口大小进行减小或者增加,本例降低窗口大小为2,每一次发送两个数据包。当接收方设备要求窗口大小为0,表明接收方已经接收了全部数据,或者接收方应用程序没有时间读取数据,要求暂停发送。发送方接收到携带窗口号为0的确认,停止这一方向的数据传输。
滑动窗口机制为端到端设备间的数据传输提供了可靠的流量控制机制。然而,它只能在源端设备和目的端设备起作用,当网络中间设备(例如路由器等)发生拥塞时,滑动窗口机制将不起作用。
JVM虚拟机GC;
后续补上
在编程中应该如何避免出现的相互循环引用,而造成的内存泄漏;
循环引用:指的是多个对象相互引用时,使得引用形成一个环形,导致外部无法真正是否掉这块环形内存。其实有点类似死锁。
比如如我有一个people类,在有一个car,people有一个car的属性,car类中又people的属性,此时产生循环引用问题
手撕算法:
单向链表,从倒数第K节点开始反转链表,输出整个链表;
乱序不重复数组,输出其中任意两个相加和为固定值M的集合;
/**
* 输入一个数组和一个数字,在数组中查找两个数,使得它们的和正好是输入的那个数字。
* 要求时间复杂度是O(N)。如果有多对数字的和等于输入的数字,输出任意一对即可。
* 例如输入数组1、2、4、7、11、15和数字15。由于4+11=15,因此输出4和11。
* <p>
* 思路一:直接穷举,从数组中任意选取两个数,判定它们的和是否为输入的那个数字。此举复杂度为O(N^2)
* <p>
* 优化:采用二分查找。可以将第二个数字的查找时间复杂度用排序算法优化到O(log N),
* 这样对于a[i],都要花O(logN)的时间去查找相对应的sum-a[i]是否在数组中,
* 总的时间复杂度已降为O(N log N),且空间复杂度为O(1)。
* <p>
* 思路二:如果a[i]在数组中,那么sum-a[i](a[k])也必然在这个数组中,可以直接寻找a[k]是否在这个数组中,时间复杂度为O(n)
* 优化:采用hash表对数组进行预处理,可用O(1)的时间复杂度将结果表示出来。但需要经过O(N)时间的预处理,并且用O(N)的空间构造hash表
* <p>
* 思路三:相对数组进行排序,时间复杂度为O(N log N),然后用两个指针i,j,各自指向数组的首尾两端,
* 令i=0,j=n-1,然后i++,j--,逐次判断a[i]+a[j]?=sum
* 如果某一刻a[i]+a[j] > sum,则要想办法让sum的值减小,所以此刻i不动,j--
* 如果某一刻a[i]+a[j] < sum,则要想办法让sum的值增大,所以此刻i++,j不动
* 所以,数组无序的时候,时间复杂度最终为O(N log N + N)=O(N log N)。
* 如果原数组是有序的,则不需要事先的排序,直接用两指针分别从头和尾向中间扫描,O(N)搞定,且空间复杂度还是O(1)。
*
* @author VicterTian
* @version V1.0
* @Date 2019/2/16
*/
public class FindTwoNumberForSum {
/**
* 思路二
*/
private static void twoSum1(int[] data, int sum) {
int count = 0;
Map<Integer, Integer> hashMap = new HashMap<>(data.length);
for (int i : data) {
hashMap.put(i, i);
}
for (Iterator<Map.Entry<Integer, Integer>> it = hashMap.entrySet().iterator(); it.hasNext(); ) {
Map.Entry<Integer, Integer> i = it.next();
int num = sum - i.getKey();
if (hashMap.containsValue(num)) {
it.remove();
System.out.println("twoSum1 = " + i.getKey() + "\t" + num);
count++;
}
}
if (count == 0) {
System.out.println("没有满足条件的值");
}
}
/**
* 思路三
*/
private static void twoSum2(int[] data, int sum) {
Arrays.sort(data);
int begin = 0;
int end = data.length - 1;
int count = 0;
while (begin < end) {
int currSum = data[begin] + data[end];
if (currSum == sum) {
System.out.println("twoSum2 = " + data[begin] + "\t" + data[end]);
count++;
begin++;
end--;
} else {
if (currSum < sum) {
begin++;
} else {
end--;
}
}
}
if (count == 0) {
System.out.println("没有满足条件的值");
}
}
public static void main(String[] args) {
int[] data = {-1, 1, -2, 7, 11, 15, 2, -4,};
twoSum1(data, -3);
twoSum2(data, 0);
}
}
上题中的解,时间复杂度是多少?还有更优的解法吗?
你有什么想问的;
略
上一篇: CentOS7 上学习使用docker
下一篇: Java基础