今日头条C++后台开发实习面试总结

来源:互联网 发布:漫画软件那个好 编辑:程序博客网 时间:2024/05/13 07:41

一. 旋转数组中寻找某个target,leetcode原题。


二. 一个数组建立堆。

堆排序中,最初的步骤就是建立一个堆。之前在一些公司的笔试题上面见到一些与建堆过程相关的题目,因为当时对建堆过程有个误解,所以经常选错。之前一直以为是在完全二叉树中依次插入序列中的元素,每插入一个元素,就调用siftup操作;而实际的建堆操作是序列中元素首先就全部填入一个完全二叉树,然后从第一个非终端节点开始,调用siftdown操作,依次调整。

以下是一篇关于建堆过程的文章,转载自:http://www.cnblogs.com/zabery/archive/2011/07/26/2117103.html

堆排序过程

堆分为大根堆和小根堆,是完全二叉树。大根堆的要求是每个节点的值都不大于其父节点的值,即A[PARENT[i]] >= A[i]。在数组的非降序排序中,需要使用的就是大根堆,因为根据大根堆的要求可知,最大的值一定在堆顶。

既然是堆排序,自然需要先建立一个堆,而建堆的核心内容是调整堆,使二叉树满足堆的定义(每个节点的值都不大于其父节点的值)。调堆的过程应该从最后一个非叶子节点开始,假设有数组A = {1, 3, 4, 5, 7, 2, 6, 8, 0}。那么调堆的过程如下图,数组下标从0开始,A[3] = 5开始。分别与左孩子和右孩子比较大小,如果A[3]最大,则不用调整,否则和孩子中的值最大的一个交换位置,在图1中是A[7] > A[3] > A[8],所以A[3]与A[7]对换,从图1.1转到图1.2。

http://images.cnblogs.com/cnblogs_com/zabery/201107/201107261321543165.png


以上便是建堆的完整过程,主要总结有以下几点需要注意:

1.首先将所有元素按照初始顺序填充到一个完全二叉树中

2.从“最后一个非终端节点”开始,调用siftdown方法,调整堆的结构,直到根节点为止


三. TCP/IP close_wait状态和time_wait状态。

TIME_WAIT 表示主动关闭,CLOSE_WAIT 表示被动关闭。

CLOSE_WAIT状态的生成原因
首先我们知道,如果我们的服务器程序APACHE处于CLOSE_WAIT状态的话,说明套接字是被动关闭的!

因为如果是CLIENT端主动断掉当前连接的话,那么双方关闭这个TCP连接共需要四个packet:

      Client --->  FIN  --->  Server 

      Client <---  ACK  <---  Server 

 这时候Client端处于FIN_WAIT_2状态;而Server 程序处于CLOSE_WAIT状态。

      Client <---  FIN  <---  Server 

这时Server 发送FIN给Client,Server 就置为LAST_ACK状态。

       Client --->  ACK  --->  Server 

Client回应了ACK,那么Server 的套接字才会真正置为CLOSED状态。

Server 程序处于CLOSE_WAIT状态,而不是LAST_ACK状态,说明还没有发FIN给Client,那么可能是在关闭连接之前还有许多数据要发送或者其他事要做,导致没有发这个FIN packet。

客户端主动关闭时,发出FIN包,收到服务器的ACK,客户端停留在FIN_WAIT2状态。而服务端收到FIN,发出ACK后,停留在COLSE_WAIT状态。
    这个CLOSE_WAIT状态非常讨厌,它持续的时间非常长,服务器端如果积攒大量的COLSE_WAIT状态的socket,有可能将服务器资源(套接字描述符耗尽)耗尽,进而无法提供服务。
    那么,服务器上是怎么产生大量的失去控制的COLSE_WAIT状态的socket呢?我们来追踪一下。
    一个很浅显的原因是,服务器没有继续发FIN包给客户端。
    服务器为什么不发FIN,可能是业务实现上的需要,现在不是发送FIN的时机,因为服务器还有数据要发往客户端,发送完了自然就要通过系统调用发FIN了,这个场景并不是上面我们提到的持续的COLSE_WAIT状态,这个在受控范围之内。
    那么究竟是什么原因呢,咱们引入两个系统调用close(sockfd)和shutdown(sockfd,how)接着往下分析。
    在这儿,需要明确的一个概念---- 一个进程打开一个socket,然后此进程再派生子进程的时候,此socket的sockfd会被继承。socket是系统级的对象,现在的结果是,此socket被两个进程打开,此socket的引用计数会变成2。
 
    继续说上述两个系统调用对socket的关闭情况。
    调用close(sockfd)时,内核检查此fd对应的socket上的引用计数。如果引用计数大于1,那么将这个引用计数减1,然后返回。如果引用计数等于1,那么内核会真正通过发FIN来关闭TCP连接。
    调用shutdown(sockfd,SHUT_RDWR)时,内核不会检查此fd对应的socket上的引用计数,直接通过发FIN来关闭TCP连接。
 
     现在应该真相大白了,可能是服务器的实现有点问题,父进程打开了socket,然后用派生子进程来处理业务,父进程继续对网络请求进行监听,永远不会终止。客户端发FIN过来的时候,处理业务的子进程的read返回0,子进程发现对端已经关闭了,直接调用close()对本端进行关闭。实际上,仅仅使socket的引用计数减1,socket并没关闭。从而导致系统中又多了一个CLOSE_WAIT的socket。。。
 
如何避免这样的情况发生?
子进程的关闭处理应该是这样的:
shutdown(sockfd, SHUT_RDWR);
close(sockfd);
这样处理,服务器的FIN会被发出,socket进入LAST_ACK状态,等待最后的ACK到来,就能进入初始状态CLOSED。


四. nginx的进程模型。

五. redis的内存分配。

六.MySQL  引擎 Innodb 聚簇索引与非聚簇索引,B+树与B树的区别。





0 0