CVTE一面(C++)

2018.8.2

1.讲项目

2.二叉树叶结点和度的关系,记不到的话,推导一遍

结点的度:结点拥有的子树的数目。

在任意一棵二叉树中,若终端结点的个数为n0,度为2的结点数为n2,则n0=n2+1

证明:因为二叉树中所有结点的度数均不大于2,所以结点总数(记为n)="0度结点数(n0)" + "1度结点数(n1)" + "2度结点数(n2)"。由此,得到等式一。
         (等式一) n=n0+n1+n2
      另一方面,0度结点没有孩子,1度结点有一个孩子,2度结点有两个孩子,故二叉树中孩子结点总数是:n1+2n2。此外,只有根不是任何结点的孩子。故二叉树中的结点总数又可表示为等式二。
         (等式二) n=n1+2n2+1
        由(等式一)和(等式二)计算得到:n0=n2+1。

3.二叉树最远的两个节点距离怎么求

《编程之美》上有这个问题。

两个结点的最大距离分为两种情况:

A.经过根结点,从左子树最深的那个结点,到右子树最深的结点的距离

B.不经过根结点。最远的距离在左子树上,或者最远的距离是在右子树上。

所以求出上面的三个的MAX就是结果

#include<iostream>
#include<algorithm>
using namespace std;
 
struct TreeNode{
	TreeNode* left;
	TreeNode* right;
};
 
struct RESULT{	
	int MaxDistance;//以它为根,节点的之间的最大距离
	int MaxDepth;//当前节点的最大深度
};
 
RESULT GetMaxDistance(TreeNode* root)
{
	if(root == nullptr)
	{
		RESULT empty{ 0, -1 };//-1是为了返回之后,加回0
		return empty;
	}
	RESULT l = GetMaxDistance(root->left);
	RESULT r = GetMaxDistance(root->right);
 
	RESULT result;
	result.MaxDepth = max(l.MaxDepth+1, r.MaxDepth+1);
	result.MaxDistance = max(max(l.MaxDistance, r.MaxDistance), l.MaxDepth + r.MaxDepth + 2);
 
	return result;
}

4.手写代码。将字符串转换为整数

如下是一种比较完整的写法。当时写的太慢了,没时间把各种情况写进去。

enum Status{Valid,Invalid};
int g_Status = Valid;//用一个全局变量来标记是不是遇到了非法输入

long long StrToIntCore(const char* digit, bool minus)
{
	long long num = 0;
	while(*digit != '\0')
	{
		if(*digit >= '0'&&*digit <= '9')
		{
			int flag = minus ? -1 : 1;
			num = num * 10 + flag*(*digit - '0');
			if((!minus && num > 0x7FFFFFFF) || (minus&&num < (signed int)0x80000000))//判断有没有超过范围
			{
				num = 0;
				break;
			}
			digit++;
		}
		else//字符串里有不合法的字符
		{
			num = 0;
			break;
		}
	}
	//正常结束
	if(*digit == '\0')
	{
		g_Status = Valid;
	}
	return num;
}


int StrToInt(const char* str)
{
	g_Status = Invalid;
	long long num = 0;
	if(str != nullptr && *str != '\0')
	{
		bool minus = false;
		if(*str == '+')
			str++;
		else if(*str == '-')
		{
			minus = true;
			str++;
		}
		if(*str != '\0')
		{
			num = StrToIntCore(str, minus);
		}
	}
	return (int)num;
}

5.讲讲多态

当时说的不好,很混乱,不知道假如按照如下来回答面试官会满意不。

多态(Polymorphisn)

多态性是允许你将父对象设置成为和一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。简单的说:允许将子类类型的指针赋值给父类类型的指针(一个接口,多种方法)。
C++ 支持两种多态性:编译时多态性,运行时多态性。
a、编译时多态性(静态多态):通过重载函数实现
b、运行时多态性(动态多态):通过虚函数实现。

多态的作用

那么多态的作用是什么呢,封装可以使得代码模块化,继承可以扩展已存在的代码,他们的目的都是为了代码重用。而多态的目的则是为了接口重用。也就是说,不论传递过来的究竟是那个类的对象,函数都能够通过同一个接口调用到适应各自对象的实现方法。

多态的用法

运行时多态:

最常见的用法就是声明基类的指针,利用该指针指向任意一个子类对象,调用相应的虚函数,可以根据指向的子类的不同而实现不同的方法。如果没有使用虚函数的话,则利用基类指针调用相应的函数的时候,将总被限制在基类函数本身,而无法调用到子类中被重写过的函数。因为没有多态性,函数调用的地址将是一定的,而固定的地址将始终调用到同一个函数,这就无法实现一个接口,多种方法的目的了。

