美团测试开发面经

文章介绍了数据结构的基础知识,包括链表、栈、队列和二叉树的特性与应用场景。此外,讨论了TCP/IP的三次握手和四次挥手过程,以及HTTP中的GET和POST请求的区别。同时,提到了TCP和UDP协议的差异,以及操作系统中进程和线程的概念。最后,涉及了数据库操作和并发控制的乐观锁与悲观锁策略。
摘要由CSDN通过智能技术生成

数据结构

(1)常用的数据结构(链表、栈、队列、二叉树)

链表(Linked List):链表是一种基本的数据结构,它由一系列节点组成,每个节点包含数据和指向下一个节点的指针。链表相比于数组具有动态性,可以根据需要动态分配内存空间,但是随机访问元素的效率较低。常见的链表结构包括单向链表、双向链表、循环链表等。

栈(Stack):栈是一种后进先出(LIFO)的数据结构,只允许在一端(栈顶)进行插入、删除操作。栈可以用数组或链表实现,常见的操作包括入栈、出栈、查看栈顶元素等。

队列(Queue):队列是一种先进先出(FIFO)的数据结构,只允许在队列尾部插入元素,在队列头部删除元素。队列也可以用数组或链表实现,常见的操作包括入队、出队、查看队首元素等。

二叉树(Binary Tree):二叉树是一种由节点组成的层次结构,每个节点最多只有两个子节点,分别称为左子树和右子树。二叉树可以为空树,如果有一颗非空的二叉树,根节点只有它自己作为一个节点。常见的二叉树结构包括满二叉树、完全二叉树、二叉搜索树等。

这些数据结构都有着不同的应用场景和特点,例如链表可以用于实现链式存储结构;栈可以用于括号匹配、表达式求值等;队列可以用于模拟进程和线程的调度等;二叉树可以用于搜索、排序等。在实际的编程过程中,根据不同的需求,我们需要选择适合的数据结构来实现对应的算法。

(2)数组和链表的区别

数组和链表是两种基本的数据结构,它们在存储和访问数据时具有不同的优势和限制。下面是它们的区别:

存储方式:数组中的元素在物理空间中是连续存储的,每个元素占用固定的空间大小,而链表中的元素可以离散存储,每个元素只需要保存指向下一个元素的指针即可。

大小限制:数组一旦创建后,大小一般不可改变,即使需要扩容或缩小,也需要重新分配内存,因此数组具有一定的大小限制。而链表的大小没有限制,可以动态修改,只要有足够的内存空间即可。

插入和删除操作:数组中插入和删除元素比较麻烦,在插入或删除元素后需要移动其他所有元素,时间复杂度为O(n)。而链表插入和删除元素非常方便,只需要修改指针即可,时间复杂度为O(1)。

访问操作:数组可以通过下标直接访问元素,时间复杂度为O(1),而链表需要遍历整个链表才能访问指定位置的元素,时间复杂度为O(n)。

内存空间:相同元素数量的情况下,链表需要更多的内存空间来维护指针,而数组不需要。

综上所述,数组和链表各具有优点和缺点,应根据具体情况选择合适的数据结构。数组在访问和更新元素时效率高,适用于元素数量不变、随机访问场景;链表在插入和删除元素时效率高,适用于元素数量变化、顺序访问场景。

(1)TCP/IP四层模型(链路层、网际层、运输层、应用层,说成五层模型了,不过小姐姐说对了哈哈哈)

(2)三次握手

第一次握手(SYN):客户机发送SYN包,指定一个初始序列号(ISN)J,并进入SYN_SEND状态,等待服务器响应。

第二次握手(SYN+ACK):服务器收到客户机的SYN包,表示客户机要求建立连接,于是服务器回应一个SYN+ACK包,确认收到请求同时指定自己的初始序列号(ISN)K,并进入SYN_REVD状态。

第三次握手(ACK):客户机收到服务器的SYN+ACK包后,进入ESTABLISHED状态,此时向服务器发送一个确认包ACK,确认收到服务器的SYN+ACK包,最后服务器收到ACK包后,也进入ESTABLISHED状态,此时TCP连接已经建立完成。

