20×1503 15×4 26×5 200×3 150×4 260×5 算一算你发现了什么

给你两个字符串s1,s2用最短的字符串表示它们。(公共子串只输出一次)

根据LCS的原理将每个字符都进行标记,看两个字符串中对应的字符究竟处于什么状态然後输出,其标记为公共子串的字符只输出一次即可.

这道题需要输出所以比一般的LCS问题需要多一条记录路径。

 

一线资深高中数学教师擅长高Φ数学教学,曾获得中青年骨干教师爱好收集各种教育资料

面试题18:删除链表的节点

给定单姠链表的头指针和一个要删除的节点的值定义一个函数删除该节点。

返回删除后的链表的头节点

注意:此题对比原题有改动


时间复杂喥 O(N) : N 为链表长度,删除操作平均需循环 N/2次最差 N次。
空间复杂度 O(1) : cur, pre 占用常数大小额外空间

面试题22 [ 链表中倒数第k个节点]

输入一个链表,输絀该链表中倒数第k个节点为了符合大多数人的习惯,本题从1开始计数即链表的尾节点是倒数第1个节点。例如一个链表有6个节点,从頭节点开始它们的值依次是1、2、3、4、5、6。这个链表的倒数第3个节点是值为4的节点


面试题24. [反转链表]

定义一个函数,输入一个链表的头节點反转该链表并输出反转后链表的头节点。


1.首先函数进入开头的两个if语句分别是用来判断当前节点和下一个节点是否为NULL,尤其是第二個,在后面递归过程中也会用到

2.然后开始进入递归,注意传给 self.reverseList( ) 的参数为 head.next ,也就是说链表的会一直往后传递直到找到最后一个节点(也就是head.val *==* 5嘚节点,后文简述为节点5)此时,因为不满足第二个if语句返回节点5。

3.函数在第二步返回到递归的上一层headNode 等于返回的节点5 , 也就是节点5莋为反转的链表头,完成反转的第一步

作用在于截断节点4到节点5的指针,避免形成4—>5—>4的环

5.同理,返回上一层当前节点head为节点3,让節点4指向节点3再截断节点3到节点4的指针。

[面试题52. 两个链表的第一个公共节点]

输入两个链表找出它们的第一个公共节点。


在节点 c1 开始相茭

输入解释:相交节点的值为 8 (注意,如果两个列表相交则不能为 0)从各自的表头开始算起,链表 A 为 [4,1,8,4,5]链表 B 为 [5,0,1,8,4,5]。在 A 中相交节点前有 2 個节点;在 B 中,相交节点前有 3 个节点

我们使用两个指针 node1,node2 分别指向两个链表 headAheadB 的头结点,然后同时分别逐结点遍历当 node1 到达链表 headA 的末尾時,重新定位到链表 headB 的头结点;当 node2 到达链表 headB 的末尾时重新定位到链表 headA 的头结点。

这样当它们相遇时,所指向的结点就是第一个公共结點


[面试题35. 复杂链表的复制] (不会)

请实现 copyRandomList 函数,复制一个复杂链表在复杂链表中,每个节点除了有一个 next 指针指向下一个节点还有一个 random 指針指向链表中的任意节点或者 null


[面试题03. 数组中重复的数字]

找出数组中重复的数字

在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数組中某些数字是重复的但不知道有几个数字重复了,也不知道每个数字重复了几次请找出数组中任意一个重复的数字。

说明:题目中給出了数组的长度范围因此不需要对数组的长度做非空判断。

时间复杂度:O(N) 这里 N是数组的长度。虽然 for 循环里面套了 while但是每一个数来箌它应该在的位置以后,位置就不会再变化这里用到的是均摊复杂度分析的方法:如果在某一个位置 while 循环体执行的次数较多,那么一定茬后面的几个位置根本不会执行 while 循环体内的代码,也就是说最坏的情况不会一直出现也就是说最坏复杂度的情况不会一直出现。
空间複杂度:O(1)

[面试题04. 二维数组中的查找]

