首页 > 编程语言 >66 道前端算法面试题附思路分析助你查漏补缺

66 道前端算法面试题附思路分析助你查漏补缺

时间:2024-09-07 16:21:04浏览次数:10  
标签:结点 遍历 66 面试题 节点 链表 数组 查漏 数字

题目:

输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如

序列 1,2,3,4,5 是某栈的压入顺序,序列 4,5,3,2,1 是该压栈序列对应的一个弹出序列,但 4,3,5,1,2 就不可能是该压栈序

列的弹出序列。(注意:这两个序列的长度是相等的)

思路:

我们可以使用一个辅助栈的方式来实现,首先遍历压栈顺序,依次将元素压入辅助栈中,每次压入元素后我们首先判断该元素是否与出

栈顺序中的此刻位置的元素相等,如果不相等,则将元素继续压栈,如果相等,则将辅助栈中的栈顶元素出栈,出栈后,将出栈顺序中

的位置后移一位继续比较。当压栈顺序遍历完成后,如果辅助栈不为空,则说明该出栈顺序不正确。

22. 从上往下打印二叉树

==============

题目:

从上往下打印出二叉树的每个节点,同层节点从左至右打印。

思路:

本质上是二叉树的层序遍历,可以通过队列来实现。首先将根节点入队。然后对队列进行出队操作,每次出队时,将出队元素的左右子

节点依次加入到队列中,直到队列长度变为 0 时,结束遍历。

23. 二叉搜索树的后序遍历

===============

题目:

输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出 Yes,否则输出 No。假设输入的数组的任意两

个数字都互不相同。

思路:

对于一个合法而二叉树的后序遍历来说,最末尾的元素为根元素。该元素前面的元素可以划分为两个部分,一部分为该元素的左子树,

所有元素的值比根元素小,一部分为该元素的右子树,所有的元素的值比该根元素大。并且每一部分都是一个合法的后序序列,因此我

们可以利用这些特点来递归判断。

24. 二叉树中和为某一值路径

================

题目:

输入一颗二叉树和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经

过的结点形成一条路径。

思路:

通过对树进行深度优先遍历,遍历时保存当前节点的值并判断是否和期望值相等,如果遍历到叶节点不符合要求则回退处理。

25. 复杂链表的复制

============

题目:

输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为

复制后复杂链表的 head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)

思路:

(1)第一种方式,首先对原有链表每个节点进行复制,通过 next 连接起来。然后当链表复制完成之后,再来设置每个节点的 ra

ndom 指针,这个时候每个节点的 random 的设置都需要从头结点开始遍历,因此时间的复杂度为 O(n^2)。

(2)第二种方式,首先对原有链表每个节点进行复制,并且使用 Map 以键值对的方式将原有节点和复制节点保存下来。当链表复

制完成之后,再来设置每个节点的 random 指针,这个时候我们通过 Map 中的键值关系就可以获取到对应的复制节点,因此

不必再从头结点遍历,将时间的复杂度降低为了 O(n),但是空间复杂度变为了 O(n)。这是一种以空间换时间的做法。

(3)第三种方式,首先对原有链表的每个节点进行复制,并将复制后的节点加入到原有节点的后面。当链表复制完成之后,再进行

random 指针的设置,由于每个节点后面都跟着自己的复制节点,因此我们可以很容易的获取到 random 指向对应的复制节点

。最后再将链表分离,通过这种方法我们也能够将时间复杂度降低为 O(n)。

26. 二叉搜索树与双向链表

===============

题目:

输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。

思路:

需要生成一个排序的双向列表,那么我们应该通过中序遍历的方式来调整树结构,因为只有中序遍历,返回才是一个从小到大的排序

序列。

基本的思路是我们首先从根节点开始遍历,先将左子树调整为一个双向链表,并将左子树双向链表的末尾元素的指针指向根节点,并

将根节点的左节点指向末尾节点。再将右子树调整为一个双向链表,并将右子树双向链表的首部元素的指针指向根元素,再将根节点

的右节点指向首部节点。通过对左右子树递归调整,因此来实现排序的双向链表的构建。

27. 字符串的排列

===========

题目:

输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串 abc,则打印出由字符 a,b,c 所能排列出来的所有

字符串 abc,acb,bac,bca,cab 和 cba。输入描述:输入一个字符串,长度不超过 9(可能有字符重复),字符只包括大小写字母。

思路:

我们可以把一个字符串看做是两个部分,第一部分为它的第一个字符,第二部分是它后面的所有字符。求整个字符串的一个全排列,可

以看做两步,第一步是求所有可能出现在第一个位置的字符,即把第一个字符和后面的所有字符交换。第二步就是求后面所有字符的一

