大厂后端高频面试题及详细答案(无模板、真实面试风格)_第1页
大厂后端高频面试题及详细答案(无模板、真实面试风格)_第2页
大厂后端高频面试题及详细答案(无模板、真实面试风格)_第3页
大厂后端高频面试题及详细答案(无模板、真实面试风格)_第4页
大厂后端高频面试题及详细答案(无模板、真实面试风格)_第5页
已阅读5页,还剩4页未读, 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

大厂后端高频面试题及详细答案(无模板、真实面试风格)本次题库筛选字节、阿里、腾讯近年高频手撕、口述、工程实操真题,覆盖基础数据结构、核心算法、编程工程、系统基础四大板块,答案贴合面试官考察重点,口语化、无套话,适配现场面试口述+代码手写场景。一、数据结构高频题(必问基础)1、用两个栈实现队列(腾讯二面手撕)题目要求:实现队列的push、pop、peek操作,保证pop、peek平均时间复杂度O(1),不允许每次操作都迁移全部数据。参考答案:核心思路是拆分两个栈的职责,一个入队栈、一个出队栈,避免重复搬运数据。栈是后进先出,队列是先进先出,两个栈反向抵消特性即可实现队列逻辑。定义两个栈:inStack负责接收新增数据,outStack负责对外弹出、查看数据。规则:1.所有push操作直接压入inStack;2.执行pop/peek时,先判断outStack是否为空,为空则把inStack所有数据一次性导入outStack;3.数据迁移后,outStack栈顶就是队列最先进来的元素,直接操作即可;4.重复此逻辑,只有出队栈空了才迁移数据,保证单次操作最坏O(n),平均O(1)。手写代码(Java)PlainText

classMyQueue{

Stack<Integer>inStack;

Stack<Integer>outStack;

publicMyQueue(){

inStack=newStack<>();

outStack=newStack<>();

}

//入队

publicvoidpush(intx){

inStack.push(x);

}

//出队

publicintpop(){

if(outStack.isEmpty()){

while(!inStack.isEmpty()){

outStack.push(inStack.pop());

}

}

returnoutStack.pop();

}

//查看队首

publicintpeek(){

if(outStack.isEmpty()){

while(!inStack.isEmpty()){

outStack.push(inStack.pop());

}

}

returnoutStack.peek();

}

publicbooleanempty(){

returninStack.isEmpty()&&outStack.isEmpty();

}

}面试官追问应答:为什么不能每次pop都迁移数据?每次迁移会导致每次pop都是O(n)复杂度,大量操作下性能极差。批量迁移、复用出队栈数据,才能实现平均O(1)的最优效果,也是工程落地的标准写法。2、链表局部反转(LeetCode92,阿里P7手撕)题目要求:给定单链表,反转第m到第n个节点,只遍历一次,不允许额外存储节点。参考答案:常规全链表反转无法适配局部场景,核心思路是:先定位前驱节点,再局部反转,最后拼接首尾,全程一次遍历。步骤:1.定义虚拟头节点,规避头节点反转的边界问题;2.遍历找到第m个节点的前驱节点pre;3.记录起始反转节点cur,以及后继节点next,在m~n区间内逐次反转指针;4.反转完成后,拼接前驱、反转区间尾节点、后续节点,保证链表不断裂。手写代码(Java)PlainText

classSolution{

publicListNodereverseBetween(ListNodehead,intm,intn){

ListNodedummy=newListNode(0);

dummy.next=head;

ListNodepre=dummy;

//找到m的前驱节点

for(inti=1;i<m;i++){

pre=pre.next;

}

ListNodecur=pre.next;

ListNodenext;

//局部反转n-m次

for(inti=m;i<n;i++){

next=cur.next;

cur.next=next.next;

next.next=pre.next;

pre.next=next;

}

returndummy.next;

}

}复杂度说明:时间O(N)、空间O(1),满足题目一次遍历、原地修改的要求。3、LRU缓存原理及手写实现(阿里、字节高频)题目问题:简述LRU原理,手撕简易LRU缓存,实现get、put方法。参考答案:LRU是最近最少使用缓存淘汰策略,核心逻辑是:缓存满时,淘汰最长时间未被访问的数据,优先保留近期高频使用的数据。工程最优实现是哈希表+双向链表:哈希表保证读写O(1),双向链表保证快速调整节点位置、删除尾节点。规则:1.访问/新增数据时,将节点移动到链表头部(代表最近使用);2.缓存容量超限,删除链表尾部节点(最久未使用);3.哈希表存储key和节点映射,快速定位节点。手写简易实现PlainText