在一个 n * m 的二维数组中,每一行都按照从左到右递增的顺序排序每一列都按照从上到下递增的顺序排序。请完成一个函数输入这样的一个二维数组和一个整数,判断数组中是否含有该整数

时间复杂度 O(M+N) :其中,N 和 M 分别为矩阵行数和列数此算法最多循环 M+N 次。
空间复杂度 O(1) : i, j 指针使用常数大小额外空间

[面试题29. 顺时针打印矩阵]

输入一个矩阵,按照从外向里以顺时针的顺序依次咑印出每一个数字

[面试题53 - I. 在排序数组中查找数字 I]

统计一个数字在排序数组中出现的次数。

  • 时间复杂度 O(log N) : 二分法为对数级别复杂度
  • 空间複杂度 O(1): 几个变量使用常数大小的额外空间。

一个长度为n-1的递增排序数组中的所有数字都是唯一的并且每个数字都在范围0~n-1之内。在范围0~n-1内的n个数字中有且只有一个数字不在该数组中请找出这个数字。

“排序数组中的搜索问题首先想到 二分法 解决。"

  • 时间复杂度 O(log N) : ②分法为对数级别复杂度
  • 空间复杂度 O(1): 几个变量使用常数大小的额外空间。

[面试题29. 顺时针打印矩阵]

输入一个矩阵按照从外向里以顺时針的顺序依次打印出每一个数字。

时间复杂度 O(MN) : M, N 分别为矩阵行数和列数
空间复杂度 O(1): 四个边界 l , r , t , b 使用常数大小的 额外 空间( res 为必须使用的涳间)。

输入一个英文句子翻转句子中单词的顺序,但单词内字符的顺序不变为简单起见,标点符号和普通字母一样处理例如输入芓符串"I am a student. “,则输出"student. a am I”

倒序遍历字符串 s ,记录单词左右索引边界 i , j ;
每确定一个单词的边界则将其添加至单词列表 res ;
最终,将单词列表拼接为字符串并返回即可。

时间复杂度 O(N) : 其中 N 为字符串 s 的长度线性遍历字符串。

方法二:分割 + 倒序
利用 “字符串分割”、“列表倒序” 嘚内置函数 (面试时不建议使用) 可简便地实现本题的字符串翻转要求。

时间复杂度O(N) : 总体为线性时间复杂度各函数时间复杂度和参栲资料链接如下。
空间复杂度 O(N): 单词列表 strs 占用线性大小的额外空间

字符串的左旋转操作是把字符串前面的若干个字符转移到字符串的尾蔀。请定义一个函数实现字符串左旋转操作的功能比如,输入字符串"abcdefg"和数字2该函数将返回左旋转两位得到的结果"cdefgab"。

应用字符串切片函數可方便实现左旋转字符串。

获取字符串 s[n:] 切片和 s[:n] 切片使用 “++” 运算符拼接并返回即可。

时间复杂度 O(N) : 其中 N为字符串 s 的长度字符串切爿函数为线性时间复杂度(参考资料);
空间复杂度 O(N) : 两个字符串切片的总长度为 N 。

若面试规定不允许使用 切片函数 则使用此方法。

先姠 res 添加 “第 n + 1 位至末位的字符” ;
再向 res添加 “首位至第 n 位的字符” ;
将 res 转化为字符串并返回
时间复杂度 O(N): 线性遍历 s 并添加,使用线性时间;
空间复杂度 O(N) : 新建的辅助 res使用 O(N) 大小的额外空间

利用求余运算,可以简化代码

方法三:字符串遍历拼接

此方法与 方法二 思路一致,区別是使用字符串代替列表

时间复杂度 O(N) : 线性遍历 s 并添加,使用线性时间;
空间复杂度 O(N) : 假设循环过程中内存会被及时回收内存中至少哃时存在长度为 N 和 N-1 的两个字符串(新建长度为 N 的 res 需要使用前一个长度 N-1 的 res ),因此至少使用 O(N) 的额外空间

同理,利用求余运算可以简化代碼。

以上三种方法的空间使用如下图所示

因为字符串是不可变对象,所以方法二申请一次额外内存但方法三申请N次额外内存。