个全排列。因此通过这种方式,我们可以以递归的思路来求出当前字符串的全排列。

详细资料可以参考:

《字符串的排列》

28. 数组中出现次数超过一半的数字

===================

题目:

数组中有一个数字出现的次数超过数组长度的一半。请找出这个数字。例如输入一个长度为 9 的数组{1,2,3,2,2,2,5,4,2}。由于数

字 2 在数组中出现了 5 次,超过数组长度的一半,因此输出 2。如果不存在则输出 0。

思路:

(1)对数组进行排序,排序后的中位数就是所求数字。这种方法的时间复杂度取决于我们采用的排序方法的时间复杂度,因此最快为

O(nlogn)。

(2)由于所求数字的数量超过了数组长度的一半,因此排序后的中位数就是所求数字。因此我们可以将问题简化为求一个数组的中

位数问题。其实数组并不需要全排序,只需要部分排序。我们通过利用快排中的 partition 函数来实现,我们现在数组中随

机选取一个数字,而后通过 partition 函数返回该数字在数组中的索引 index,如果 index 刚好等于 n/2,则这个数字

便是数组的中位数,也即是要求的数,如果 index 大于 n/2,则中位数肯定在 index 的左边,在左边继续寻找即可,反之

在右边寻找。这样可以只在 index 的一边寻找,而不用两边都排序,减少了一半排序时间,这种方法的时间复杂度为 O(n)。

(3)由于该数字的出现次数比所有其他数字出现次数的和还要多,因此可以考虑在遍历数组时保存两个值:一个是数组中的一个数

字,一个是次数。当遍历到下一个数字时,如果下一个数字与之前保存的数字相同,则次数加 1,如果不同,则次数减 1,如果

次数为 0,则需要保存下一个数字,并把次数设定为 1。由于我们要找的数字出现的次数比其他所有数字的出现次数之和还要大,

则要找的数字肯定是最后一次把次数设为 1 时对应的数字。该方法的时间复杂度为 O(n),空间复杂度为 O(1)。

详细资料可以参考:

《出现次数超过一半的数字》

29. 最小的 K 个数

=============

题目:

输入 n 个整数,找出其中最小的 K 个数。例如输入 4,5,1,6,2,7,3,8 这 8 个数字,则最小的 4 个数字是 1,2,3,4 。

思路:

(1)第一种思路是首先将数组排序,排序后再取最小的 k 个数。这一种方法的时间复杂度取决于我们选择的排序算法的时间复杂

度,最好的情况下为 O(nlogn)。

(2)第二种思路是由于我们只需要获得最小的 k 个数,这 k 个数不一定是按序排序的。因此我们可以使用快速排序中的 part

ition 函数来实现。每一次选择一个枢纽值,将数组分为比枢纽值大和比枢纽值小的两个部分,判断枢纽值的位置,如果该枢

纽值的位置为 k-1 的话,那么枢纽值和它前面的所有数字就是最小的 k 个数。如果枢纽值的位置小于 k-1 的话,假设枢

纽值的位置为 n-1,那么我们已经找到了前 n 小的数字了,我们就还需要到后半部分去寻找后半部分 k-n 小的值,进行划

分。当该枢纽值的位置比 k-1 大时,说明最小的 k 个值还在左半部分,我们需要继续对左半部分进行划分。这一种方法的平

均时间复杂度为 O(n)。

(3)第三种方法是维护一个容量为 k 的最大堆。对数组进行遍历时,如果堆的容量还没有达到 k ,则直接将元素加入到堆中,这

就相当于我们假设前 k 个数就是最小的 k 个数。对 k 以后的元素遍历时,我们将该元素与堆的最大值进行比较,如果比最

大值小,那么我们则将最大值与其交换,然后调整堆。如果大于等于堆的最大值,则继续向后遍历,直到数组遍历完成。这一

种方法的平均时间复杂度为 O(nlogk)。

详细资料可以参考:

《寻找最小的 k 个数》

30. 连续子数组的最大和

==============

题目:

HZ 偶尔会拿些专业问题来忽悠那些非计算机专业的同学。今天测试组开完会后,他又发话了:在古老的一维模式识别中,常常需要计

算连续子向量的最大和,当向量全为正数的时候,问题很好解决。但是,如果向量中包含负数,是否应该包含某个负数,并期望旁边的