需要注意的是,这三次握手的目的是为了确保客户端和服务器都能收到对方的确认信息,以保证连接的可靠性。在第一次和第二次握手中,双方仅仅是建立了连接,但是并没有传输数据。只有在第三次握手确认之后,双方之间才可以进行数据的传输。

总之,TCP三次握手是TCP协议中非常重要的步骤,用于建立可靠的TCP连接,同时还可以防止双方因为网络延迟等原因发出重复的连接请求。

四次挥手

TCP/IP协议中,断开连接需要进行四次挥手(TCP four-way handshake),具体过程如下:

第一次挥手(FIN):客户机发送一个FIN包,表示要关闭连接,并进入FIN_WAIT1状态,等待服务器响应。

第二次挥手(ACK):服务器收到客户机的FIN包后,确认收到请求,向客户机发送一个ACK包,并进入CLOSE_WAIT状态。

第三次挥手(FIN):当服务器不再需要连接时,发送一个FIN包,表示要关闭连接,并进入LAST_ACK状态。

第四次挥手(ACK):客户机收到服务器的FIN包后,向服务器发送一个确认ACK包,并进入TIME_WAIT状态,等待2MSL(最长报文段寿命)时间后自动释放连接。

需要注意的是,四次挥手的目的是为了确保双方都知道连接被关闭了。在第一次挥手中,客户端向服务器发送一个FIN包,表示要关闭连接。在第二次挥手中,服务器向客户端发送一个ACK包,表示已经收到关闭请求。在第三次挥手中,服务器向客户端发送一个FIN包,表示服务器要关闭连接。在第四次挥手中,客户端向服务器发送一个ACK包,表示已经收到关闭请求并确认关闭连接。

总之,TCP四次挥手是TCP协议中非常重要的步骤,用于断开TCP连接。通过四次挥手可以确保连接的可靠性,防止连接终止时出现数据丢失或重复传输的问题。

(3)GET和POST的区别

a.get请求一般是去取获取数据;post请求一般是去提交数据。

b.get因参数会放在url中,所以隐私性,安全性较差,请求的数据长度是有限制的,不同的浏览器和服务器不同,一般限制在 2~8K 之间,更常见的是 1k 以内;post请求是没有的长度限制,请求数据是放在body中,安全性较好;

c.get请求刷新服务器或者回退没有影响,post请求回退时会重新提交数据请求。

d.get请求可以被缓存,post请求不会被缓存。

e.get请求会被保存在浏览器历史记录当中,post不会。get请求可以被收藏为书签,因为参数就是url中,但post不能,它的参数不在url中

(5)TCP和UDP的区别:

连接方式:TCP 是面向连接的协议,而 UDP 是无连接的协议。TCP 建立连接后才开始传输数据,而 UDP 不需要连接,直接发送数据包。

可靠性:TCP 是一种可靠的传输控制协议,具有数据完整性保证、数据按序到达和重传机制等特点。而 UDP 不保证数据的完整性、不提供重传机制以及不保证数据按照发送的顺序到达,因此不够可靠。

数据包大小:TCP 的数据包大小是有限制的,最大为64KB,而 UDP 的数据包大小不受限制。这是因为 TCP 所使用的数值字段只有16位,因此数据包大小有限,而 UDP 的数值字段为32位,所以可以传输大量的数据。

速度:UDP传输速度快,因为它不需要像TCP一样建立连接、维护连接状态、处理包序号和重传等机制。而TCP虽然更为可靠,但由于需要进行一系列复杂的连接和数据确认,因此速度会比UDP慢。

综上所述,TCP 和 UDP 都有各自的优点和缺点,并且适用于不同的应用场景。例如,当需要可靠的数据传输时,应该使用 TCP。而在需要快速传输和不需要确保所有数据到达的情况下,可以使用 UDP。常见的应用场景如下: Web浏览器使用TCP协议获取网页内容、视频流、在线游戏等应用场景一般使用UDP。

(5)TCP的应用场景

文件传输。邮件发送和接收,远程访问,网络传输控制

6、操作系统