非运行时多态:

通过函数重载实现

简而言之:

  1. 有virtual才可能发生动态多态现象
  2. (无virtual)调用就按原类型调用

6.各种排序算法的稳定性,快排的复杂度

7.讲讲桶排序。

讲的很混乱,表达的不够清楚。如下的回答不知道怎么样。

桶排序是在已经数据的范围的条件下,创建若干个桶,根据相应的比较规则将待排数据落入各个对应的桶中,最后扫描 桶 来实现排序。

桶排序需要的附加条件:数据的范围已知。

比如,输入整数数据 A(1)  A(2)......A(n-1)  A(n),它们都是由小于M的整数组成,此时,就可以创建 M 个桶进行桶排序了

再比如,固定长度的字母字符串数据: S(1) S(2) .....S(n-1) S(n),任意的字符串S(i),都由26个小写字母组成,在桶排序过程中,就可以创建26个桶来保存这些字符串

8.tcp的稳定性是如何保证的

1、应用数据被分割成TCP认为最适合发送的数据块。这和UDP完全不同,应用程序产生的数据报长度将保持不变。                                                                (将数据截断为合理的长度)

2、当TCP发出一个段后,它启动一个定时器,等待目的端确认收到这个报文段。如果不能及时收到一个确认,将重发这个报文段。                        (超时重发)

3、当TCP收到发自TCP连接另一端的数据,它将发送一个确认。这个确认不是立即发送,通常将推迟几分之一秒 。                                                                 (对于收到的请求,给出确认响应) (之所以推迟,可能是要对包做完整校验)

4、 TCP将保持它首部和数据的检验和。这是一个端到端的检验和,目的是检测数据在传输过程中的任何变化。如果收到段的检验和有差错,TCP将丢弃这个报文段和不确认收到此报文段。 (校验出包有错,丢弃报文段,不给出响应,TCP发送数据端,超时时会重发数据)

5、既然TCP报文段作为IP数据报来传输,而IP数据报的到达可能会失序,因此TCP报文段的到达也可能会失序。如果必要,TCP将对收到的数据进行重新排序,将收到的数据以正确的顺序交给应用层。  (对失序数据进行重新排序,然后才交给应用层)

6、既然IP数据报会发生重复,TCP的接收端必须丢弃重复的数据。(对于重复数据,能够丢弃重复数据)

7、TCP还能提供流量控制。TCP连接的每一方都有固定大小的缓冲空间。TCP的接收端只允许另一端发送接收端缓冲区所能接纳的数据。这将防止较快主机致使较慢主机的缓冲区溢出。(TCP可以进行流量控制,防止较快主机致使较慢主机的缓冲区溢出)TCP使用的流量控制协议是可变大小的滑动窗口协议。

9.讲讲超时重传

原理是在发送某一个数据以后就开启一个计时器,在一定时间内如果没有得到发送的数据报的ACK报文,那么就重新发送数据,直到发送成功为止。 
  影响超时重传机制协议效率的一个关键参数是重传超时时间RTO,Retransmission TimeOut)。RTO的值被设置过大过小都会对协议造成不利影响。 
  (1)RTO设长了,重发就慢,没有效率,性能差。 
  (2)RTO设短了,重发的就快,会增加网络拥塞,导致更多的超时,更多的超时导致更多的重发。 
  连接往返时间(RTT,Round Trip Time),指发送端从发送TCP包开始到接收它的立即响应所消耗的时间。

10.长时间收不到数据会怎样。

别人博客写的:

第一次发送后所设置的超时时间实际上为1.5秒,此后该时间在每次重传时增加一倍,一直到64秒,采用的是指数退避算法。一共重传12次,大约9分钟才放弃重传,该时间在目前的TCP实现中是不可变的,Solaris2.2允许管理者改变这个时间,tcp_ip_abort_interval变量。且其默认值为两分钟,而不是最常用的9分钟。

11.讲讲滑动窗口

TCP的滑动窗口主要有两个作用,一是提供TCP的可靠性,二是提供TCP的流控特性。同时滑动窗口机制还体现了TCP面向字节流的设计思路。

TCP滑动窗口技术通过动态改变窗口大小来调节两台主机间数据传输。每个TCP/IP主机支持全双工数据传输,因此TCP有两个滑动窗口:一个用于接收数据,另一个用于发送数据。TCP使用肯定确认技术,其确认号指的是下一个所期待的字节。

