大厂面试基础算法总结(强烈推荐)❤️❤️❤️❤️❤️

522 阅读14分钟

文章目录

一、前言

  最近发现了一个网站,叫 牛客网(www.nowcoder.com),看了看上面的题目,普遍偏简单,于是找回了大学时候刷题的快感,赶紧来刷一刷。选了一个企业,把它的面试题刷完了。
在这里插入图片描述
大致做一波总结,如果有小伙伴有兴趣的,可以照着我的目录来刷,所有题目基本都属于没有很强算法基础就能做的,分成两大块内容:数据结构 和 算法。数据结构包含:链表、队列、栈、二叉树。算法包含:排序、二分枚举、搜索、动态规划、贪心、位运算应用、模拟等等。


在这里插入图片描述


二、数据结构

1、单向链表

1)链表的删除

  • 单向链表的删除,需要先找到这个结点,并且记录它的前驱结点,让前驱结点指向它的后继结点,整个过程最坏时间复杂度为 O ( n ) O(n) O(n),主要是遍历的时间复杂度,删除结点这个操作本身是 O ( 1 ) O(1) O(1) 的。
题目链接难度
NC53 删除链表的倒数第n个节点★☆☆☆☆
NC24 删除有序链表中重复出现的元素★★☆☆☆
NC25 删除有序链表中重复的元素★★☆☆☆
NC2 重排链表★★☆☆☆
NC133 链表的奇偶重排★★☆☆☆

2)链表的翻转

  • 对于翻转链表的问题,比较好的做法就是遍历链表,删除遍历到的结点,插到头部,即所谓头插法,只需要实现delNodeinsertNode两个接口即可。时间复杂度 O ( n ) O(n) O(n),空间复杂度 O ( 1 ) O(1) O(1)。
  • 如图所示,先把需要翻转的结点删除,然后插到头部,然后执行下一个结点,直到整个链表遍历完毕,就完成了链表的翻转。
  • 当然,有些问题可能是选择某一段区间的链表结点进行翻转,方法一样,为了处理头结点的特殊情况,可以在链表头增加一个 “伪头结点”,这样就可以防止链表头随时变化的情况,返回链表头只需要返回"伪头结点" 的 next即可。
题目链接难度
NC78 反转链表★☆☆☆☆
NC50 链表中的节点每k个一组翻转★☆☆☆☆
NC40 两个链表生成相加链表★☆☆☆☆
NC96 判断一个链表是否为回文结构★☆☆☆☆
NC21 链表内指定区间反转★★☆☆☆

3)链表的快慢指针

  • 判断一个链表里面有没有环,采用的是快慢指针。总体思路是一个指针走1步,一个指针走2步,由于 1 和 2 互素,如果有环,他们一定会相遇。
  • 假设没有环的部分,长度为 x x x,环的长度为 y y y,环入口点到相遇点的距离为 z z z,那么慢指针走过的距离为 x + z x+z x+z,快指针走过的距离为 n y + x + z ny + x + z ny+x+z,又知道慢指针速度为1,快指针速度为2,如果它们相遇,则相遇的时间相等,有: ( x + z ) 1 = ( n y + x + z ) 2 \frac {(x+z)} 1 = \frac {(ny+x+z)} 2 1(x+z)​=2(ny+x+z)​
  • n n n 为任意整数,所以这个方程 z = n y − x z = ny - x z=ny−x,这个公式的含义是:慢指针只要再走 x x x 步 就能到达环的入口了。
题目链接难度
NC66 两个链表的第一个公共结点★☆☆☆☆
NC4 判断链表中是否有环★★☆☆☆
NC3 链表中环的入口节点★★★☆☆

2、双向链表

  • 双向链表比链表本身多了一个指针,指向它的前驱。一般对一些时间复杂度要求较高的问题采用双向链表。
  • 以下两题双向链表的题目,涉及到其它数据结构,较为复杂,建议最后做。
题目链接难度
NC93 设计LRU缓存结构★★★☆☆
NC94 LFU缓存结构设计★★★☆☆

3、栈