正数会弥补它呢?例如:{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为 8(从第 0 个开始,到第 3 个为止)。你会不会被他忽悠

住?(子向量的长度至少是 1)

思路:

(1)第一种思路是直接暴力求解的方式,先以第一个数字为首往后开始叠加,叠加的过程中保存最大的值。然后再以第二个数字为首

往后开始叠加,并与先前保存的最大的值进行比较。这一种方法的时间复杂度为 O(n^2)。

(2)第二种思路是,首先我们观察一个最大和的连续数组的规律,我们可以发现,子数组一定是以正数开头的,中间包含了正负数。

因此我们可以从第一个数开始向后叠加,每次保存最大的值。叠加的值如果为负数,则将叠加值初始化为 0,因为后面的数加上负

数只会更小,因此需要寻找下一个正数开始下一个子数组的判断。一直往后判断,直到这个数组遍历完成为止,得到最大的值。

使用这一种方法的时间复杂度为 O(n)。

详细资料可以参考:

《连续子数组的最大和》

31. 整数中 1 出现的次数(待深入理解)

=======================

题目:

求出 1~13 的整数中 1 出现的次数,并算出 100~1# 300 的整数中 1 出现的次数?为此他特别数了一下 1~13 中包含 1 的数字有 1、10、11、

12、13 因此共出现 6 次,但是对于后面问题他就没辙了。ACMer 希望你们帮帮他,并把问题更加普遍化,可以很快的求出任意非负整

数区间中 1 出现的次数。

思路:

(1)第一种思路是直接遍历每个数,然后将判断每个数中 1 的个数,一直叠加。

(2)第二种思路是求出 1 出现在每位上的次数,然后进行叠加。

详细资料可以参考:

《从 1 到 n 整数中 1 出现的次数:O(logn)算法》

32. 把数组排成最小的数

==============

题目:

输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321

},则打印出这三个数字能排成的最小数字为 321323。

思路:

(1)求出数组的全排列,然后对每个排列结果进行比较。

(2)利用排序算法实现,但是比较时,比较的并不是两个元素的大小,而是两个元素正序拼接和逆序拼接的大小,如果逆序拼接的

结果更小,则交换两个元素的位置。排序结束后,数组的顺序则为最小数的排列组合顺序。

详细资料可以参考:

《把数组排成最小的数》

33. 丑数(待深入理解)

==============

题目:

把只包含质因子 2、3 和 5 的数称作丑数。例如 6、8 都是丑数,但 14 不是,因为它包含因子 7。 习惯上我们把 1 当做是第一个丑数。求

按从小到大的顺序的第 N 个丑数。

思路:

(1)判断一个数是否为丑数,可以判断该数不断除以 2,最后余数是否为 1。判断该数不断除以 3,最后余数是否为 1。判断不断除以

5,最后余数是否为 1。在不考虑时间复杂度的情况下,可以依次遍历找到第 N 个丑数。

(2)使用一个数组来保存已排序好的丑数,后面的丑数由前面生成。

34. 第一个只出现一次的字符

================

题目:

在一个字符串(1<=字符串长度<=10000,全部由大写字母组成)中找到第一个只出现一次的字符,并返回它的位置。

思路:

(1)第一种思路是,从前往后遍历每一个字符。每遍历一个字符,则将字符与后边的所有字符依次比较,判断是否含有相同字符。这

一种方法的时间复杂度为 O(n^2)。

(2)第二种思路是,首先对字符串进行一次遍历,将字符和字符出现的次数以键值对的形式存储在 Map 结构中。然后第二次遍历时

,去 Map 中获取对应字符出现的次数,找到第一个只出现一次的字符。这一种方法的时间复杂度为 O(n)。

35. 数组中的逆序对

============

题目:

在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对

的总数 P。

思路:

(1)第一种思路是直接求解的方式,顺序扫描整个数组。每扫描到一个数字的时候,逐个比较该数字和它后面的数字的大小。如果

后面的数字比它小,则这两个数字就组成了一个逆序对。假设数组中含有 n 个数字。由于每个数字都要和 O(n)个数字作比

较,因此这个算法的时间复杂度是 O(n^2)。

(2)第二种方式是使用归并排序的方式,通过利用归并排序分解后进行合并排序时,来进行逆序对的统计,这一种方法的时间复杂

度为 O(nlogn)。

详细资料可以参考:

《数组中的逆序对》

36. 两个链表的第一个公共结点

=================

题目:

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

思路:

(1)第一种方法是在第一个链表上顺序遍历每个结点,每遍历到一个结点的时候,在第二个链表上顺序遍历每个结点。如果在第二

个链表上有一个结点和第一个链表上的结点一样,说明两个链表在这个结点上重合,于是就找到了它们的公共结点。如果第一

个链表的长度为 m,第二个链表的长度为 n。这一种方法的时间复杂度是 O(mn)。

(2)第二种方式是利用栈的方式,通过观察我们可以发现两个链表的公共节点,都位于链表的尾部,以此我们可以分别使用两个栈