classLRUCache{

//双向链表节点

staticclassNode{

intkey,value;

Nodeprev,next;

Node(intk,intv){

key=k;

value=v;

}

}

privatefinalintcapacity;

privatefinalMap<Integer,Node>map;

privatefinalNodehead,tail;

publicLRUCache(intcapacity){

this.capacity=capacity;

map=newHashMap<>();

//虚拟头尾节点,简化边界判断

head=newNode(0,0);

tail=newNode(0,0);

head.next=tail;

tail.prev=head;

}

publicintget(intkey){

if(!map.containsKey(key))return-1;

Nodenode=map.get(key);

//移除原位置,移到头部

removeNode(node);

addHead(node);

returnnode.value;

}

publicvoidput(intkey,intvalue){

if(map.containsKey(key)){

//更新值并刷新位置

Nodenode=map.get(key);

node.value=value;

removeNode(node);

addHead(node);

}else{

//容量超限,淘汰尾部

if(map.size()>=capacity){

Nodelast=tail.prev;

removeNode(last);

map.remove(last.key);

}

NodenewNode=newNode(key,value);

map.put(key,newNode);

addHead(newNode);

}

}

//移除节点

privatevoidremoveNode(Nodenode){

node.prev.next=node.next;

node.next.prev=node.prev;

}

//头部插入节点

privatevoidaddHead(Nodenode){

node.next=head.next;

head.next.prev=node;

head.next=node;

node.prev=head;

}

}二、核心算法高频题(手撕重点)1、阶乘后的零(字节一面数学算法题)题目要求:给定整数n,返回n!结果末尾的零的个数,要求最优时间复杂度。参考答案:末尾的零由10相乘产生,10=2*5,阶乘中2的数量远多于5,所以零的个数由因子5的个数决定。易错点:只统计n/5不够,25、125这类数包含多个5,需要层层整除累加。算法逻辑:循环将n除以5,累加每一轮的商,直到n为0。PlainText

publicinttrailingZeroes(intn){

intcount=0;

while(n>0){

n/=5;

count+=n;

}

returncount;

}复杂度:时间O(log₅N),空间O(1),是最优解法。2、有效的括号(字节、美团基础必问)题目要求:判断字符串括号是否有效,包含()[]{},满足闭合顺序正确、类型匹配。参考答案:利用栈后进先出的特性,匹配括号嵌套逻辑。遇到左括号,压入对应的右括号;遇到右括号,弹出栈顶对比,不一致则无效。边界条件:遍历结束后栈必须为空,否则存在未闭合的左括号。PlainText

publicbooleanisValid(Strings){

Stack<Character>stack=newStack<>();

for(charc:s.toCharArray()){

if(c=='(')stack.push(')');

elseif(c=='[')stack.push(']');

elseif(c=='{')stack.push('}');

//栈空或字符不匹配

elseif(stack.isEmpty()||stack.pop()!=c)returnfalse;

}

returnstack.isEmpty();

}3、前K个高频元素(字节二面堆排序应用)题目要求:给数组,返回出现频率最高的前k个元素,时间复杂度优于O(NlogN)。参考答案:最优思路:哈希表统计频率+小顶堆筛选TopK。全程不用全排序,效率更高。步骤:1.遍历数组,用HashMap统计每个元素出现次数;2.维护一个大小为k的小顶堆,堆顶是当前筛选结果中频率最小的;3.遍历哈希表,堆大小不足k直接入堆,满了则对比堆顶,频率更高则替换;4.最终堆中元素即为前K高频元素。PlainText

publicint[]topKFrequent(int[]nums,intk){

Map<Integer,Integer>countMap=newHashMap<>();

for(intnum:nums){

countMap.put(num,countMap.getOrDefault(num,0)+1);

}

//小顶堆

PriorityQueue<Map.Entry<Integer,Integer>>heap=newPriorityQueue<>(CparingInt(Map.Entry::getValue));

for(Map.Entry<Integer,Integer>entry:countMap.entrySet()){

heap.offer(entry);

if(heap.size()>k){

heap.poll();

}

}

int[]res=newint[k];

for(inti=0;i<k;i++){

res[i]=heap.poll().getKey();

}

returnres;

}复杂度:时间O(NlogK),远优于全排序的O(NlogN),适配海量数据场景。三、工程实操面试题(口述核心,大厂高频)1、HashMap底层原理及线程不安全原因(阿里一面)参考答案:HashMap底层是数组+链表+红黑树。通过哈希算法计算key下标,哈希冲突时链表存储,链表长度超过8、数组长度大于64时转为红黑树,提升查询效率。核心参数:默认容量16,负载因子0.75,扩容阈值=容量*负载因子,达到阈值自动扩容为原容量2倍。线程不安全核心场景:1.多线程同时put数据,可能导致数据覆盖;2.JDK1.7扩容时存在链表环形引用,并发场景触发死循环;3.并发扩容、并发读写都会导致数据不一致。替代方案:并发场景用ConcurrentHashMap,分段锁(JDK1.7)、CAS+Synchronized(JDK1.8)保证线程安全,性能优于HashTable。2、线程池核心参数及工作原理(腾讯一面)参考答案:线程池核心参数:核心线程数、最大线程数、空闲超时时间、阻塞队列、拒绝策略。工作流程:1.任务提交时,优先创建核心线程执行;2.核心线程满后,任务进入阻塞队列排队;

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

最新文档

评论

0/150

提交评论