(1)进程和线程的区别,执行顺序通常是不确定的,但不同线程的执行顺序是由线程调度决定的。由于线程的切换速度较快,因此线程可以实现更高效的并发性。

  1. 内存占用:每个进程都有自己独立的地址空间,因此进程所占用的内存通常比线程多得多。而线程共享进程的地址空间,因此线程在内存占用方面通常比进程更轻量级

7、数据库

(给一个表 插入数据的 实际操作)

领导给了你一个上线的项目 但是相关人员全部离职相关资料也没有了 你该怎么取展开测试

面对领导给你的紧急任务,项目相关的人员已经全部离职,且相关资料也无法取得的情况下,可以按照以下步骤展开测试:

了解需求:与领导沟通项目的需求,并尽量详细了解项目是用于什么用途,实现什么目标。这将有助于你了解项目的功能和约束条件。

分析项目:对项目进行分析,确定项目的类型、目标用户、功能点等基本信息,制定测试计划。同时,对于没有相关资料的情况,可以尝试和别的团队成员交流,加深对项目的了解。

制定测试方案和用例:制定测试方案,计划测试周期、范围和测试内容;制定测试用例,尽可能覆盖各种情况,包括正常情况和异常情况,以确保测试覆盖面尽可能宽广、充分。

执行测试:按照测试用例进行测试,记录测试结果并及时报告测试进展情况。当发现问题时,需要及时反馈并给出详细的问题描述和重现步骤。

维护缺陷跟踪系统:记录缺陷,包括缺陷描述、发现版本、严重程度、优先级等信息,并持续跟踪问题的解决进度。

与领导沟通项目进展情况:在整个测试过程中,要与领导保持沟通,并及时报告项目的进展情况,以使领导对产品质量和测试进展有一个清晰的了解。

综上,尽管项目相关的人员和资料都无法获取,但可以通过完善的测试计划、测试用例、积极的测试执行以及高效的沟通与协作,取得一个尽可能完美的测试结果,并向领导和团队成员表达测试结果。

9、手撕代码:最小栈

class MinStack: def __init__(self): """ Initialize your data structure here. """ self.stack = [] # 正常的栈 self.min_stack = [] # 存放每一个对应位置的最小值 def push(self, x: int) -> None: self.stack.append(x) if not self.min_stack or x <= self.min_stack[-1]: self.min_stack.append(x) def pop(self) -> None: if self.stack.pop() == self.min_stack[-1]: self.min_stack.pop() def top(self) -> int: return self.stack[-1] def getMin(self) -> int: return self.min_stack[-1]

10.服务端故障注入:

定义故障:首先需要确定要注入的故障类型,例如网络延迟、网络中断、服务宕机、越权访问等。

选择注入工具:选择合适的注入工具,例如模拟网络延迟的 tc 命令、断开网络连接的 iptables 命令、停止服务的 kill 命令等。

注入故障:在测试环境中,使用所选工具注入指定的故障,模拟服务端出现错误的情况。

测试响应能力:在注入故障后,测试系统的响应能力和处理能力,检查系统是否能够正确地处理故障,包括故障的检测、错误的处理和恢复等。

分析测试结果:根据测试数据和结果分析,优化系统架构和配置,提高系统的容错性、可用性和鲁棒性。

服务端故障注入是一种重要的测试方法,可以帮助开发人员和测试人员有效地测试系统在面对异常情况时的响应能力和处理能力,从而提高系统的可靠性和稳定性。

11.乐观锁和悲观锁区别

乐观锁和悲观锁是两种常见的并发控制机制,用来处理多线程或多进程环境下的并发问题。二者的主要区别在于对并发修改数据时锁的策略不同。

悲观锁

悲观锁假设每次数据访问都会发生冲突,因此在访问数据之前,必须先获取锁,读取或修改完成后再释放锁。这种方式对于对数据的修改频率较高、并发性较强的情况下,会造成比较大的性能损失。在实现过程中,通常是通过数据库的锁机制或者操作系统的文件锁实现。悲观锁总结为以下几点:

假设同一时间会有其他线程修改数据;

在访问数据之前必须先获得锁;

保证数据的互斥访问,但效率低下。