,依次将链表元素入栈。然后在两个栈同时将元素出栈,比较出栈的节点,最后一个相同的节点就是我们要找的公共节点。这

一种方法的时间复杂度为 O(m+n),空间复杂度为 O(m+n)。

(3)第三种方式是,首先分别遍历两个链表,得到两个链表的长度。然后得到较长的链表与较短的链表长度的差值。我们使用两个

指针来分别对两个链表进行遍历,首先将较长链表的指针移动 n 步,n 为两个链表长度的差值,然后两个指针再同时移动,

判断所指向节点是否为同一节点。这一种方法的时间复杂度为 O(m+n),相同对于上一种方法不需要额外的空间。

详细资料可以参考:

《两个链表的第一个公共结点》

37. 数字在排序数组中出现的次数

==================

题目:

统计一个数字:在排序数组中出现的次数。例如输入排序数组{ 1, 2, 3, 3, 3, 3, 4, 5}和数字 3 ,由于 3 在这个数组中出

现了 4 次,因此输出 4 。

思路:

(1)第一种方法是直接对数组顺序遍历的方式,通过这种方法来统计数字的出现次数。这种方法的时间复杂度为 O(n)。

(2)第二种方法是使用二分查找的方法,由于数组是排序好的数组,因此相同数字是排列在一起的。统计数字出现的次数,我们需要

去找到该段数字开始和结束的位置,以此来确定数字出现的次数。因此我们可以使用二分查找的方式来确定该数字的开始和结束

位置。如果我们第一次我们数组的中间值为 k ,如果 k 值比所求值大的话,那么我们下一次只需要判断前面一部分就行了,如

果 k 值比所求值小的话,那么我们下一次就只需要判断后面一部分就行了。如果 k 值等于所求值的时候,我们则需要判断该值

是否为开始位置或者结束位置。如果是开始位置,那么我们下一次需要到后半部分去寻找结束位置。如果是结束位置,那么我们

下一次需要到前半部分去寻找开始位置。如果既不是开始位置也不是结束位置,那么我们就分别到前后两个部分去寻找开始和结

束位置。这一种方法的平均时间复杂度为 O(logn)。

38. 二叉树的深度

===========

题目:

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

度。

思路:

根节点的深度等于左右深度较大值加一,因此可以通过递归遍历来实现。

39. 平衡二叉树

==========

题目:

输入一棵二叉树,判断该二叉树是否是平衡二叉树。

思路:

(1)在遍历树的每个结点的时候,调用函数得到它的左右子树的深度。如果每个结点的左右子树的深度相差都不超过 1 ,那么它

就是一棵平衡的二叉树。使用这种方法时,节点会被多次遍历,因此会造成效率不高的问题。

(2)在求一个节点的深度时,同时判断它是否平衡。如果不平衡则直接返回 -1,否则返回树高度。如果一个节点的一个子树的深

度为-1,那么就直接向上返回 -1 ,该树已经是不平衡的了。通过这种方式确保了节点只能够被访问一遍。

40. 数组中只出现一次的数字

================

题目:

一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。

思路:

(1)第一种方式是依次遍历数组,记录下数字出现的次数,从而找出两个只出现一次的数字。

(2)第二种方式,根据位运算的异或的性质,我们可以知道两个相同的数字异或等于 0,一个数和 0 异或还是它本身。由于数组中

的其他数字都是成对出现的,因此我们可以将数组中的所有数依次进行异或运算。如果只有一个数出现一次的话,那么最后剩下

的就是落单的数字。如果是两个数只出现了一次的话,那么最后剩下的就是这两个数异或的结果。这个结果中的 1 表示的是 A 和

B 不同的位。我们取异或结果的第一个 1 所在的位数,假如是第 3 位,接着通过比较第三位来将数组分为两组,相同数字一定会

被分到同一组。分组完成后再按照依次异或的思路,求得剩余数字即为两个只出现一次的数字。

41. 和为 S 的连续正数序列

=================

题目:

小明很喜欢数学,有一天他在做数学作业时,要求计算出 9~16 的和,他马上就写出了正确答案是 100。但是他并不满足于此,他在想究

竟有多少种连续的正数序列的和为 100(至少包括两个数)。没多久,他就得到另一组连续正数和为 100 的序列:18,19,20,21,22。

现在把问题交给你,你能不能也很快的找出所有和为 S 的连续正数序列?Good Luck!输出描述:输出所有和为 S 的连续正数序列。序

列内按照从小至大的顺序,序列间按照开始数字从小到大的顺序。

思路:

维护一个正数序列数组,数组中初始只含有值 1 和 2,然后从 3 依次往后遍历,每遍历到一个元素则将这个元素加入到序列数组中,然后