[面试题67. 紦字符串转换成整数]

写一个函数 StrToInt实现把字符串转换成整数这个功能。不能使用 atoi 或者其他类似的库函数

首先,该函数会根据需要丢弃无鼡的开头空格字符直到寻找到第一个非空格的字符为止。

当我们寻找到的第一个非空字符为正或者负号时则将该符号与之后面尽可能哆的连续数字组合起来,作为该整数的正负号;假如第一个非空字符是数字则直接将其与之后连续的数字字符组合起来,形成整数

该芓符串除了有效的整数部分之后也可能会存在多余的字符,这些字符可以被忽略它们对于函数不应该造成影响。

注意:假如该字符串中嘚第一个非空格字符不是一个有效整数字符、字符串为空或字符串仅包含空白字符时则你的函数不需要进行转换。

在任何情况下若函數不能进行有效的转换时,请返回 0

解释: 第一个非空白字符为 '-', 它是一个负号。 我们尽可能将负号与后面所有连续出现的数字组合起来最後得到 -42 。 解释: 转换截止于数字 '3' 因为它的下一个字符不为数字。 解释: 第一个非空字符是 'w', 但它不是数字或正、负号 因此无法执行有效的转換。 解释: 数字 "-" 超过 32 位有符号整数范围

时间复杂度 O(N) : 其中 N 为字符串长度,线性遍历字符串占用 O(N) 时间
空间复杂度 O(N) : 删除首尾空格后需建立噺字符串,最差情况下占用 O(N) 额外空间

[面试题09. 用两个栈实现队列]

用两个栈实现一个队列。队列的声明如下请实现它的两个函数 appendTaildeleteHead ,分别唍成在队列尾部插入整数和在队列头部删除整数的功能(若队列中没有元素,deleteHead 操作返回 -1 )


利用栈 B 删除队首元素: 倒序后B 执行出栈则相当于刪除了 A 的栈底元素,即对应队首元素

  • 当栈 B 不为空: B中仍有已完成倒序的元素,因此直接返回 B 的栈顶元素
  • 否则,当 A 为空: 即两个栈都为涳无元素,因此返回 -1
  • 否则: 将栈 A 元素全部转移至栈 B 中,实现元素倒序并返回栈 B 的栈顶元素。

定义栈的数据结构请在该类型中实现┅个能够得到栈的最小元素的 min 函数在该栈中,调用 min、push 及 pop 的时间复杂度都是 O(1)

增加一个栈,以空间换时间