题目链接难度
NC76 用两个栈实现队列★☆☆☆☆
NC128 容器盛水问题★★☆☆☆
NC157 单调栈★★☆☆☆
NC108 最大正方形★★☆☆☆

4、二叉树

  • 二叉树的遍历有先序、中序、后序、层序;
  • 先序、中序、后序遍历,考察的点主要是递归,一般做题思路是:根据左右子树的遍历结果,来决定当前树的值。子树的结果可以通过返回值返回,也可以通过传参引用返回。
  • 层序遍历就是广度优先搜索了。对于广搜,主要应用是图的最短路问题,如果需要详细学习的话,可以参见以下这篇文章:夜深人静写算法(十)- 单向广搜
题目链接难度
NC45 实现二叉树先序,中序和后序遍历★☆☆☆☆
NC15 求二叉树的层序遍历★☆☆☆☆
NC14 二叉树的之字形层序遍历★☆☆☆☆
NC72 二叉树的镜像★☆☆☆☆
NC62 平衡二叉树★☆☆☆☆
NC13 二叉树的最大深度★☆☆☆☆
NC5 二叉树根节点到叶子节点的所有路径和★☆☆☆☆
NC8 二叉树根节点到叶子节点和为指定值的路径★☆☆☆☆
NC16 判断二叉树是否对称★☆☆☆☆
NC6 二叉树的最大路径和★☆☆☆☆
NC12 重建二叉树★★☆☆☆
NC136 输出二叉树的右视图★★☆☆☆
NC60 判断一棵二叉树是否为搜索树★★☆☆☆
NC9 二叉树中是否存在节点和★★☆☆☆
NC117 合并二叉树★★☆☆☆
NC123 序列化二叉树★★★☆☆
NC64 二叉搜索树与双向链表★★★☆☆

三、算法

1、排序

  • 排序的话,一般面试最喜欢问的是快排,基本大部分技术博客都讲过了,我就不讲了。但是,实际应用中一般用 STL 的 sort配合类重写仿函数就能解决大部分多关键字的排序问题了。
  • 静态的第 k k k 大的数,如果时间复杂度没有太苛刻的要求,基本也都是排个序就解决的事情。
  • 倒是可以用下面的题来练习写一下快排。
题目链接难度
NC140 排序★☆☆☆☆
NC119 最小的K个数★☆☆☆☆
NC88 寻找第K大★☆☆☆☆
NC33 合并有序链表★☆☆☆☆
NC22 合并两个有序的数组★☆☆☆☆
NC51 合并k个已排序的链表★☆☆☆☆
NC70 链表排序★☆☆☆☆
NC97 出现次数的TopK问题★☆☆☆☆
NC73 数组中出现次数超过一半的数字★☆☆☆☆
NC36 在两个长度相等的排序数组中找到上中位数★☆☆☆☆

2、二分枚举

  • 二分查找作为最经典的 O ( l o g 2 n ) O(log_2n) O(log2​n) 的算法,也是面试考察的重点,所以手写一个二分查找也是必须的,实际应用中一般用 STL 的 lower_bound就能实现数组的二分查找了。
  • 二分查找不光可以在数组中找数据,还可以对单调函数,二分枚举答案求可行解,例如:求一个数的平方根。
  • 实际上有序数组也是特殊的的单调函数,只不过它的值是离散的。
题目链接难度
NC105 二分查找★☆☆☆☆
NC48 在转动过的有序数组中寻找目标值★☆☆☆☆
NC61 两数之和★☆☆☆☆
NC54 数组中相加和为0的三元组★☆☆☆☆
NC32 求平方根★☆☆☆☆
NC86 矩阵元素查找★☆☆☆☆
NC29 矩阵查找★☆☆☆☆

3、广度优先搜索

题目链接难度
NC99 树的直径★★★☆☆
NC138 矩阵最长递增路径★★★☆☆

4、深度优先搜索

  • 深度优先搜索的最大的用处是穷举问题的所有情况,并且进行适当的合法剪枝,所以没有思路的时候可以试着用深搜来求解问题。深搜的实现是递归。
  • 一般枚举全排列、全组合的时候也要用到深搜。