判断此时序列数组的和。如果序列数组的和大于所求值,则将第一个元素(最小的元素弹出)。如果序列数组的和小于所求值,则继续

往后遍历,将元素加入到序列中继续判断。当序列数组的和等于所求值时,打印出此时的正数序列,然后继续往后遍历,寻找下一个连

续序列,直到数组遍历完成终止。

详细资料可以参考:

《和为 s 的连续正数序列》

42. 和为 S 的两个数字

===============

题目:

输入一个递增排序的数组和一个数字 S,在数组中查找两个数,是的他们的和正好是 S,如果有多对数字的和等于 S,输出两个数

的乘积最小的。输出描述:对应每个测试案例,输出两个数,小的先输出。

思路:

首先我们通过规律可以发现,和相同的两个数字,两个数字的差值越大,乘积越小。因此我们只需要从数组的首尾开始找到第一对和

为 s 的数字对进行了。因此我们可以使用双指针的方式,左指针初始指向数组的第一个元素,右指针初始指向数组的最后一个元素

。然后首先判断两个指针指向的数字的和是否为 s ,如果为 s ,两个指针指向的数字就是我们需要寻找的数字对。如果两数的和

比 s 小,则将左指针向左移动一位后继续判断。如果两数的和比 s 大,则将右指针向右移动一位后继续判断。

详细资料可以参考:

《和为 S 的字符串》

43. 左旋转字符串

===========

题目:

汇编语言中有一种移位指令叫做循环左移(ROL),现在有个简单的任务,就是用字符串模拟这个指令的运算结果。对于一个给定的

字符序列 S,请你把其循环左移 K 位后的序列输出。例如,字符序列 S=”abcXYZdef”,要求输出循环左移 3 位后的结果,即 “X

YZdefabc”。是不是很简单?OK,搞定它!

思路:

字符串裁剪后拼接

44. 翻转单词顺序列

============

题目:

牛客最近来了一个新员工 Fish,每天早晨总是会拿着一本英文杂志,写些句子在本子上。同事 Cat 对 Fish 写的内容颇感兴趣,有

一天他向 Fish 借来翻看,但却读不懂它的意思。例如,“student. a am I”。后来才意识到,这家伙原来把句子单词的顺序翻转了

,正确的句子应该是“I am a student.”。Cat 对一一的翻转这些单词顺序可不在行,你能帮助他么?

思路:

通过空格将单词分隔,然后将数组反序后,重新拼接为字符串。

45. 扑克牌的顺子

===========

题目:

LL 今天心情特别好,因为他去买了一副扑克牌,发现里面居然有 2 个大王,2 个小王(一副牌原本是 54 张_)…他随机从中抽出

了 5 张牌,想测测自己的手气,看看能不能抽到顺子,如果抽到的话,他决定去买体育彩票,嘿嘿!!“红心 A,黑桃 3,小王,大王

,方片 5”,“Oh My God!”不是顺子… LL 不高兴了,他想了想,决定大\小王可以看成任何数字,并且 A 看作 1,J 为 11,

Q 为 12,K 为 13。上面的 5 张牌就可以变成“1,2,3,4,5”(大小王分别看作 2 和 4),“So Lucky!”。LL 决定去买体育彩票啦。

现在,要求你使用这幅牌模拟上面的过程,然后告诉我们 LL 的运气如何。为了方便起见,你可以认为大小王是 0。

思路:

首先判断 5 个数字是不是连续的,最直观的方法是把数组排序。值得注意的是,由于 0 可以当成任意数字,我们可以用 0 去补满数

组中的空缺。如果排序之后的数组不是连续的,即相邻的两个数字相隔若干个数字,但只要我们有足够的。可以补满这两个数字的空

缺,这个数组实际上还是连续的。

于是我们需要做 3 件事情:首先把数组排序,再统计数组中 0 的个数,最后统计排序之后的数组中相邻数字之间的空缺总数。如

果空缺的总数小于或者等于 0 的个数,那么这个数组就是连续的:反之则不连续。最后,我们还需要注意一点:如果数组中的非 0

数字重复出现,则该数组不是连续的。换成扑克牌的描述方式就是如果一副牌里含有对子,则不可能是顺子。

详细资料可以参考:

《扑克牌的顺子》

46. 圆圈中最后剩下的数字(约瑟夫环问题)

=======================

题目:

0, 1, … , n-1 这 n 个数字排成一个圈圈,从数字 0 开始每次从圆圏里删除第 m 个数字。求出这个圈圈里剩下的最后一个数

字。

思路:

(1)使用环形链表进行模拟。

