面试必备:高频算法题汇总「图文解析 + 教学视频 + 范例代码」必问之 链表 + 栈 + 队列 部分!
链表
链表
是最基本的数据结构,面试官也常常用链表来考察面试者的基本能力,而且链表相关的操作相对而言比较简单,也适合考察写代码的能力。链表的操作也离不开指针,指针又很容易导致出错。
综合多方面的原因,链表题目在面试中占据着很
重要
的地位。
public class ListNode {int val;ListNode next;ListNode(int x) {val = x;next = null;}
}
删除节点
思路:
- 将
下一个节点
复制到当前
public void deleteNode(ListNode node) {if (node.next == null){node = null;return;}// 取缔下一节点node.val = node.next.valnode.next = node.next.next
}
翻转链表
思路
思路:每次都将原第一个结点之后的那个结点放在新的表头后面。
比如1,2,3,4,5
- 第一次:把第一个
结点1
后边的结点2
放到新表头
后面,变成2,1,3,4,5
- 第二次:把第一个
结点1
后边的结点3
放到新表头
后面,变成3,2,1,4,5
- ……
- 直到: 第一个
结点1
,后边没有结点为止。
视频
大圣算法 翻转链表(Reverse Linked List ) – LeetCode 206
public ListNode reverse(ListNode head) {//prev表示前继节点ListNode prev = null;while (head != null) {//temp记录下一个节点,head是当前节点ListNode temp = head.next;head.next = prev;prev = head;head = temp;}return prev;
}
中间元素
思路
我总结了一下,可以称为 田忌赛马’
法
public ListNode findMiddle(ListNode head){if(head == null){return null;}ListNode slow = head;ListNode fast = head;// fast.next = null 表示 fast 是链表的尾节点while(fast != null && fast.next != null){fast = fast.next.next;slow = slow.next;}return slow;
}
合并两个已排序链表
思路
- 递归方法:首先比较给新链表
接上
一个结点,然后这个结点的next
就是剩下的两条链表合并的结果。
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {ListNode dummy = new ListNode(0);ListNode lastNode = dummy;while (l1 != null && l2 != null) {if (l1.val < l2.val) {lastNode.next = l1;l1 = l1.next;} else {lastNode.next = l2;l2 = l2.next;}lastNode = lastNode.next;}if (l1 != null) {lastNode.next = l1;} else {lastNode.next = l2;}return dummy.next;
}
链表排序
归并排序
归并排序
的也是基于分治
的思想,但是与快排不同的是归并是先划分
,然后从底层开始向上合并
。归并排序的主要思想是将两个
已经排好序的分段
合并成一个有序的分段
。除了找到中间节点的操作必须遍历链表外,其它操作与数组的归并排序基本相同。
视频
合并两个排序链表
public ListNode sortList(ListNode head) {if (head == null || head.next == null) {return head;}// 取得中间节点,将链表一分为二ListNode mid = findMiddle(head);ListNode right = sortList(mid.next);mid.next = null;ListNode left = sortList(head);return mergeTwoLists(left, right);
}// 查找中间元素算法
public ListNode findMiddle(ListNode head){if(head == null){return null;}ListNode slow = head;ListNode fast = head;// fast.next = null 表示 fast 是链表的尾节点while(fast != null && fast.next != null){fast = fast.next.next;slow = slow.next;}return slow;
}// 合并两个有序链表
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {ListNode dummy = new ListNode(0);ListNode lastNode = dummy;while (l1 != null && l2 != null) {if (l1.val < l2.val) {lastNode.next = l1;l1 = l1.next;} else {lastNode.next = l2;l2 = l2.next;}lastNode = lastNode.next;}if (l1 != null) {lastNode.next = l1;} else {lastNode.next = l2;}return dummy.next;
}
快速排序
快速排序的主要思想是:
选定一个
基准元素
经过一趟排序,将所有元素分成
两部分
分别对两部分重复上述操作,直到所有元素都已排序成功
因为单链表只能从链表头节点向后遍历,没有prev
指针,因此必须选择头节点作为基准元素。这样第二步操作的时间复杂度就为O(n)
。由于之后都是分别对两部分完成上述操作,因此会将链表划分为lgn个段,因此时间复杂度为O(nlgn)
public ListNode sortList(ListNode head) {quickSort(head, null);return head;
}private void quickSort(ListNode start, ListNode end) {if (start == end) {return;}ListNode pt = partition(start, end);quickSort(start, pt);quickSort(pt.next, end);
}// 快排 轮状法
private ListNode partition(ListNode start, ListNode end) {int pivotKey = start.val;ListNode p1 = start, p2 = start.next;while (p2 != end) {if (p2.val < pivotKey) {p1 = p1.next;swapValue(p1, p2);}p2 = p2.next;}swapValue(start, p1);return p1;
}private void swapValue(ListNode node1, ListNode node2) {int tmp = node1.val;node1.val = node2.val;node2.val = tmp;
}
两个链表是否相交
思路
如果两个单链表有
共同
的节点那么从第一个节点开始,后面的节点都会
重叠
,直至链表结束因为两个链表中有一个
共同
节点则从这个节点里的
指针域
指向下一个节点的地址就相同所以
相交以后
的节点就会相同
,直至链表结束,总的模型就像一个“Y”
视频
【一起玩算法】交叉链表练习题讲解
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {if (headA == null || headB == null) {return null;}ListNode currA = headA;ListNode currB = headB;int lengthA = 0;int lengthB = 0;// 让长的先走到剩余长度和短的一样while (currA != null) {currA = currA.next;lengthA++;}while (currB != null) {currB = currB.next;lengthB++;}currA = headA;currB = headB;while (lengthA > lengthB) {currA = currA.next;lengthA--;}while (lengthB > lengthA) {currB = currB.next;lengthB--;}// 然后同时走到第一个相同的地方while (currA != currB) {currA = currA.next;currB = currB.next;}// 返回交叉开始的节点return currA;
}
栈 / 队列
- 栈(stack)又名堆栈:
它是一种运算受限的线性表。限定仅在表尾进行插入和删除操作的线性表。这一端被称为栈顶,相对地,把另一端称为栈底。向一个栈插入新元素又称作进栈、入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素。
- 队列是一种特殊的线性表
特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。
带最小值操作的栈
这道面试题主要考察我们对于辅助栈的使用。
常见的辅助栈包括两种:
辅助栈和数据栈同步
辅助栈和数据栈不同步
我们这里采用辅助栈和数据栈同步的方式:
特点:编码简单,不用考虑一些边界情况,就有一点不好:辅助栈可能会存一些“不必要”的元素。
辅助栈为空的时候,必须放入新进来的数;
新来的数小于或者等于辅助栈栈顶元素的时候,才放入,特别注意这里“等于”要考虑进去,因为出栈的时候,连续的、相等的并且是最小值的元素要同步出栈;
出栈的时候,辅助栈的栈顶元素等于数据栈的栈顶元素,才出栈。
总结一下:出栈时,最小值出栈才同步;入栈时,最小值入栈才同步。
public class MinStack {private Stack<Integer> stack;private Stack<Integer> minStack; // 维护一个辅助栈,传入当前栈的最小值public MinStack() {stack = new Stack<Integer>();minStack = new Stack<Integer>();}public void push(int number) {stack.push(number);if (minStack.isEmpty()) {minStack.push(number);} else {minStack.push(Math.min(number, minStack.peek()));}}public int pop() {minStack.pop();return stack.pop();}public int min() {return minStack.peek();}
}
有效括号
思路:
初始化栈 S。
一次处理表达式的每个括号。
如果遇到开括号,我们只需将其推到栈上即可。这意味着我们将稍后处4理它,让我们简单地转到前面的 子表达式。
如果我们遇到一个闭括号,那么我们检查栈顶的元素。如果栈顶的元素是一个 相同类型的 左括号,那么我们将它从栈中弹出并继续处理。否则,这意味着表达式无效。
如果到最后我们剩下的栈中仍然有元素,那么这意味着表达式无效。
视频
英文:【编程】字符串中括号平衡判断(HackerRank)
public boolean isValidParentheses(String s) {Stack<Character> stack = new Stack<Character>();for (Character c : s.toCharArray()) {if ("({[".contains(String.valueOf(c))) {stack.push(c);} else {if (!stack.isEmpty() && isValid(stack.peek(), c)) {stack.pop();} else {return false;}}}return stack.isEmpty();
}private boolean isValid(char c1, char c2) {return (c1 == '(' && c2 == ')') || (c1 == '{' && c2 == '}')|| (c1 == '[' && c2 == ']');
}
用栈实现队列
思路:
思路是有两个栈,一个用来放数据(数据栈),一个用来辅助(辅助栈)。
数据添加时,会依次压人栈,取数据时肯定会取栈顶元素,但我们想模拟队列的先进先出,所以就得取栈底元素,那么辅助栈就派上用场了
把数据栈的元素依次弹出到辅助栈,但保留最后一个元素,最后数据栈就剩下了最后一个元素,直接把元素返回,这时数据栈已经没有了数据。
最后呢,把辅助栈的元素依次压人数据栈,这样,我们成功取到了栈底元素。
视频
图解「剑指Offer」之使用栈实现队列
public class MyQueue {private Stack<Integer> outStack;private Stack<Integer> inStack;public MyQueue() {outStack = new Stack<Integer>();inStack = new Stack<Integer>();}private void in2OutStack(){while(!inStack.isEmpty()){outStack.push(inStack.pop());}}public void push(int element) {inStack.push(element);}public int pop() {if(outStack.isEmpty()){this.in2OutStack();}return outStack.pop();}public int top() {if(outStack.isEmpty()){this.in2OutStack();}return outStack.peek();}
}
逆波兰表达式求值 (后缀)
思路:
逆波兰表达式求解,定义一个栈辅助计算
当遇到运算符"+"、"-"、"*"、"/"时,从栈中pop出两个数字计算,否则将数字入栈;
public int evalRPN(String[] tokens) {Stack<Integer> s = new Stack<Integer>();String operators = "+-*/";for (String token : tokens) {if (!operators.contains(token)) {s.push(Integer.valueOf(token));continue;}// 这里有个坑int a = s.pop();int b = s.pop();// 先出的在运算符后// 后出的在运算符前if (token.equals("+")) {s.push(b + a);} else if(token.equals("-")) {s.push(b - a);} else if(token.equals("*")) {s.push(b * a);} else {s.push(b / a);}}return s.pop();
}
重点
为了提高文章质量,防止冗长乏味
下一部分算法题
本片文章篇幅总结越长。我一直觉得,一片过长的文章,就像一堂超长的 会议/课堂,体验很不好,所以我打算再开一篇文章
在后续文章中,我将继续针对
链表
栈
队列
堆
动态规划
矩阵
位运算
等近百种,面试高频算法题,及其图文解析 + 教学视频 + 范例代码
,进行深入剖析有兴趣可以继续关注 _yuanhao 的编程世界不求快,只求优质,每篇文章将以 2 ~ 3 天的周期进行更新,力求保持高质量输出
相关文章
? 面试必备:高频算法题汇总「图文解析 + 教学视频 + 范例代码」必问之 排序 + 二叉树 部分!?
高效解决「SQLite」数据库并发访问安全问题,只这一篇就够了
每个人都要学的图片压缩终极奥义,有效解决 Android 程序 OOM
Android 让你的 Room 搭上 RxJava 的顺风车 从重复的代码中解脱出来
ViewModel 和 ViewModelProvider.Factory:ViewModel 的创建者
单例模式-全局可用的 context 对象,这一篇就够了
缩放手势 ScaleGestureDetector 源码解析,这一篇就够了
看完这篇还不会 GestureDetector 手势检测,我跪搓衣板!
Android 属性动画框架 ObjectAnimator、ValueAnimator ,这一篇就够了
看完这篇再不会 View 的动画框架,我跪搓衣板
请点赞!因为你的鼓励是我写作的最大动力!
为了方便大家跟进学习,我在 GitHub 建立了一个仓库
仓库地址:超级干货!精心归纳视频、归类、总结
,各位路过的老铁支持一下!给个 Star !
面试必备:高频算法题汇总「图文解析 + 教学视频 + 范例代码」必问之 链表 + 栈 + 队列 部分!相关推荐
- 面试必备:高频算法题汇总「图文解析 + 教学视频 + 范例代码」之 字符串处理+动态规划 合集!
Attention 秋招接近尾声,我总结了 牛客.WanAndroid 上,有关笔试面经的帖子中出现的算法题,结合往年考题写了这一系列文章,所有文章均与 LeetCode 进行核对.测试.欢迎食用 本 ...
- 面试必备:高频算法题汇总「图文解析 + 教学视频 + 范例代码」必问之 排序 + 二叉树 部分!
排序 所谓排序算法,即通过特定的算法因式将一组或多组数据按照既定模式进行重新排序.这种新序列遵循着一定的规则,体现出一定的规律,因此,经处理后的数据便于筛选和计算,大大提高了计算效率. 对于排序: 我 ...
- 面试必备:高频算法题汇总「图文解析 + 教学视频 + 范例代码」必知必会 排序 + 二叉树 部分!
排序 时光小说网 https://wap.youxs.org/ 所谓排序算法,即通过特定的算法因式将一组或多组数据按照既定模式进行重新排序.这种新序列遵循着一定的规则,体现出一定的规律,因此,经处理后 ...
- 2021届秋招大厂高频算法题汇总
当初在准备秋招的时候,算法是一个大难题,不知道该怎么准备,也不知道应该要刷哪些题,只能在网上看面经,然后整理一遍,下面这些题目主要来源三个方面, (1)我自己遇到的真题 (2)牛客上的面经,别人遇到的 ...
- 面试高频算法题补充系列:木棍切割问题
前言 了解更多常考高频算法题可以关注 公众号:一个搬砖的胖子 高频算法网站:https://codetop.cc/ 小程序:CodeTop 此题经常在字节跳动后端面试中遇到,尤其是教育部门的三面.我押 ...
- 面试高频算法题补充系列:如何判断一个点是否在三角形内?
前言 了解更多常考高频算法题可以关注 公众号:一个搬砖的胖子 企业面试题库:https://codetop.cc/ 小程序:CodeTop 该题曾出现在字节跳动.腾讯.网易.美团.小马智行等公司的面试 ...
- Leetcode高频算法题——字节跳动篇
众所周知,字节跳动面试非常喜欢考算法题. Leetcode官方的企业题库不仅付费,而且题量非常大. 因此,我从20年7月开始,手动整理牛客面经涉及的Leetcode算法题,并按照频度由高到低进行了排列 ...
- 大厂敲门砖——算法,手撸3道高频算法题,检测真水平
3道高频算法题 手撸算法1:查找数组中重复元素和重复元素的个数 手撸算法2:写个二分查找demo吧 手撸算法3:把两个有序数组合并成一个有序数组 要进大厂,算法是不可或缺的一环,也是块儿敲门砖,科一都 ...
- 算法面试必备-----贝壳算法面试准备
算法面试必备-----贝壳算法面试准备 算法面试必备-----贝壳算法面试准备 机器学习基础 1.过拟合和欠拟合的学习曲线怎么画,横纵坐标轴分别代表什么?实际应用当中,如何区分你是过拟合还是前拟合? ...
最新文章
- Ubuntu16.04下安装mysql5.7数据库
- 异步Socket通信总结[转]
- 【Spring学习】spring依赖注入用法总结
- 两种方式解决 生产者消费者问题
- 【CCF】 201809-1 卖菜
- python调用jenkinsAPI构建jenkins,并传递参数
- 艰困之道中学到的经验教训
- 开发CISCO交换机管理软件
- Flashlight should be gray after finishing Recor...
- Redis集群环境搭建实践
- redis基础学习(一)— 配置项
- Discrete Logging ZOJ - 1898 (模板题大小步算法)
- python安装requests库提示更新_解决已经安装requests,却依然提示No module named requests问题...
- DPCM差分预测编码
- 【网单服务端】卡丁车深海之城单机游戏服务端+使用说明
- ac68无线网卡 linux驱动下载,华硕USB-AC68驱动|华硕USB-AC68无线网卡驱动下载 v2.1.2.1官方版 - 121下载站...
- JS手写实现call、apply、bind
- 一个可以提升180%推广效果的信息流广告投放策略
- 抱团股会一直涨?无脑执行大小盘轮动策略,轻松跑赢指数5倍【附Python代码】
- linux freetype 安装