题目链接难度
NC102 最近公共祖先★☆☆☆☆
NC38 螺旋矩阵★☆☆☆☆
NC121 字符串的排列★☆☆☆☆
NC109 岛屿数量★☆☆☆☆
【推荐】NC137 表达式求值★★☆☆☆
NC26 括号生成★★☆☆☆
NC42 有重复项数字的所有排列★★☆☆☆
NC27 集合的所有子集★★☆☆☆
NC46 加起来和为目标值的组合★★☆☆☆
NC43 没有重复项数字的所有排列★★☆☆☆
NC39 N皇后问题★★☆☆☆
NC79 丑数★★☆☆☆

5、动态规划

  • 这里的动态规划都是最简单的,基本把所有 “简单动态规划” 的内容都涵盖了,这里说一下每一类动态规划的思路。

1)递推

【NC68 跳台阶】一只青蛙一次可以跳上 1 1 1 级台阶,也可以跳上 2 2 2 级。求该青蛙跳上一个 n n n 级的台阶总共有多少种跳法。

  • 递推的问题一般依赖前一项,或者前两项,或者前 c c c 项,且是方案数相加的形式;
  • 对于 n n n 个台阶,我跳 1 1 1 步以后,剩下就是跳 n − 1 n-1 n−1 步的情况;跳 2 2 2 步以后,剩下就是跳 n − 2 n-2 n−2 步的情况;不能一下子跳 3 3 3 步,所以这个问题的状态转移方程就是: f [ n ] = f [ n − 1 ] + f [ n − 2 ] f[n] = f[n-1] + f[n-2] f[n]=f[n−1]+f[n−2]

2)子段最优值

【NC19 子数组的最大累加和问题】给定一个数组 a a a,返回子数组的最大累加和,例如, a [ ] = [ 1 , − 2 , 3 , 5 , − 2 , 6 , − 1 ] a[] = [1, -2, 3, 5, -2, 6, -1] a[]=[1,−2,3,5,−2,6,−1],所有子数组中, [ 3 , 5 , − 2 , 6 ] [3, 5, -2, 6] [3,5,−2,6] 可以累加出最大的和 12 12 12,所以返回 12 12 12 。

  • 子段最优值问题,一般是有一个数组序列,要求求一个连续(或非连续)的子序列,运用某种运算达到最优值。

  • 我们一般可以这么设计状态:令 f [ n ] f[n] f[n] 表示以第 n n n 个数结尾能够达到的最优解。

  • 如果要求序列连续,那么 f [ n ] f[n] f[n] 的最优解取决于 f [ n − 1 ] f[n-1] f[n−1] 选 和 不选两种情况的最优值。

  • 如果不要求序列连续,那么 f [ n ] f[n] f[n] 的最优解取决于 选 f [ i ] ( i < n ) f[i] (i < n) f[i](i<n) 中的其中一个所达成的最优值。

  • 上面这个问题的状态转移方程(属于连续子段最优值问题)就是: f [ i ] = a [ i ] + { 0 f [ i − 1 ] ≤ 0 f [ i − 1 ] f [ i − 1 ] > 0 f[i] = a[i] + \begin{cases} 0 & f[i-1] \le 0 \\ f[i-1] & f[i-1] > 0\end{cases} f[i]=a[i]+{

    0f[i−1]​f[i−1]≤0f[i−1]>0​

3)区间 DP

  • 区间 DP 的状态转移方程如下: d p [ i ] [ j ] = o p t ( d p [ i + 1 ] [ j − 1 ] + c o s t ( i , j ) , d p [ i ] [ k ] + d p [ k + 1 ] [ j ] ) dp[i][j] = opt(dp[i+1][j-1] + cost(i,j), dp[i][k]+dp[k+1][j]) dp[i][j]=opt(dp[i+1][j−1]+cost(i,j),dp[i][k]+dp[k+1][j])

4)二维DP

  • 最长公共子序列、最长公共子串、最小编辑距离 都属于二维DP,一般用于两个字符串的匹配问题。
  • 一般的状态转移方程为: d p [ i ] [ j ] = o p t ( d p [ i − 1 ] [ j ] + x , d p [ i ] [ j − 1 ] + y , d p [ i − 1 ] [ j − 1 ] + z ) dp[i][j] = opt(dp[i-1][j]+x, dp[i][j-1]+y, dp[i-1][j-1]+z) dp[i][j]=opt(dp[i−1][j]+x,dp[i][j−1]+y,dp[i−1][j−1]+z)
  • 如图所示,比较直观的想法就是,红色位置的状态取决于三个蓝色位置的最优值:
题目链接类型
NC68 跳台阶递推
NC65 斐波那契数列递推
NC19 子数组的最大累加和问题子段最优值 - 最大子段和
NC49 最长的括号子串子段最优值
NC83 子数组最大乘积子段最优值
NC52 括号序列区间DP
NC91 最长递增子序列子段最优值 - 最长递增子序列
NC127 最长公共子串二维DP - 最长公共子串
NC92 最长公共子序列二维DP - 最长公共子序列
NC35 最小编辑代价二维DP - 最小编辑距离
NC59 矩阵的最小路径和二维DP
NC34 求路径二维DP
NC87 丢棋子问题二维DP
NC122 正则表达式匹配二维DP
NC44 通配符匹配二维DP
NC17 最长回文子串最长回文子串
NC116 把数字翻译成字符串子段方案数
NC126 换钱的最少货币数0/1 背包

6、贪心

  • 贪心说起来比较微妙,就是穷尽你平生所学来想这样做对不对!就这么简单(难)。
  • 贪心一般配合排序来做。
  • 遇到一些没什么想法的题,可以先排个序,然后看看有什么规律,再来决定用贪心还是动态规划。
题目链接类型
NC101 缺失数字★☆☆☆☆
NC37 合并区间★☆☆☆☆
NC95 最长连续子序列★☆☆☆☆
NC111 最大数★★☆☆☆
NC7 股票(一次交易)★☆☆☆☆
NC55 最长公共前缀★☆☆☆☆
NC132 环形链表的约瑟夫问题★☆☆☆☆
NC107 寻找峰值★☆☆☆☆
NC110 旋转数组★☆☆☆☆
NC134 股票(无限次交易))★☆☆☆☆
NC135 股票(两次交易)★☆☆☆☆
NC130 分糖果问题★☆☆☆☆

7、尺取法

  • 尺取法算 ACM 里比较经典的一种解题技巧了,其实就是双指针,左右两个指针,不断推进右指针,然后根据限制条件来推进左指针,每个指针只会往前不会后退,所以时间复杂度是 O ( n ) O(n) O(n) 的。
题目链接难度
NC41 最长无重复子串★☆☆☆☆

8、字符串模拟

题目链接难度
NC103 反转字符串★☆☆☆☆
NC1 大数加法★☆☆☆☆
NC141 判断回文★☆☆☆☆
NC100 将字符串转化为整数★☆☆☆☆
NC20 数字字符串转化成IP地址★☆☆☆☆
NC57 反转数字★☆☆☆☆
NC89 字符串变形★☆☆☆☆
NC113 验证IP地址★☆☆☆☆

9、树状数组

  • 放入面试范畴属于较难的数据结构,因为平时也不会去关注这种数据结构,但是如果想学,其实是很简单的,它的特点是:单点更新,成段求和,并且都能在 O ( l o g 2 n ) O(log_2n) O(log2​n) 内完成,常数小于线段树,奉上链接:夜深人静写算法(十三)- 树状数组
题目链接难度
NC90 设计getMin功能的栈★★☆☆☆
NC118 数组中的逆序对★★☆☆☆
NC131 随时找到数据流的中位数★★★☆☆

10、模拟

  • 所谓模拟,就是最直白的跟着题目模拟,题目让你怎么做你就怎么做。
题目链接难度
NC112 进制转换★☆☆☆☆
NC18 顺时针旋转矩阵★☆☆☆☆
NC10 大数乘法★☆☆☆☆

11、位运算

  • 位运算相关的题一般都比较有意思,尤其是异或。
题目链接难度
NC75 数组中只出现一次的数字★★☆☆☆
NC30 数组中未出现的最小正整数★★☆☆☆