(2)根据规律得出(待深入理解)

详细资料可以参考:

《圆圈中最后剩下的数字》

47. 1+2+3+…+n

================

题目:

求 1+2+3+…+n,要求不能使用乘除法、for、while、if、else、switch、case 等关键字及条件判断语句(A?B:C)。

思路:

由于不能使用循环语句,因此我们可以通过递归来实现。并且由于不能够使用条件判断运算符,我们可以利用 && 操作符的短路特

性来实现。

48. 不用加减乘除做加法

==============

题目:

写一个函数,求两个整数之和,要求在函数体内不得使用 +、-、×、÷ 四则运算符号。

思路:

通过位运算,递归来实现。

49. 把字符串转换成整数。

===============

题目:

将一个字符串转换成一个整数,要求不能使用字符串转换整数的库函数。数值为 0 或者字符串不是一个合法的数值则返回 0。输入描

述:输入一个字符串,包括数字字母符号,可以为空。输出描述:如果是合法的数值表达则返回该数字,否则返回 0。

思路:

首先需要进行符号判断,其次我们根据字符串的每位通过减 0 运算转换为整数和,依次根据位数叠加。

50. 数组中重复的数字

=============

题目:

在一个长度为 n 的数组里的所有数字都在 0 到 n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知

道每个数字重复了几次。请找出数组中任意一个重复的数字。

思路:

(1)首先将数组排序,排序后再进行判断。这一种方法的时间复杂度为 O(nlogn)。

(2)使用 Map 结构的方式,依次记录下每一个数字出现的次数,从而可以判断是否出现重复数字。这一种方法的时间复杂度为 O

(n),空间复杂度为 O(n)。

(3)从数组首部开始遍历,每遍历一个数字,则将该数字和它的下标相比较,如果数字和下标不等,则将该数字和它对应下标的值

交换。如果对应的下标值上已经是正确的值了,那么说明当前元素是一个重复数字。这一种方法相对于上一种方法来说不需要

额外的内存空间。

51. 构建乘积数组

===========

题目:

给定一个数组 A[0,1,…,n-1],请构建一个数组 B[0,1,…,n-1],其中 B 中的元素 B[i]=A[0]A[1]…*A[i-1]*A

[i+1]*…*A[n-1]。不能使用除法。

思路:

(1) C[i]=A[0]×A[1]×…×A[i-1]=C[i-1]×A[i-1]

D[i]=A[i+1]×…×A[n-1]=D[i+1]×A[i+1]

B[i]=C[i]×D[i]

将乘积分为前后两个部分,分别循环求出后,再进行相乘。

(2)上面的方法需要额外的内存空间,我们可以引入中间变量的方式,来降低空间复杂度。(待深入理解)

详细资料可以参考:

《构建乘积数组》

52. 正则表达式的匹配

=============

题目:

请实现一个函数用来匹配包括’.‘和’‘的正则表达式。模式中的字符’.‘表示任意一个字符,而’'表示它前面的字符可以出现任

意次(包含 0 次)。 在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"abaca"匹配,

但是与"aa.a"和"ab*a"均不匹配。

思路:

(1)状态机思路(待深入理解)

详细资料可以参考:

《正则表达式匹配》

53. 表示数值的字符串

=============

题目:

请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串"+100",“5e2”,“-123”,“3.1416"和”-1E-

16"都表示数值。 但是"12e",“1a3.14”,“1.2.3”,"±5"和"12e+4.3"都不是。、

思路:

利用正则表达式实现

54. 字符流中第一个不重复的字符

==================

题目:

请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符 “go” 时,第一个只出现一次

的字符是 “g” 。当从该字符流中读出前六个字符 “google” 时,第一个只出现一次的字符是 “l”。 输出描述:如果当前字符流

没有存在出现一次的字符,返回#字符。

思路:

同第 34 题

55. 链表中环的入口结点

==============

题目:

一个链表中包含环,如何找出环的入口结点?

思路:

首先使用快慢指针的方式我们可以判断链表中是否存在环,当快慢指针相遇时,说明链表中存在环。相遇点一定存在于环中,因此我

们可以从使用一个指针从这个点开始向前移动,每移动一个点,环的长度加一,当指针再次回到这个点的时候,指针走了一圈,因此

通过这个方法我们可以得到链表中的环的长度,我们将它记为 n 。

然后我们设置两个指针,首先分别指向头结点,然后将一个指针先移动 n 步,然后两个指针再同时移动,当两个指针相遇时,相遇

点就是环的入口节点。

详细资料可以参考:

《链表中环的入口结点》

《《剑指 offer》——链表中环的入口结点》

56. 删除链表中重复的结点

===============

题目:

