剑指Offer
剑指Offer第二版题目及代码。
王劭阳
这个作者很懒,什么都没留下…
展开
-
面试题1:赋值运算符函数
题目:如下为类型CMyString的声明,请为该类型添加赋值运算符函数。class CMyString{public: CMyString(char* pData = nullptr); CMyString(const CMyString& str); ~CMyString(void);private: char* m_pData;};...原创 2020-01-26 16:05:22 · 234 阅读 · 0 评论 -
面试题2:实现Singleton模式
题目:设计一个类,我们只能生成该类的一个实例。分析:只能生成一个实例的类是实现了单例模式的类,这在面试过程中会经常碰到。解法:不好的解法一:只适用于单线程环境把构造函数设置为私有函数,以禁止他人创建实例,定义一个静态实例,在需要的时候创建该实例。package com.wsy;public class Singleton { private Singlet...原创 2020-01-26 17:28:59 · 126 阅读 · 0 评论 -
面试题3:数组中重复的数字
题目:找出数组中重复的数字在一个长度为n的数组里的所有数字都在0~n-1的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是重复的数字2或者3。分析:一个简单的方法,把输入的数组排序,从排序后的数组中查找重复的数字,就非常容易了,只需要从头...原创 2020-01-27 11:07:34 · 187 阅读 · 0 评论 -
面试题4:二维数组中的查找
题目:在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。分析:分3种情况来分析查找过程:当前位置等于要查找的数字,查找结束; 当前位置小于要查找的数字,根据数据排序规则,要查找的数字位于当前位置的右边或下边; 当前位置大于要查找的数字,根据数据排序规则,要查找的数...原创 2020-01-27 21:13:06 · 166 阅读 · 0 评论 -
面试题5:替换空格
题目:请实现一个函数,把字符串中的每个空格替换成“%20”。例如,输入“We are happy.”,则输出“We%20are%20happy.”。分析:在网络编程中,如果URL参数中包含特殊字符,如空格、‘#’等,可能导致服务端无法获取正确的参数值,于是我们需要将这些特殊符号转换成服务端可以识别的字符。转换规则是在‘%’后跟上ASCII码的两位十六进制的表示。比如空格的ASCII码是...原创 2020-01-28 00:12:57 · 230 阅读 · 0 评论 -
面试题6:从尾到头打印链表
题目:输入一个链表的头结点,从尾到头反过来打印出每个结点的值。分析:比较容易想到的方法是链表的逆置,再遍历输出。此时,对于链表的结构能否改变,取决于面试官的要求,如果面试官允许修改,那么可以采用链表逆置再输出的方式解决。这里假设不能修改链表的结构。遍历顺序和输出顺序是反向的,可以利用栈这个数据结构,进而可以联想到递归,不过递归有栈深度限制,递归太深就栈溢出了。解法:pac...原创 2020-01-28 14:21:30 · 149 阅读 · 0 评论 -
面试题7:重建二叉树
题目:输入某二叉树的前序遍历和中序遍历的结果,请重建该二叉树。分析:在二叉树的谦虚遍历序列中,第一个数字总是树的根节点的值。但在中序遍历序列中,根节点的值在序列的中间,左子树的结点的值位于根节点的值的左边,右子树的结点的值位于根节点的值的右边。因此,我们需要扫描中序遍历序列找到根节点,将中序遍历划分为两部分,根节点左侧的部分是根节点的左子树,根节点右侧的部分是根节点的右子树。其中的左子...原创 2020-01-28 23:00:34 · 90 阅读 · 0 评论 -
面试题8:二叉树的下一个结点
题目:给定一棵二叉树和其中的一个结点,如果找出中序遍历序列的下一个结点?树中的结点除了两个分别指向左、右子结点的指针,还有一个指向父结点的指针。分析:以这棵树为例,它的中序遍历序列是{d,b,h,e,i,a,f,c,g}。树中从父结点到子结点的指针用实线表示,从子结点到父结点的指针用虚线表示。下面观察a,f,i这三个结点,它们分别对应3种不同的情况。a结点:它的下一个结点是f。...原创 2020-01-29 12:07:21 · 256 阅读 · 0 评论 -
面试题9:用两个栈实现队列
题目:用两个栈实现一个队列。实现两个函数appendTail()和deleteHead(),分别完成在队列尾部插入结点和队列头部删除结点的功能。分析:根据栈和队列的性质,我们知道,栈是后进先出,队列的先进先出。现在我们要用两个栈来实现队列。假设有a,b,c三个元素入队列,依次将它们放入stack1,此时栈顶是c,栈底是a,现在需要执行队列的出队操作,按照先进先出的原则,第一个出队的...原创 2020-01-29 15:58:09 · 160 阅读 · 0 评论 -
面试题10:斐波那契数列
题目:求斐波那契数列的第n项。写一个函数,输入n,求斐波那契(Fibonacci)数列的第n项。分析:根据斐波那契数列的定义,最容易想到的解法就是递归,但是递归的效率很慢,因为存在大量的重复计算问题。继而可以想到递推公式。解法:效率很低的解法,挑剔的面试官不会喜欢package com.wsy;public class Main { public st...原创 2020-01-30 10:36:14 · 418 阅读 · 0 评论 -
面试题11:旋转数组的最小数字
题目:把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如,数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。分析:最直观的解法就是:遍历一遍数组,即可找到最小的元素,此时时间复杂度是O(n)。但是旋转数组的概念并没有用上,这肯定不是面试官想要的方案。我们注意到旋转之后的数组...原创 2020-01-30 18:42:18 · 104 阅读 · 0 评论 -
面试题12:矩阵中的路径
题目:请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一格开始,每一步可以在矩阵中向左、右、上、下移动一格。如果一条路径经过了矩阵的某一格,那么该路径不能再次进入该格子。例如,在下面的3×4的矩阵中包含一条字符串“bfce”的路径(路径中的字母用下划线标出)。但矩阵中不包含字符串“abfb”的路径,因为字符串的第一个字符b占据了矩阵中的第一行第...原创 2020-01-31 12:35:26 · 117 阅读 · 0 评论 -
面试题13:机器人的运动范围
题目:地上有一个m行n列的方格。一个机器人从坐标(0,0)的格子开始移动,它每次可以向左、右、上、下移动一格,但不能进入行坐标和列坐标的数位之和大于k的格子。例如,当k为18时,机器人能够进入方格(35,37),因为3+5+3+7=18。但它不能进入方格(35,38),因为3+5+3+8=19,请问该机器人能够到达多少个格子?分析:和前一题有些类似,将m×n的方格看成一个m×n的矩阵。...原创 2020-01-31 14:13:07 · 134 阅读 · 0 评论 -
面试题14:剪绳子
题目:给你一根长度为n的绳子,请把绳子剪成m段(m、n都是整数,n>1并且m>1),每段绳子的长度记为k[0],k[1],…,k[m]。请问k[0]×k[1]×…×k[m]可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到最大的乘积是18。分析:这个问题有两种不同的方法。一个是需要O(n²)时间和O(n)空间的动态规划,一个是需...原创 2020-02-01 12:05:13 · 213 阅读 · 0 评论 -
面试题15:二进制中1的个数
题目:请实现一个函数,输入一个整数,输出该整数的二进制表示中1的个数。例如,把9表示成二进制是1001,有2个是1。因此,如果输入9,则该哈数输出2。如果输入负数,则统计该数字二进制补码中的1的个数。分析:可能引起死循环的解法:先判断二进制中最右边一位是不是1;接着把输入的整数右移一位,此时原来处于从右边数起第二位被移到最右边了,再判断是不是1;这样每移动一位,直到整个整数变成0为...原创 2020-02-01 14:12:33 · 139 阅读 · 0 评论 -
面试题16:数值的整数次方
题目:实现函数double power(double base, int exponent),求base的exponent次方。不得使用库函数,同时不需要考虑大数问题。分析:自以为题目简单的解法:看起来很简单,做一个循环,将base乘以exponent次即可。很明显,这样的考虑不全面。如果exponent是0或者负数呢?全面但不够高效的解法,我们离Offer已经很近了:如果...原创 2020-02-01 17:04:06 · 107 阅读 · 0 评论 -
面试题17:打印从1到最大的n位数
题目:输入数字n,按照顺序打印出从1到最大的n位数的十进制数。比如输入3,则打印出1、2、3一直到最大的3位数999。分析:跳进面试官陷阱:题目看起来很简单,一个循环,一个输出,就可以做到,但是题目的陷阱在于:当n比较大的时候,可能超过数据类型的范围。需要用字符串模拟才行。在字符串上模拟数字加法的解法,绕过陷阱才能拿到Offer:这里既然要用到大数,就用字符数组模拟大数了。...原创 2020-02-02 08:58:19 · 222 阅读 · 0 评论 -
面试题18:删除链表的结点
题目:在O(1)时间内删除链表结点。给定单向链表的头指针和一个结点指针,定义一个函数在O(1)时间内删除该节点。分析:在单向链表中删除结点,常规的做法是从链表的头结点开始,顺序遍历查找要删除结点的前一个结点,因为要让prev的next指向要删除结点的next结点,再删除掉这个要删除的结点。此时的时间复杂度是O(n)。怎么实现O(1)时间删除链表结点呢?已知要删除结点,我们可以轻...原创 2020-02-02 16:09:26 · 161 阅读 · 0 评论 -
面试题19:正则表达式匹配
题目:请实现一个函数用来匹配包含'.'和'*'的正则表达式。模式中的字符'.'表示任意一个字符,而'*'表示它前面的字符可以出现任意次(含0次)。匹配是指字符串的所有字符匹配整个模式。例如,字符串“aaa”与模式“a.a”和“ab*ac*a”匹配,但与“aa.a”和“ab*a”均不匹配。分析:每次从字符串里拿出一个字符和模式中的字符去匹配。当字符串中的字符和模式中的字符相匹配时,接着匹...原创 2020-02-03 21:57:19 · 149 阅读 · 0 评论 -
面试题20:表示数值的字符串
题目:请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串“+100”、“5e2”、“-123”、“3.1416”及“-1E-16”都表示数值,但“12e”、“1a3.14”、“1.2.3”、“+-5”及“12e+5.4”都不是。分析:表示数值的字符串遵循模式A[.[B]][e|EC]或者.B[e|EC],其中A为数值的整数部分,B紧跟着小数点为数值的小数部分,C紧...原创 2020-02-04 22:08:22 · 126 阅读 · 0 评论 -
面试题21:调整数组顺序使奇数位于偶数前面
题目:输入一个整数数组,实现一个函数来调整数组中数字的顺序,使得所有奇数位于数组的前半部分,所有偶数位于数组的后半部分。分析:只完成基本功能的解法,仅适用于初级程序员要求把奇数放在数组的前半部分,偶数放在数字的后半部分,因此所有奇数都位于偶数前面。当我们在扫描这个数组的时候,如果发现偶数出现在奇数前面,则交换它们的顺序,交换之后就符合了。因此,可以维护两个指针:第一个指针初始化...原创 2020-02-05 09:44:43 · 186 阅读 · 0 评论 -
面试题22:链表中倒数第k个结点
题目:输入一个链表,输出该链表中倒数第k个结点。分析:如果是双向链表,可以遍历到最后一个,再通过prev指针找到倒数第k个。如果是单向链表,访问倒数第k个,需要遍历n-k+1个结点,但是获取n的时候,需要遍历一遍链表,这不是最佳的解决方案。为了实现只遍历链表一次就能找到倒数第k个结点,我们可以定义两个指针,第一个指针从链表的头指针开始遍历走k-1步,第二个指针保持不动,从第k步...原创 2020-02-05 10:59:51 · 143 阅读 · 0 评论 -
面试题23:链表中环的入口结点
题目:如果一个链表中包含环,如何找出环的入口结点?如下图,环的入口结点是3。分析:确定链表中是否有环:可以使用快慢指针来验证。快慢指针同时从链表头出发,快速指针每次走两步,慢速指针每次走一步。如果快速指针追上了慢速指针,那么就存在环,如果快速指针走到了链表的末尾都没有追上慢速指针,那么就不包含环。统计环中结点的个数:从快慢指针相遇的位置继续遍历,再次回到这个位置的时候,...原创 2020-02-05 12:56:14 · 88 阅读 · 0 评论 -
面试题24:反转链表
题目:定义一个函数,输入一个链表的头结点,反转该链表并输出反转后链表的头结点。分析:原地逆置链表,需要操作3个链表的关系。假设i->j->k,当把j指向i的时候,就断开了j->k的指针,为了还能找到k,就需要在断开之前,将k保存下来。解法:package com.wsy;class Node { private int value; pr...原创 2020-02-05 13:59:12 · 110 阅读 · 0 评论 -
面试题25:合并两个排序的链表
题目:输入两个递增排序的链表,合并这两个链表并使新链表中的结点仍然是递增排序的。分析:定义两个指针,分别指向这两个链表的第一个结点。当第一个结点的值小于第二个结点的值时,链表1的头结点将是合并后的链表的头结点。继续比较两个头结点,如果第二个结点的值小,链表2的头结点将是合并剩余结点得到的链表的头结点。解法:package com.wsy;class Node { ...原创 2020-02-05 15:21:01 · 223 阅读 · 0 评论 -
面试题26:树的子结构
题目:输入两棵二叉树A和B,判断B是不是A的子结构,比如下图,左边是A树,右边是B树。分析:第一步,在树A中找到和树B的根节点的值应用的结点R。这一步可以通过树的遍历来查找。可以采用递归或者循环的方式遍历。第二步,判断树A中以R为根节点的子数是不是包含和树B应用的结构。这里也采用递归的方式进行检测,递归的结束条件是A树或者B树到达了叶节点,在对比的过程中,如果A树达到了...原创 2020-02-05 18:06:36 · 96 阅读 · 0 评论 -
面试题27:二叉树的镜像
题目:请完成一个函数,输入一棵二叉树,该函数输出它的镜像。分析:两棵互为镜像的树,它们的根节点相同,左右两个子结点交换位置。对于这两个子结点来说,它们的两个子结点也交换了位置,这就是存在一个递归的关系。递归结束的条件是:结点为空或者结点为叶节点。解法:package com.wsy;class Tree { private int value; pri...原创 2020-02-06 09:05:25 · 88 阅读 · 0 评论 -
面试题28:对称的二叉树
题目:请实现一个函数,用来判断一棵二叉树是不是对称的。如果一棵二叉树和它的镜像一样,那么它是对称的。分析:通常,我们有3种不同的二叉树遍历算法,即前序、中序、后序。这三种遍历中,都是先左后右的方式。对于前序遍历,如果我们采用根右左的方式输出序列,和采用根左右的方式输出序列,对于一棵对称的满二叉树来说,这两个序列是一样的。可是,对于非满二叉树来说,就不一定了。或者对于整棵树中结点值相...原创 2020-02-06 10:28:04 · 182 阅读 · 0 评论 -
面试题29:顺时针打印矩阵
题目:输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字。分析:这里没有涉及复杂的数据结构或者算法,但是需要写多个循环,需要判断多个边界条件。需要注意的是,最后一圈可能退化成只有一行、只有一列,甚至只有一个数字。分析一圈:第一步总是需要的,因为至少有一步。如果只有一行,就不需要第二步了。需要第二步的前提条件是终止行号大于起始行号。需要第三步打印的前提条件是圈内至少有两...原创 2020-02-06 22:45:08 · 120 阅读 · 0 评论 -
面试题30:包含min函数的栈
题目:定义栈的数据结构,请在该类型中实现一个能够得到栈的最小元素的min函数。在该栈中,调用min、push及pop的时间复杂度是O(1)。分析:我们需要一个辅助栈,每次向栈中放数据的时候,同时向辅助栈中放当前栈中的min元素,同时,在弹出栈的时候,也在辅助栈中将当前的min元素弹出,这样可以获取到弹出栈后的次小元素。解法:package com.wsy;import j...原创 2020-02-07 10:10:33 · 88 阅读 · 0 评论 -
面试题31:栈的压入、弹出序列
题目:输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如,序列{1,2,3,4,5}是某栈的压栈序列,序列{4,53,2,1}是该压栈序列对应的一个弹出序列,但{4,3,5,1,2}就不可能是该压栈序列的弹出序列。分析:以弹出序列{4,5,3,2,1}为例分析压栈和弹出的过程。第一个希望被弹出的数字是4,因此4需要先压...原创 2020-02-07 15:39:56 · 238 阅读 · 0 评论 -
面试题32:从上到下打印二叉树
题目:不分行从上到下打印二叉树。从上到下打印出二叉树的每个结点,同一层的结点按照从左到右的顺序打印。分析:这里就是按照层次遍历,我们需要一个队列来辅助完成遍历。每次打印一个结点的时候,如果该结点有子结点,则把该结点的子结点放到一个队列的末尾。接下来到队列的头部取出最早进入队列的结点,重复前面的打印操作,直至队列中所有的结点都被打印出来。解法:package com.w...原创 2020-02-07 21:04:23 · 173 阅读 · 0 评论 -
面试题33:二叉搜索树的后续遍历序列
题目:输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历结果。如果是则返回true,否则返回false。假设输入的数组的任意两个数字都互不相同。例如,输入数组{5,7,6,9,11,10,8},则返回true,因为这个整数序列是二叉搜索树的后序遍历结果。如果输入的数组是{7,4,6,5},则由于没有哪棵二叉搜索树的后序遍历结果是这个序列,因此返回false。分析:在后序遍历得到的...原创 2020-02-08 17:40:37 · 137 阅读 · 0 评论 -
面试题34:二叉树中和为某一值的路径
题目:输入一棵二叉树和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。从树的根节点开始往下一直到叶节点所经过的结点形成的一条路径。分析:使用前序遍历的方式访问某一结点时,把该结点添加到路径上,并累加该结点的值。如果该结点为叶结点,并且路径中结点值的和刚好等于输入的整数,当前路径符合要求,我们把它打印出来。如果当前结点不是叶结点,则继续访问它的子结点。当前结点访问结束后,递归函数...原创 2020-02-09 10:17:14 · 95 阅读 · 0 评论 -
面试题35:复杂链表的复制
题目:请实现函数ComplexListNode clone(ComplexListNode head)复制一个复杂链表。在复杂链表中,每个结点除了一个next引用指向下一个结点,还有一个sibling引用指向任意结点或null。分析:第一种方法:第一步复制原始链表上的每个结点,并用next连接起来,第二步设置每个结点的sibling引用,在第二步设置sibling的时候,因为无...原创 2020-02-13 11:47:35 · 106 阅读 · 0 评论 -
面试题36:二叉搜索树与双向链表
题目:输入一棵二叉搜索树,将该二叉搜索树转化长一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点的引用。分析:在二叉树中,每个结点都有两个指向子结点的引用。双向链表中,每个结点,也有两个引用分别对应前一个结点和后一个结点。由于这两种结点结构像是,同时二叉搜索树也是一种排序的数据结构。要求转换后的链表是排好序的,我们可以中序遍历二叉树的每个结点,因为对二叉排序树中序遍...原创 2020-02-13 23:29:34 · 89 阅读 · 1 评论 -
面试题37:序列化二叉树
题目:分析:解法:原创 2020-02-14 18:58:45 · 98 阅读 · 0 评论 -
面试题38:字符串的排列
题目:输入一个字符串,打印出该字符串中字符的所有排列。例如,输入的字符串是abc,则打印出由字符串a、b、c所能排列出来的所有字符串abc、acb、bac、bca、cab和cba。分析:将一个字符串看成两部分组成:第一部分是它的第一个字符,第二部分是后面的所有字符。要求整个字符串的排列,第一步求所有可能出现在第一个位置的字符,也就是把第一个字符和后面的所有字符交换。第二步固定第一个...原创 2020-02-15 00:25:36 · 210 阅读 · 0 评论 -
面试题39:数组中出现次数超过一半的数字
题目:数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如,输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。分析:如果已知的数组是有序的,那么很容易找到出现频次超过一半的数字,找中位数就是了。可是题目中的数组并不是有序的,如果使用排序算法将数组排序,需要O(nlogn)的时间,这种方法是不合适...原创 2020-02-15 15:50:57 · 811 阅读 · 0 评论 -
面试题40:最小的k个数
题目:输入n个整数,找出其中最小的k个整数。例如,输入4、5、1、6、2、7、3、8这8个数字,则最小的4个数字是1、2、3、4。分析:时间复杂度是O(n)的算法,只有当我们可以修改输入的数组时可用根据对快速排序的理解,当某次排序确定的第i个位置的下标正好是数组中的第k个数字,那么此时k以及k左侧的这k个数字就是最小的k个数字了,这k个数字可能是无序的,这不重要,我们找到了最小的k...原创 2020-02-15 18:46:03 · 275 阅读 · 0 评论