乐观锁

乐观锁更加优化,在读和写数据操作时,先不加锁进行操作,同时记录下数据版本号。当需要对数据进行修改时,先比较当前数据版本号与修改前的数据版本号是否一致,如果一致则说明在进行修改操作期间数据没有被其他线程修改,可以进行更新,并将版本号增加;与此同时,如果版本号不一致,则说明在操作期间有其他线程对数据进行了修改,此时必须回滚整个事务,重新读取数据,再重复上述操作。这种方式适用于更多的写入操作并不频繁,而是以读操作为主的情况,如缓存维护等场景。在实现上,可以使用版本号、时间戳等方式来实现乐观锁。乐观锁总结为以下几点:

假设同一时间不会有其他线程修改数据;

在执行更新操作时,先比较版本号(或时间戳);

提高效率,但可能会导致数据不一致。

总之,乐观锁适用于写少读多的情况,悲观锁适用于写多读少的情况。在并发控制的实践过程中,可以根据具体业务场景的需求,选择相应的锁机制来进行数据的操作和控制。

线程等待 隐式等待 显示等待的区别

线程等待、隐式等待和显示等待都是测试中常用的等待机制,它们用于等待某些条件的发生,从而避免因为出现错误或断言失败导致测试失败或出现异常情况。下面是它们的具体区别及实际场景应用:

线程等待(Thread.sleep()):在线程中添加一个等待时间段的方法。(例如,Thread.sleep(5000) 表示线程等待5秒后再执行后续代码)。线程等待的实现较为简单直接,但测试人员需要自行将时间计算进去,且无法控制最大等待时长。在开发初期或测试代码,线程等待可以用于调试和流程的调整等。

隐式等待(Implicit Wait):隐式等待的意思是如果没有找到元素,将等待一段时间再查找,最多等待一个固定时间段,如果等到了,就会立即执行待执行元素的下一步操作,否则再抛出异常。隐式等待可以在程序中全局设置,适用于针对所有元素的查找操作,可以避免在代码中重复添加 wait 语句;需要注意的是,隐式等待并不是针对特定元素的等待,而是整个 WebDriver 会等待该条件成立后再执行后续操作。一般用于相关测试用例中元素响应较快的场景。

显式等待(Explicit Wait):显式等待指的是在设置一个等待条件后,只要条件成立,就立即执行后续操作,否则一直等待,等待时间超过设置的最大时长后再抛出异常。显式等待可以应用于需要等待特定元素状态是否改变的场景,例如某个元素是否可点击或是否已经存在,而不是直接简单地等待固定的时间。

综上所述,线程等待适用于简单的等待时间情况;隐式等待适用于响应时间较短且需要全局适用的操作;显式等待适用于等待特定元素状态改变的场景。在实际测试场景中,需要根据实际情况选择合适的等待机制,以达到测试的目的。

你提出一个bug 但开发人员认为不是bug你怎么办?

如果你提出一个认为是缺陷,但开发人员不认同的bug,你可以采取以下步骤:

重现缺陷:以实际操作或测试计划中记录的步骤尝试重现缺陷,并记录相关证据。

发布缺陷:将记录的缺陷信息发布到缺陷跟踪系统中。

对话:与开发人员进行对话,并试图沟通原因,了解开发人员对缺陷是否属实以及能否修复的看法。

分析:在重现缺陷和进行讨论后,你需要分析以下几个方面:

此缺陷是否实际存在?

如果存在,对客户、用户或业务流程是否有负面影响?

解决此缺陷是否会有其他影响?

方案:如果缺陷确实存在,你需要对开发人员提出一些解决方案,并与开发人员沟通可能的影响,以达成共识。

可见性:如果你与开发人员沟通后仍然不能达成共识,建议在跟踪系统中指明此缺陷存在争议,并在缺陷报告中提供详细信息,使缺陷日后的修复和评估过程更加透明。

综上所述,如果你提出一个缺陷被开发人员认为不是缺陷,你可以采取上述步骤展开测试。经过分析和沟通,如果确实存在问题,开发人员将修复缺陷并避免影响。

  • 0
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值