在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。例如,链表 1->2->3-

3->4->4->5 处理后为 1->2->5

思路:

解决这个问题的第一步是确定删除的参数。当然这个函数需要输入待删除链表的头结点。头结点可能与后面的结点重复,也就是说头

结点也可能被删除,所以在链表头额外添加一个结点。

接下来我们从头遍历整个链表。如果当前结点的值与下一个结点的值相同,那么它们就是重复的结点,都可以被删除。为了保证删除

之后的链表仍然是相连的而没有中间断开,我们要把当前的前一个结点和后面值比当前结点的值要大的结点相连。我们要确保 prev

要始终与下一个没有重复的结点连接在一起。

57. 二叉树的下一个结点

==============

题目:

给定一棵二叉树和其中的一个结点,如何找出中序遍历顺序的下一个结点?树中的结点除了有两个分别指向左右子结点的指针以外,

还有一个指向父节点的指针。

思路:

这个问题我们可以分为三种情况来讨论。

第一种情况,当前节点含有右子树,这种情况下,中序遍历的下一个节点为该节点右子树的最左子节点。因此我们只要从右子节点

出发,一直沿着左子节点的指针,就能找到下一个节点。

第二种情况是,当前节点不含有右子树,并且当前节点为父节点的左子节点,这种情况下中序遍历的下一个节点为当前节点的父节

点。

第三种情况是,当前节点不含有右子树,并且当前节点为父节点的右子节点,这种情况下我们沿着父节点一直向上查找,直到找到

一个节点,该节点为父节点的左子节点。这个左子节点的父节点就是中序遍历的下一个节点。

58. 对称二叉树

==========

题目:

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

思路:

我们对一颗二叉树进行前序遍历的时候,是先访问左子节点,然后再访问右子节点。因此我们可以定义一种对称的前序遍历的方式

,就是先访问右子节点,然后再访问左子节点。通过比较两种遍历方式最后的结果是否相同,以此来判断该二叉树是否为对称二叉

树。

59. 按之字形顺序打印二叉树(待深入理解)

=======================

题目:

请实现一个函数按照之字形顺序打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右到左的顺序打印,即第一行按照

从左到右的顺序打印,第二层按照从右到左顺序打印,第三行再按照从左到右的顺序打印,其他以此类推。

思路:

按之字形顺序打印二叉树需要两个栈。我们在打印某一行结点时,把下一层的子结点保存到相应的栈里。如果当前打印的是奇数层

,则先保存左子结点再保存右子结点到一个栈里;如果当前打印的是偶数层,则先保存右子结点再保存左子结点到第二个栈里。每

一个栈遍历完成后进入下一层循环。

详细资料可以参考:

《按之字形顺序打印二叉树》

60. 从上到下按层打印二叉树,同一层结点从左至右输出。每一层输出一行。

=====================================

题目:

从上到下按层打印二叉树,同一层的结点按从左到右的顺序打印,每一层打印一行。

思路:

用一个队列来保存将要打印的结点。为了把二叉树的每一行单独打印到一行里,我们需要两个变量:一个变量表示在当前的层中还

没有打印的结点数,另一个变量表示下一次结点的数目。

61. 序列化二叉树(带深入理解)

==================

题目:

请实现两个函数,分别用来序列化和反序列化二叉树。

思路:

数组模拟

62. 二叉搜索树的第 K 个节点

==================

题目:

给定一颗二叉搜索树,请找出其中的第 k 小的结点。

思路:

对一颗树首先进行中序遍历,在遍历的同时记录已经遍历的节点数,当遍历到第 k 个节点时,这个节点即为第 k 大的节点。

63. 数据流中的中位数(待深入理解)

====================

题目:

如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有值排序之后位于中间的数值。如果数据

流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。

64. 滑动窗口中的最大值(待深入理解)

=====================

题目:

给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的

大小 3,那么一共存在 6 个滑动窗口,他们的最大值分别为{4,4,6,6,6,5}; 针对数组{2,3,4,2,6,2,5,1}的滑动窗口有以下

6 个: {[2,3,4],2,6,2,5,1}, {2,[3,4,2],6,2,5,1}, {2,3,[4,2,6],2,5,1}, {2,3,4,[2,6,2],5,1}, {2

,3,4,2,[6,2,5],1}, {2,3,4,2,6,[2,5,1]}。

思路:

使用队列的方式模拟

65. 矩阵中的路径(待深入理解)

==================

题目:

请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一个格子开始,每

一步可以在矩阵中向左,向右,向上,向下移动一个格子。如果一条路径经过了矩阵中的某一个格子,则该路径不能再进入该格子