我们看看滑动窗口是如何工作的。我们看下面几张图。 

 

 
首先是第一次发送数据这个时候的窗口大小是根据链路带宽的大小来决定的。我们假设这个时候窗口的大小是3。这个时候接受方收到数据以后会对数据进行确认告诉发送方我下次希望收到的是数据是多少。这里我们看到接收方发送的ACK=3(这是发送方发送序列2的回答确认,下一次接收方期望接收到的是3序列信号)。这个时候发送方收到这个数据以后就知道我第一次发送的3个数据对方只收到了2个。就知道第3个数据对方没有收到。下次在发送的时候就从第3个数据开始发。这个时候窗口大小就变成了2 。 
 

这个时候发送方发送2个数据。 

 
看到接收方发送的ACK是5就表示他下一次希望收到的数据是5,发送方就知道我刚才发送的2个数据对方收了这个时候开始发送第5个数据。 
这就是滑动窗口的工作机制,当链路变好了或者变差了这个窗口还会发生变化,并不是第一次协商好了以后就永远不变了。                 
 

对于TCP会话的发送方,任何时候在其发送缓存内的数据都可以分为4类,“已经发送并得到对端ACK的”,“已经发送但还未收到对端ACK的”,“未发送但对端允许发送的”,“未发送且对端不允许发送”。“已经发送但还未收到对端ACK的”和“未发送但对端允许发送的”这两部分数据称之为发送窗口(中间两部分)。

当收到接收方新的ACK对于发送窗口中后续字节的确认是,窗口滑动,滑动原理如下图。

当收到ACK=36时窗口滑动。

2)对于TCP的接收方,在某一时刻在它的接收缓存内存在3种。“已接收”,“未接收准备接收”,“未接收并未准备接收”(由于ACK直接由TCP协议栈回复,默认无应用延迟,不存在“已接收未回复ACK”)。其中“未接收准备接收”称之为接收窗口。

12.窗口值为0

ACK号增大,窗口减小,左右边界距离减小。当左右边界相等时,称之为零窗口。此时发送端不能再发送新数据。这种情况下,TCP发送端开始探测对方窗口,伺机增大提供窗口。

TCP是通过接收端的通告窗口来实现流量控制的。通告窗口指示了接收端可接收的数据量。当窗口值变为0时,可以有效阻止发送端继续发送,直到窗口大小恢复为非零值。当接收端重新获得可用空间时,会给发送端传输一个窗口更新,告知其可继续发送数据。这样的窗口更新通常都不包含数据(纯ACK),不能保证传输的可靠性。

如果一个包含窗口更新的ACK丢失,通信双方就会一直处于等待状态:接收方等待接收数据(已将窗口设为非零值),发送方等待收到窗口更新告知其可继续发送。为防止这种死锁,发送端会采用一个持续计数器间歇性查询接收端,看其窗口是否已经增长。持续计数器会触发窗口探测的传输,强制要求接收端返回ACK(包含了窗口大小)。主机需求RFC建议在一个RTO之后发送第一个窗口探测,随后以指数时间间隔发送。

13.time_wait=0?

我听到这个时很懵逼,只记得TIME_WAIT=2MSL。但不晓得怎么回答等于0,都没懂这个问题是干嘛,现在想来,估计是讲一下TIME_WAIT不能等于0,而等于2MSL的原因?.。。

time_wait状态是四次挥手中server向client发送FIN终止连接后进入的状态。是客户端的状态

1、可靠的终止TCP的连接。 
如果用来确认服务器结束的报文段(也就是最后一条报文段)丢失,那么服务器将重发结束报文段。因此客户端需要停留在某个状态来处理重复收到结束报文段的情况(即向服务器发送确认报文段),否则,客户端将以复位报文段来回应服务器,服务器则认为这是一个错误就会一直等待客户端来给它回应,这就会导致资源的浪费。 
2、保证让迟来的TCP报文段有足够的时间被识别并被丢弃。 
我们知道在linux系统上一个TCP端口不能被多次打开(两次以上),当一个TCP连接处于TIME_WAIT时,我们将无法立即使用该连接所占的端口来建立一个新的连接。如果不存在TIME_WAIT状态应用程序建立一个和刚关闭的连接相似的连接(相似指的是它们有同的ip和端口号),这个新的和原来相似的连接就有可能收到属于原来连接的TCP报文段,这是不应该发生的。

之后的问题就没什么可说的了。

什么怎么学习,看什么书,了不了解开源项目。

人生第一次面试,反正就是很害怕。现在想来,也没什么,大不了就不知道- -

猜你喜欢

转载自blog.csdn.net/qq_22080999/article/details/81394388