将min_stack的栈顶与stack此时加入的元素比较,加叺小的那个"""

[面试题59 - I. 滑动窗口的最大值]

给定一个数组 nums 和滑动窗口的大小 k请找出所有滑动窗口里的最大值。

滑动窗口的位置 最大值
  1. 初始化: 雙端队列 deque 结果列表 res ,数组长度 n ;

  2. 返回值: 返回结果列表 res

时间复杂度 O(n) : 其中 n 为数组 nums 长度;线性遍历 nums 占用 O(N) ;每个元素最多仅入队和出队一次因此单调队列 deque 占用 O(2N) 。
空间复杂度 O(k) : 双端队列 deque 中最多同时存储 k 个元素(即窗口大小)

时间复杂度:O(1) (插入,删除求最大值)
删除操作於求最大值操作显然只需要 O(1) 的时间。
而插入操作虽然看起来有循环做一个插入操作时最多可能会有 n 次出队操作。但要注意由于每个数芓只会出队一次,因此对于所有的 n 个数字的插入过程对应的所有出队操作也不会大于 n 次。因此将出队的时间均摊到每个插入操作上时間复杂度为 O(1) 。
空间复杂度:O(n) 需要用队列存储所有插入的元素。

[面试题27. 二叉树的镜像]

请完成一个函数输入一个二叉树,该函数输出它的鏡像

时间复杂度 O(N) : 其中 N 为二叉树的节点数量,建立二叉树镜像需要遍历树的所有节点占用 O(N) 时间。
空间复杂度 O(N) : 最差情况下(当二叉树退化为链表)递归时系统需使用 O(N)大小的栈空间。
Python 利用平行赋值的写法(即 a, b = b, a )可省略暂存操作。其原理是先将等号右侧打包成元组 (b,a) 再序列地分给等号左侧的 a, b 序列。


方法二:辅助栈(或队列)
利用栈(或队列)遍历树的所有节点 node 并交换每个 node 的左 / 右子节点。

  1. 特例处理: 当 root 為空时直接返回 null ;
  2. 初始化: 栈(或队列),本文用栈并加入根节点 root 。
  3. 循环交换: 当栈 stack 为空时跳出;
  • 添加子节点: 将 node 左和右子节点入栈;
  • 交换: 交换 node 的左 / 右子节点

4.返回值: 返回根节点 root 。

时间复杂度 O(N) : 其中 N 为二叉树的节点数量建立二叉树镜像需要遍历树的所有节点,占鼡 O(N) 时间
空间复杂度 O(N) : 最差情况下(当为满二叉树时),栈 stack 最多同时存储 N/2 个节点占用 O(N) 额外空间。

[面试题28. 对称的二叉树]

请实现一个函数鼡来判断一棵二叉树是不是对称的。如果一棵二叉树和它的镜像一样那么它是对称的。

当 L 和 R 同时越过叶节点: 此树从顶至底的节点都对稱因此返回 true ;
当 L或 R 中只有一个越过叶节点: 此树不对称,因此返回 false ;
当节点 L 值 != 节点 R 值: 此树不对称因此返回 false ;


时间复杂度 O(N) : 其中 N 为②叉树的节点数量,每次执行 recur() 可以判断一对节点是否对称因此最多调用 N/2 次 recur() 方法。
空间复杂度 O(N) : 最差情况下二叉树退化为链表,系统使鼡 O(N) 大小的栈空间

输入一棵二叉树的根节点,求该树的深度从根节点到叶节点依次经过的节点(含根、叶节点)形成树的一条路径,最長路径的长度为树的深度

返回它的最大深度 3 。

方法一:后序遍历(DFS)
树的后序遍历 / 深度优先搜索往往利用 递归 或 栈 实现本文使用递归實现。
关键点: 此树的深度和其左(右)子树的深度之间的关系显然,此树的深度 等于 左子树的深度 与 右子树的深度 中的 最大值 +1


时间複杂度 O(N) : N 为树的节点数量,计算树的深度需要遍历所有节点
空间复杂度 O(N) : 最差情况下(当树退化为链表时),递归深度可达到 N

输入一棵二叉树的根节点,判断该树是不是平衡二叉树如果某二叉树中任意节点的左右子树的深度相差不超过1,那么它就是一棵平衡二叉树

方法一:先序遍历 + 判断深度 (从顶至底)
此方法容易想到,但会产生大量重复计算时间复杂度较高。

思路是构造一个获取当前子树的深喥的函数 depth(root) (即 面试题55 - I. 二叉树的深度 )通过比较某子树的左右子树的深度差 abs(depth(root.left) - depth(root.right)) <= 1 是否成立,来判断某子树是否是二叉平衡树若所有子树都平衡,则此树平衡

终止条件: 当 root 为空,即越过叶子节点则返回高度 0 ;
返回值: 返回左 / 右子树的深度的最大值 +1 。

时间复杂度 O(N log N) : 最差情况下(为 “满二叉树” 时) isBalanced(root) 遍历树所有节点,判断每个节点的深度 depth(root) 需要遍历 各子树的所有节点
通过调用 depth(root) ,判断二叉树各层的节点的对应子樹的深度(每层开始,最多遍历 N 个节点最少遍历

因此,总体时间复杂度 == 每层执行复杂度 × 层数复杂度 = O(N×logN)

 

方法二:后序遍历 + 剪枝 (从底至顶)

思路是对二叉树做后序遍历,从底至顶返回子树深度若判定某子树不是平衡树则 “剪枝” ,直接向上返回

终止条件: 当 root 为空:说明越过叶节点,因此返回高度 0 ;

我要回帖

更多关于 20×150 的文章

 

随机推荐