。例如 a b c e s f c s a d e e 矩阵中包含一条字符串"bcced"的路径,但是矩阵中不包含"abcb"路径,因为字符串的

第一个字符 b 占据了矩阵中的第一行第二个格子之后,路径不能再次进入该格子。

66. 机器人的运动范围(待深入理解)

====================

题目:

地上有一个 m 行和 n 列的方格。一个机器人从坐标 0,0 的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能

进入行坐标和列坐标的数位之和大于 k 的格子。 例如,当 k 为 18 时,机器人能够进入方格(35,37),因为 3+5+3+7 = 18。但是

,它不能进入方格(35,38),因为 3+5+3+8 = 19。请问该机器人能够达到多少个格子?

剑指 offer 相关资料可以参考:

==================

  • 《剑指 offer 题目练习及思路分析》

  • 《JS 版剑指 offer》

  • 《剑指 Offer 学习心得》

推荐

标签:结点,遍历,66,面试题,节点,链表,数组,查漏,数字
From: https://blog.csdn.net/m0_57259945/article/details/141869201

相关文章

  • 美团面试题:生成字符串的不同方式
    美团面试题:生成字符串的不同方式引言问题分析动态规划思路伪代码C代码实现代码解析复杂度分析优化建议结论引言小红拿到了一个空字符串sss,她希望通过两种操作生成一个给定的字符串ttt。我们需要计算生成字符串......
  • Java 2024年详细面试题合集(持续更新)
    1.java中的数据结构数组、链表、哈希表、栈、堆、队列、树、图2.什么是跨域?跨域的三要素跨域指的是浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器施加的安全限制协议、域名、端口注意:localhost和127.0.0.1虽然都指向本机,但也属于跨域3.tomcat三......
  • 微信小程序、uniapp前端面试题
    使用微信小程序官方文档小程序配置 一、微信小程序项目结构主要有四个文件类型WXML(WeiXinMarkupLanguage)是框架设计的一套标签语言,结合基础组件、事件系统,可以构建出页面的结构。内部主要是微信自己定义的一套组件WXSS(WeiXinStyleSheets)是一套样式语言,用于描述WXM......
  • rk3566 rk3588 Android11/13 给内置APP添加相关权限,无需手动同意APP权限
    现象:打开APP会跳出权限弹窗,给APP相关权限才能够使用APP。目录1、adb查看logcat2、在SystemUIService.java内给APP添加加权限3、开机自启动APP4、executeCMD函数1、adb查看logcat打开APP,logcat会打印APP包名。我这边包名是com.jhooit.endoscope2、在SystemUIService.......
  • rk3566 android11 识别WiFi/蓝牙芯片模块有误,导致WiFi、蓝牙打不开的情况
    现象:WiFi、蓝牙驱动已安装,设备树等配置都已完成,但是WiFi/蓝牙还是打不开,要排除是否是开发板识别蓝牙WiFi芯片有误的情况。目录一、WIFI芯片识别流程二、WiFi芯片识别有误1、adb命令查看加载的WIFI芯片2、WIFI芯片对应的pidvid3、查看WiFi芯片设备和ID号4、修改默认加......
  • SSM相关面试题
    1Spring1.1什么是SpringIOC和DI?①控制反转(IOC):Spring容器使用了工厂模式为我们创建了所需要的对象,我们使用时不需要自己去创建,直接调用Spring为我们提供的对象即可,这就是控制反转的思想。② 依赖注入(DI):Spring使用JavaBean对象的Set方法或者构造方法为我们在创建......
  • 微服务相关面试题
     1Springboot1.1讲一讲SpringBoot自动装配的原理在SpringBoot项目中的引导类上有一个注解@SpringBootApplication,这个注解是对三个注解进行了封装,分别是: @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan其中@EnableAutoConfiguration是实现自......
  • Java基础面试题:概述
    花了一周时间收集大量Java经典面试题目......
  • P6638 「JYLOI Round 1」常规 题解
    题目传送门前置知识可持久化线段树|前缀和&差分解法进行差分,区间查询转化成前缀和相减。先将\(\{a\}\)升序排序。设当前询问的区间为\([1,r]\),在\(\{a\}\)中找到一个最大的\(pos\)使得\(a_{pos}\ler\),则\([1,r]\)中所做常规的次数为\(\sum\limits_{i=1......
  • 70%的人都答错了的面试题,vue3的ref是如何实现响应式的?
    本文将通过debug的方式带你搞清楚当ref接收的是对象和原始类型时,分别是如何实现响应式的。注:本文中使用的vue版本为3.4.19。看个demo还是老套路,我们来搞个demo,index.vue文件代码如下:<template><div><p>count的值为:{{count}}</p><p>user.count的值为:{{use......