您的位置:首页 > 职场人生

【面试】剑指offer题目解析及tips速查(不断更新中)

2016-04-28 15:18 721 查看
【注】阅读《剑指offer》时做的笔记,简要描述问题内容、几种解法、需要注意的tips及全面的测试用例列举。适合已经读过《剑指offer》,在面试前需要快速浏览一遍回忆细节、巩固注意事项的人。在每个题目中标注了题目在原书中出现的页码以及对应的在线OJ练习链接。



1、将字符串转换成整数。P12

tips:输入为正负号(一个正负号、多个正负号、一个正负号后边没有数字);输入非数字字符;输入空指针、空串;最大正数和最小负数溢出(0x7fffffff 0x80000000);不能转换成整数。



2、求链表中倒数第k个结点。P13


tips:双指针,一个先走k-1步;输入为空指针;结点总数小于k;k为0。



3、为一个类添加赋值运算符函数(=重载)。P24


tips:返回值类型为类类型应用(连续赋值);参数类型常量引用;释放自身内存;是否是同一个实例的检查。

tips+:释放原始实例内存后new的时候内存不足,会丢失原始值。new成功再释放或建立局部临时实例并交换值,临时实例在作用域外自动析构。

测试用例:简单赋值;自身赋值;连续赋值。



4、二维数组中的查找x:数组从左到右递增,从上到下递增。P38


tips:右上角开始查找,x小左移,x大下移。

tips+:数组为空(空指针),行列<=0情况;左移和下移越界情况;参数为指针,根据内存存储规则访问数组元素。

在线OJ练习:二维数组中的查找



5、替换空格:实现函数将一个字符串中所有空格替换成"%20"。P44


tips:要求在原来的字符串上替换,且原始输入字符串后有足够多的内存。

key1:每扫描到一个空格将后边(O(N)个)字符右移3位,共O(N)个空格,时间复杂度O(N2)。

key2:遍历串统计空格数,新字符串长度为原长度+2*空格数,两个指针分别指向新旧字符串末尾,从后向前复制和移动指针(O(N))。

测试用例:空格位于字符串的前、后、中间;连续多个空格;没有空格;空指针;空字符串;字符串是一个空格字符;字符串是连续多个空格。

在线OJ练习:替换空格



相关题目:将两个排序数组A1和A2合并为排序数组,A1后有足够空间容纳A2。


tips:从后向前比较,避免从前往后复制时需要重复移动。

【转载请注明文章出处:http://blog.csdn.net/iamthezbl/article/details51274534



6、单向链表末尾插入一个结点(提供链表头指针**和新
结点值)。P49

tips:空表;表末尾。


7、在链表中找到第一个含有某值的结点并删除该结点(提供链表头指针**和新结点值)。P50

tips:空表(头指针指向的指针空)、头指针空(链表不存在)、第一个结点就是所找的值、值不存在、delete p后将其NULL。



8、从尾到头打印链表每个结点的值(输入头结点指针ListNode*
pHead),不允许修改输入。P52


tips:用栈实现(栈不空时弹出);用递归实现,先递归打印后边的结点,再打印本结点(链表较大时可能溢出)。

测试用例:多个结点的一般测试;单结点链表;头结点指针NULL。

在线OJ练习:从尾到头打印链表



9、根据前序遍历和中序遍历重建二叉树并输出头结点(不含重复数字)。P55


tips:前序找根节点,中序遍历找根,递归为构建左右子树后再返回根;注意用指针访问原序列防止修改;注意两个序列不匹配、为空、递归基准(只剩一个结点)、判定子树是否为空、适当返回异常信息(throw)。

测试用例:完全、不完全二叉树;没有右子、左子的二叉树;只有一个结点;输入NULL;前序遍历和中序遍历不匹配。

在线OJ练习:重建二叉树



10、用两个栈实现一个队列。数据成员为stack<T>
stack1,stack2;实现函数void appendTail(const T& node); T deteleHead(); P59


tips:插入一个元素时均压入stack1;删除一个元素时,若stack2不为空直接弹出,stack2为空时将stack1全部弹出压入stack2。

测试用例:空队列的插入和删除;非空队列的插入和删除;连续删除元素直至队列为空。

在线OJ练习:用两个栈实现队列



相关题目:用两个队列实现一个栈。P61

tips:保持其中一个队列为空,插入时插入非空队列,删除时弹出非空队列元素并插入另个空队列,直至剩余最后一个元素弹出作为栈元素的弹出。
【转载请注明文章出处:http://blog.csdn.net/iamthezbl/article/details51274534



11、实现二分查找、快速排序、归并排序。P64




12、用O(N)及常数辅助空间将公司员工年龄排序。P65



tips:新建一个长度100的数组,下标代表年龄,数组元素为计数。



13、找出升序旋转数组中的最小数字(将一个排序元素的前若干个移到最后)。P66


tips:顺序查找,O(N)。

tips+:两个指针分别指向开头和结尾,二分法取中间元素与两边元素比较,确定中间元素属于前后哪部分,把其中一个指针移到中间缩小查找范围,O(lgN)。三个元素均相等时无法判断,要用顺序查找。初始要将最小元素位置赋为最左,以防不发生旋转,左不小于右时才进入循环;两指针相邻时右指针为所求。

测试用例:升序数组的旋转、不旋转、有或没有重复数字、数组只有一个数字、NULL。

在线OJ练习:旋转数组的最小数字



14、写一个函数,输入n,求斐波那契数(n为0是0,n为1是1)。P73


tips:不用递归,否则会重复计算;可以把已经得到的数列中间项保存起来,计算过就不再计算;或者直接从小到大计算。

在线OJ练习:斐波那契数列



15、青蛙跳台阶,一次可以跳1或2,n级台阶有多少种跳法。P75


tips:n为1是1,n为2是2的斐波那契数列。

测试用例:一般功能测试;0、1、2的边界测试;较大数的效率测试。

在线OJ练习:跳台阶



相关题目:青蛙一次可以跳1、2、3……n级,跳n级台阶需要多少种跳法?P76

在线OJ练习:变态跳台阶





相关题目:用2×1的方格覆盖2×n的网格有多少种放法?P76


[b]在线OJ练习:矩形覆盖

[/b]

[b]【转载请注明文章出处:http://blog.csdn.net/iamthezbl/article/details51274534

[/b]



16、输入一个数n,输出这个数二进制表示中1的个数。P75


tips:判断最后一位是不是1(和1与),然后右移一位,判断最后一位,直到n为0。当输入0x80000000会陷入死循环(负数右移高位会补符号位1)。

tips+:和1与判断最后一位是不是1,将1左移,和n与判断次低位是不是1,直到1移出左端(如32位)。

tips++:n=(n-1)&n;把一个整数减去1与原始数与运算,会把原始数最右边的1变为0。

测试用例:正数、负数;边界值1、0x7FFFFFFF、0x80000000、0xFFFFFFFF、0。

在线OJ练习:二进制中1的个数



相关题目:用一条语句判断一个整数是不是2的整数次方?P82

tips:只有一个1,(n-1)&n是0。


相关题目:输入m和n,统计m的二进制改变多少位才能变为n?P82


tips:m^n,统计结果中1的个数。



17、输入double
base和int exponent,求base的exponent次方,不考虑大数问题。P90


tips:考虑一般整数的负指数幂、0的负指数幂、浮点数的相等不能用'=='判断。

tips+:n为偶数时拆分成两个n/2次幂相乘,n为奇数时拆分成两个(n-1)/2幂相乘再乘一次底数;右移运算代替除以2、与运算代替%判断奇偶。

测试用例:底数和指数分别设置为整数、负数和零。

在线OJ练习:数值的整数次方



18、输入n,打印1到最大的n位十进制数。P94


tips:求出最大数,然后循环打印。number每次乘10,计数器计数一次,知道计数器达到n。当n很大时long long型也可能溢出。

tips+:用字符串表示数,模拟加法和打印数字;需要n+1的字符串,不够位数在前边补0;模拟加法要考虑进位、首位溢出、char到int再到char的转换、+1后某位无进位则剩余的前边几位都无需计算;打印数字要从第一个不为零的开始打印,可以设置flag帮助标记。

tips++:每位上有0到9种选择的全排列,用递归从高位开始设置,打印仍不打印开始的零。

测试用例:1、2、3……;-1、0

扩展:char可以表示256个字符,0-9只用了10个,是否有更高效的方式。



相关题目:实现任意两个整数的加法(对字符串+1功能的扩展)?P98

tips:考察输入数字有负数。



19、给定单向链表的头指针和一个结点指针,定义函数在O(1)时间删除该结点。P99


tips:将下一个结点内容复制给该结点,删除下一个结点;注意指针的释放和链表的特殊情况。

测试用例:从多个结点的链表中删除中间结点、头结点、尾结点(O(N));链表只有一个结点;指向头结点指针的指针为NULL、头结点指针NULL、指向要删除的结点的指针NULL;要删除的结点不在链表中(O(N))。


20、输入整数数组,实现一个函数调整数组顺序,将奇数放在前半部分,偶数放在后半部分。P102

tips:从头到尾扫描,遇到偶数拿出临时存储,其余元素前移一位,再将这个偶数放在最后。O(N2)

tips+:类似快排分割维护两侧向中间移动的双指针的方法,指针停止时交换,直到指针交汇。O(N)

tips++:考虑可扩展性,将函数指针作为一个参数,将对数据两种类型的判断写成一个函数,可以解决一类相似问题。

测试用例:奇数偶数交替出现、只有奇数或偶数、奇数均在偶数前边、偶数均值奇数前边、输入NULL、数组只有一个数字。

在线OJ练习:调整数组顺序使奇数位于偶数前面

[b]【转载请注明文章出处:http://blog.csdn.net/iamthezbl/article/details51274534


21、输入一个链表,输出链表的倒数第k个结点(题目2)。P107[/b]

tips:双指针;输入为空指针;结点总数小于k;k为0。

测试用例:第k个结点在链表的中间、头、尾;链表头指针NULL;结点总数少于k;k=0。

在线OJ练习:链表中倒数第k个结点



相关题目:求链表的中间结点?P111


tips:双指针,一个每次走一步,一个每次走两步。



相关题目:判断一个单向链表是否形成了环形结构?P111


tips:双指针,一个每次走一步,一个每次走两步。走得快的追上走得慢的证明有环,走得快的到了NULL也没有追上走得慢的,证明无环。



22、输入一个链表的头结点,反转链表并输出反转后的头结点。P112


tips:三个指针分别指向当前结点、前一个结点、后一个结点;找到反转后的头结点;NULL和只有一个结点情况。

测试用例:输入链表多个结点、一个结点、NULL。

在线OJ练习:反转链表



相关题目:用递归实现链表反转?P114




23、合并两个递增排序的链表得到新的递增链表。P114


tips:两个链表的较小值点做头结点,next链接是递归函数的返回值;注意NULL。

测试用例:两个链表有多个结点,值不同或存在相同;至少一个为NULL;都只有一个结点时。

在线OJ练习:合并两个排序得链表



24、输入二叉树A和B,判断B是不是A的子结构。P117


tips:递归函数遍历树A找与B根结点相同的结点,然后调用另一个递归函数判断结构是否相同;注意递归终止条件和特殊情况(无左、右子等),本题中均是NULL的情况。

测试用例:A、B均是普通二叉树且A包含或不包含B;二叉树有一个或两个是NULL;树中没有左子或右子。

在线OJ练习:数的子结构



25、输入一个二叉树,输出它的镜像。P125


tips:前序遍历这棵树的每个结点,如果遍历到的结点有子结点,就交换结点,交换完所有非叶结点就得到树的镜像。

测试用例:普通二叉树;二叉树所有结点无左子树或右子树;单结点;NULL指针。

在线OJ练习:二叉树的镜像

扩展:用递归和循环分别实现。

[b]【转载请注明文章出处:http://blog.csdn.net/iamthezbl/article/details51274534


26、输入一个矩阵,按照从外向里以顺时针顺序依次打印出每一个数字。P127[/b]

tips:循环地打印一个圆圈,圆圈由4条直线构成,起点为对角线上元素。循环终止条件为列数>2start且行数>2start。最后一圈可能退化成不是完整的圈,因此4步打印是否执行要根据要打印的圈的起始终止的行号和列号的关系决定。

测试用例:数组有多行、一行、一列、一个数、空。

在线OJ练习:顺时针打印矩阵



27、定义栈的数据结构,实现能得到栈最小值的min函数,以O(1)的时间复杂度调用min、push和pop。P132


tips:用一个辅助栈,每次数据栈压入的值比最小值小时,辅助栈同时压入这个新值,数据栈压入的值比最小值大(或相等)时,辅助栈同时压入最小值(及辅助栈的栈顶元素),数据栈弹出时辅助栈同时弹出栈顶元素。则辅助栈的栈顶元素始终为当前的最小值。注意压入和弹出及求最小值时判断栈空。

测试用例:压入值比之前最小值大、小;弹出的是或不是最小值。

在线OJ练习:包含min函数的栈



28、输入两个整数序列,一个为入栈顺序(所有数字都不相等),判断另一个是否为对应的出栈顺序。P134


tips:用一个辅助栈,按压栈序列压入,规则是:如果下一个弹出的数字在辅助栈顶,直接弹出;如果不在栈顶,将压栈序列中为压栈的数字依次压栈,直到下一个弹出的数字被压入,然后弹出它;如果所有数字都入栈仍没有找到下一个弹出的数字,说明序列不匹配。注意用两个指针标注当前读取序列的进度,并在每次压入和弹出时移动指针;判断栈空及是否全部压入、全部弹出等。

测试用例:输入序列含有多个数字、一个数字;两组序列匹配、不匹配;输入NULL。

在线OJ练习:栈的压入、弹出序列



29、二叉树的层序遍历。P137


tips:用存储数结点指针的队列(deque)实现。注意判断输入是否NULL;终止条件为队列空;队列弹出前需先分配指针指向要删除的点;打印根结点后要判断左右子是否存在再将其放入队列。

测试用例:输入NULL;普通二叉树、单结点树。

在线OJ练习:从上往下打印二叉树

扩展:广度优先遍历有向图。



30、输入一个整数数组(无重复数字),判断它是不是某个二叉搜索树的后序遍历结果。P140


tips:最后一个数为根结点,前边数字的第一部分都比根结点小,第二部分都比根节点大,递归。注意判断输入NULL;不存在左子、右子等(同时也是递归的基准)情况;子树空也是二叉搜索树。

测试用例:后序遍历对应一棵完全二叉树、没有左子或右子的树、只有一个结点;不对应一棵二叉搜索树;输入NULL。

在线OJ练习:二叉搜索树的后序遍历序列

扩展:给定前序遍历。

【转载请注明文章出处:http://blog.csdn.net/iamthezbl/article/details51274534



31、输入一个二叉树和一个整数,打印二叉树中结点值的和为输入整数的所有路径。P143


tips:前序遍历访问,访问到一个结点时将这个点加入路径,并将其值加到和中,如果是叶结点且和为输入值,则打印路径,否则递归返回到上个结点处;注意在返回之前要将该结点从路径删除并将其值从和中减去。可以用一个vector保存路径,在末尾添加和删除结点(虽然栈也符合这种后进先出规则,但要打印全路径时只能访问到栈顶元素)。注意判断输入NULL、是否是叶结点、是否存在左子右子及递归返回前删除当前结点和当前值。

测试用例:二叉树中有零条、一条、多条路径符合;输入NULL。

在线OJ练习:二叉树中和为某一值得路径



32、实现函数ComplexListNode*
Clone (ComplexListNode*pHead)复制一个复杂链表,链表中除了有m_pNext指向下一个结点,还有m_pSibling指向链表中的任意结点或者NULL。P147

tips:复制链表结点用next连接,再对每个结点遍历链表找到sibling的指向。复杂度O(N2)。

tips+:复制链表并用next连接,用哈希表将原链表结点与新链表对应结点对<N,N'>存储起来,若N指向S,则N'指向S'。需要O(N)的时间和空间。
tips++:将新链表的每个结点插在原始链表对应结点后边,若N指向S,则N的next指向S的next,再将链按奇数和偶数拆分成两个链。需要O(N)的时间。注意复制指针的时候检查原始指针是否为NULL,因此新指针的sibling应初始化为NULL。
测试用例:一般链表、结点指向自身、两个结点互相指向形成环、只有一个结点、输入NULL。

在线OJ练习:复杂链表的复制



33、输入一棵二叉搜索树,将其转换为排序的双向链表,不能创建任何新结点,只能在树中调整指针方向。P151

tips:中序遍历方法能将二叉搜索树排序。使用递归。设定一个指针指向已经排好顺序的链表部分的最后一个结点。对根的左子树递归调用函数,然后将根接在排好的链表后边,再对右子树递归调用函数,最后根据指向链表尾部的指针找到链表头部,返回头结点。注意递归前判断根、左子、右子是否为空;尾指针指向是不是NULL(链表空);将根链接到链表后要更新尾指针指向。

测试用例:完全二叉树、没有左子或右子的树、单结点、NULL。

在线OJ练习:二叉搜索树与双向链表



34、输入一个字符串,打印出该字符串中字符的所有排列。P154

tips:将字符串看成两部分,第一个字符和后边的字符。第一个字符的所有可能即将第一个字符与后边的字符交换。最终排列即为固定第一个字符,加后边字符的全排列。使用递归。注意判断是否达到末尾('\0')。

测试用例:一个或多个字符、空串或NULL。

在线OJ练习:字符串的排列

扩展:求所有组合。可能有长度为1、2……n的组合。对于长度为m的组合,分成第一个字符和后边所有字符两部分。包含第一个字符,求n-1个字符中长度为m-1的组合;不包含第一个字符,求n-1个字符中长度为n的组合。递归。



相关题目:正方体顶点放八个数字,判断所有使每组对面上4个顶点和相等的摆放方式?P157


tips:对a1、a2……a8全排列,判断哪些排列可以满足题目组合。



相关题目:八皇后问题:8×8的方格上放八个皇后,所有皇后不能同行同列同对角线,所有放法。P157


tips:定义数组a[8],下边表示行号,数组初始化为0-7,代表列号。对数组内数字全排列,可以保证不同行不同列,然后判断是否同对角线,即:i-j=a[i]-a[j] 或 j-i=a[i]-a[j]。



35、输入一个数组,找到出现次数超过数组长度一半的数字。P163

tips:排序并统计每个数字出现的次数。O(NlogN)。

tips+:如果数字排序,中间的数字肯定是出现次数超过数组长度一半的数字。即长度为n的数组中第n/2大的数。运用快速查找(与快排类似)。需要检验输入数组是否合法及是否存在满足条件的数。数字的交换会改变原数组。

tips++:两个变量分别保存数字和次数。遍历的新数字与保存的数字相同,次数+1;与保存数字不同则-1;次数为0就保存新数字,次数重置为1.要找的数字是最后一个将次数设为1的数字。仍需要判断输入合法性及是否存在这样的数。

测试用例:输入数组中存在、不存在满足条件的数字;只有一个数字;输入NULL。

在线OJ练习:数组中出现次数超过一半的数字

【转载请注明文章出处:http://blog.csdn.net/iamthezbl/article/details51274534



36、输入n个整数,找出其中最小的k个数。P167

tips:将输入数组排序,返回前k个。O(NlogN)。

tips+:找到第k大的数(快排和快速查找中的方法)。这个数左边的k个数字就是整个数组的最小k个数(数组会被改变,且输出数字不是排序得)。O(N)。

tips++:设置一个k大的容器,当容器不满时读入的新数直接存入;当容器满时,新数与容器中最大数比较,若小于,则删除最大数,存入新数,否则继续读取下一个数。适合海量数据,O(Nlogk)。容器的实现可以用最大堆或红黑树(STL的multiset)。

测试用例:一般数字、数字中有相同数字;输入k为1或N;输入k比N大或k小于1或数组指针NULL。

在线OJ练习:最小的k个数



37、输入一个整数数组(有正有负),返回和最大的子数组的和。P171

tips:逐步累加,和小于(等于)0时重新累加,并不断更新目前的最大和。最大和初值为最小的负数0x80000000。为区分输入不合法导致的返回值0还是和为0导致的返回值0,可以设定一个bool全局变量作为标识。O(Nl)

tips+:动态规划分析,以第i个数字结尾的子数组的最大和f(i),如果f(i-1)比0小,则f(i)为a[i];如果f(i-1)比0大则f(i)为a[i]+f(i-1)。

测试用例:数组中有正有负、全是正数、全是负数;指针为NULL、输入数组长度<=0。

在线OJ练习:连续子数组的最大和


38、输入一个整数n,求1到n的所有整数的十进制表示中1出现的次数。P174
tips:对于每一个数,除10取余为1则累加1,然后除以10,判断十位数以此类推。时间复杂度过高。

tips+:每次去掉最高位递归。

测试用例:输入普通数;输入0、1;输入较大的数。

在线OJ练习:整数中1出现的次数



39、输入一个正整数数组,将数组内所有数拼接起来,返回得到的数中最小的一个。P177

tips:求数组中数的全排列,返回其中最小的。

tips+:把数组转换为字符串应对大数溢出问题;定义compare规则,并将两个字符串的比较规则compare(若mn<nm,则m在前n在后,拼接得到的位数相同,因此可以直接用字符串比较函数strcmp比较mn和nm)作为参数用排序函数qsort对数组中元素排序,排好序的数组顺次打印便得到最小数字。还需证明两个数的比较规则下排序得到的多个数数组结果为何满足题意:是否满足比较的性质(自反、对称、传递);是否满足题意(反证法)。注意判空操作和释放空间。

测试用例:数组中多个数字、数字中有重复数位、只有一个数字。

在线OJ练习:把数组排成最小的数



40、只包含因子2、3、5的数称为丑数,求第1500个丑数。令第一个丑数是1。P182

tips:逐个判断每个整数是不是丑数:连续除以2、3、5后得到1,即为丑数。

tips+:申请一个额外数组按顺序存储已经找到的丑数。若目前找到的最后一个丑数是M,则下一个丑数一定是M前边的某个丑数乘以2或3或5得到的。记录T2、T3、T5三个位置,使得Ti为M之前的某个位置,且Ti之前的数乘以i均小于M,Ti之后的数乘以i均大于M。新的丑数=min(a[T2]*2,a[T3]*3,a[T5]*5)。每求出一个丑数,更新这三个位置。注意释放临时数组。

测试用例:输入普通整数;输入0和1;输入较大数,如1500。

在线OJ练习:丑数

【转载请注明文章出处:http://blog.csdn.net/iamthezbl/article/details51274534


41、在字符串中找出第一个只出现一次的字符。P186
tips:扫描字符串构建一个哈希表,键为字符,值为字符出现的次数。再扫描一遍字符串,每扫描到一个字符就找到它的对应次数,直到找到一个次数为一的字符。可以用一个256的char数组实现哈希表,数组下标为字符的ASCII码,值为次数。

测试用例:字符串中存在、不存在出现一次的字符;字符串中所有字符均出现一次;一个字符;NULL。

扩展:如果不是256个字符,而是其他较大的key,如汉字,有几千个,怎样处理?



相关题目:输入两个字符串,从第一个字符串中删除在第二个字符串中出现过的字符。P188


tips:建立数组实现的哈希表存储第二个字符串的字符情况,再遍历第一个字符串,就知道每一个字符是否在2中出现过。



相关题目:删除字符串中所有重复出现的字符。P188


tips:构建bool型数组实现哈希表,初始化为false。当扫描到一个字符,根据ASCII码找到数组下标,若false就改成true,若true就删除。



相关题目:判断输入的两个字符串是不是互为变位词(含有字符相同,每个字符的数量也相同)。P189


tips:构建数组实现的简单哈希表,遍历第一个字符串构建哈希表,值为次数;遍历第二个字符串,遇到一个字符,哈希表对应位置减1.遍历完成后数组为全0说明是变位词。



42、输入一个数组,求出数组中逆序对(前一个数字大于后一个数字)的总数。P189

tips:归并排序,合并两个已排序数组时,从较大的数开始依次存入辅助数组,并同时记录逆序对数。注意指针前移不要越界。

测试用例:输入未经排序、递增排序、递减排序的数组;数组中含有重复数字;两个数字、单个数字;NULL。



43、输入两个单向链表,找到它们的第一个公共结点。P193

tips:蛮力法,遍历第一个链,每到一个结点,遍历第二个链考察是否相同。时间O(MN)。

tips+:将链表结点按遍历顺序放入两个辅助栈,每次比较栈顶元素是否相同,然后同时弹出一个,直到找到第一对不相同的栈顶元素。时间空间O(M+N)。

tips++:先遍历得到两个链表的长度,让较长的链表先走k步,然后两个链表共同走,同时比较结点是否相同。时间O(M+N)。

测试用例:两个链表有公共结点、无公共结点;公共结点在链表的头、中间、尾部;输入NULL。



相关题目:一棵二叉树两个叶结点的最低公共祖先。P196


tips:见57题。



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

tips:二分查找到这个数,然后分别向前后扫描计数。O(N),与从头到尾扫描计数一样。

tips+:二分查找第一个k,如果中间数字等于k,前一个数字也等于k,对前半段递归,否则中间数字就是第一个k,同样方法找到最后一个k。下标的差值得到数字出现的次数。O(logN)

测试用例:数组中包含、不包含查找的数字;查找的数字出现一次、多次;查找的值为最大、最小;数组只有一个数字;输入NULL。



45、输入一棵树的根结点,求树的深度(根到叶结点的最长路径长)。P207

tips:一棵树的深度为其左右子树深度的较大值+1。用遍历的方法递归实现。注意根为NULL情况优先考虑,则不用考虑左右子树是否存在。

测试用例:普通二叉树;二叉树没有左或右子树;只有一个结点;指针NULL。

【转载请注明文章出处:http://blog.csdn.net/iamthezbl/article/details51274534



46、输入一棵树的根结点,判断是不是平衡二叉树。P209

tips:根据遍历顺序,先判断根结点的左右子树深度差,若满足平衡,再递归判断其左右子树是否平衡。效率不高,会多次重复访问结点。

tips+:后序遍历,参数为根结点指针已经一个存储高度的参数(需要可修改),先求左右子树深度,若均平衡,再判断根结点树。要随时更新深度变量的值。
测试用例:平衡、不平衡二叉树;二叉树没有左或右子树;单结点;输入NULL。



47、一个整型数组中有两个数字只出现一次,其他都出现了两次,找出这两个数字。P211

tips:一个数字异或本身为0,将数组从头到尾异或得到的是这两个数字的异或结果。结果二进制表示中第一个1记为第n位,则这两个数字的第n位(用与1并和右移运算判断,注意不能超出int的位数)一定一个是0一个是1。按第n位是0还是1将原数组分成两部分。每个子数组分别从头到尾异或,得到的就是这两个出现一次的数字。

测试用例:数组中有多对重复数字;数组没有重复数字。



48、输入递增序列数组和一个数字s,在数组中查找两个数和为1,若有多对符合,则任意输出一对。P214

tips:两个指针分别指向第一个数字和最后一个数字,如果和小于s,将第一个指针后移;若和大于s,将第二个指针前移。

测试用例:输入NULL;存在/不存在和为s的数组;只有一个数字;存在一个等于s的数字但不存在和为s的数字。



49、输入一个整数s,打印所有和为s的连续正数序列(至少含两个数字)。P216

tips:设定两个数字small和big分别初始化为1和2。若和小于s,则增大big;若和大于s,则增大small。求连续序列和的时候不要每次重新用循环累加求,而是根据每次只改变一个数的特点,用上一个和直接求得。最少需要两个数字,当small到(1+s)/2就停止。s<3时不存在解。

测试用例:存在和为s的序列、不存在和为s的序列;s小于3。


50、输入一个英文句子,翻转句子中单词的顺序,单词内部顺序不变,标点符号当做字母(即与前边的单词看做一个词。P218
tips:第一次翻转字符串的所有字符;第二次扫描空格确定每个单词的起始位置(用两个指针分别指向单词的开头和结尾),每次翻转一个单词内部的所有字母。第一一个函数可以翻转字符串中的一部分(双指针指向要翻转的内容两端,交换两个指针指向对象的位置,然后两个指针分别向中间移动)。

测试用例:多个单词、一个单词;NULL;空串;只有空格。

【转载请注明文章出处:http://blog.csdn.net/iamthezbl/article/details51274534


51、字符串左旋转操作。输入字符串和一个数字k,将字符串的前k位移动到字符串最后。P220
tips:将字符串根据k分成两部分分别翻转(上一题的函数),再整体翻转一次。


测试用例:输入NULL、空串;字符串有一个字符;字符串长度为n、n-1;字符串长度小于n。


52、n个骰子仍在地上,朝上一面的和为s。输入n,打印s所有可能值出现的概率。P223
tips:n个骰子点数和最小为n,最大为6n,共6n-n+1种情况。建立一个长度6n-n+1的数组,和为s的次数存储在数组的第s-n个元素里。将骰子分为两堆,一个和n-1个。第一个骰子的六种情况和剩下骰子点数和的和。递归实现。有多次重复计算,效率不高。

tip+:用循环求骰子点数。建立两个数组存储每种总数出现的次数。在一次循环中,和为k出现的次数存储在第一个数组的第k个元素内;下一次循环中,添加一个新骰子,和为k出现的次数为上一次循环中和为k-1、k-2、k-3、k-4、k-5、k-6出现的次数总和,将其存储在另一个数组中。将点数最大值6设置为全局变量,增强程序的扩展性。数组轮换可以用一个flag(值为0和1,通过flag=flag-1实现转换)。注意每次数组轮换存储新的值前要清空(重置为全0)。

测试用例:输入普通n;输入0;输入较大的数字。


53、从扑克牌中抽取5张牌,判断是不是顺子。A为1,JQK分别为11、12、13,大小王是任意数字。P226
tips:将大小王设置为0。将数组排序,统计0出现的次数。统计相邻数字不连续的空缺个数,空缺总数小于等于0的个数则是顺子;如果非0元素出现重复,也不是顺子。统计间隔方法:small指向第一个非零数字(根据0全排在最开始,且零的个数已求出),big为small+1;如果两个数相等,则不是顺子,直接返回false;空缺数为big上的数字-small上的数字-1;再将small置为big,big+1,一直做到数组末尾。

测试用例:顺子、不是顺子;NULL;有多个大小王、没有大小王;有对子。


54、0到n-1这n个数字围成一个环,从0开始每次删除第m个数字(删除后从下一个数字开始计数),求环中最后剩下的数字。P228
tips:用环形链表模拟环,可以用标准模板库中的list,每次迭代器扫描到链表末尾时,将迭代器移动到链表头部,实现环形结构。重复遍历时间较长,且需要额外的空间。

tips+:令f(n,m)为从0到n-1数字中不断删除第m个数字后剩下的数字。找到递推公式:



测试用例:m小于n、m大于n;共有0个数字;输入很大的数字。


55、不用乘除和循环、条件判断类型的语句求1到n的和。P233
tips1:创建一个类,将累加操作放入类的构造函数里,数据成员设置为静态变量。创建n个类的对象(数组)。

tips2:!!n可以将n转化为bool型。构造类A和派生类B,分别定义两个函数对应n=0和n非零的求和。利用虚函数,根据n是否为0选择调用哪个指针指向的函数(指针分别指向基类和派生类的对象)。

tips3:同2,将两种函数的函数指针分别存入一个数组,根据!!n决定调用哪一个。

tips4:模板类型,让编译器以N为参数创建类型,同时需要N-1作为参数,最终实现求和。输入n必须为常量,且递归深度不能太深。

测试用例:输入n;n为0和1。

【转载请注明文章出处:http://blog.csdn.net/iamthezbl/article/details51274534


56、不用加减乘除实现两个数的和。P237
tips:用异或实现相加不进位,与运算并左移一位实现进位,再将这两部分做加法直到进位为0。

测试用例:输入正数、负数和0。


57、设计一个不能被继承的类。P239
tips:将构造函数和析构函数定义为私有函数,将调用构造函数new一个对象的函数和delete对象的函数设置为公有的。

tips+:将原始类设置为模板为T的类,且构造函数析构函数都是私有的,并将T类型设置为其友元类;T虚拟继承原始的类,则可以利用类T调用原始类的构造和析构函数;但T中继承的新类M无法创建实例,由于虚拟继承只保留一份成员,会跳过T直接找到原始类,但M不是原始类的友元,无法调用它的构造和析构函数。


58、二叉查找树中两个结点的最低公共祖先。P252
tips:从树中从上到下找到第一个在两个输入结点的值之间的结点,就是最低公共祖先。


59、普通树中两个结点的最低公共祖先,每个结点有指向父结点的指针。P253
tips:转换为求两个链表的公共结点,这两个链表的尾结点就是树的根结点。


60、普通树中两个结点的最低公共祖先,结点没有指向父结点的指针。P254
tips:从根部开始遍历,每遍历一个结点,看它的子树是不是同时包含这两个结点;如果在子树中,再分别遍历它的子结点,看它们的子树是不是同时包含这两个结点。直到找到一个结点,它的子树包含两个结点,而任一子结点的子树都不同时包含这两个结点,这个点就是最低公共祖先。需要对同一个结点遍历多次。

tips+:前序遍历树,同时保存访问的路径,得到根结点到结点1的路径;再得到根结点到结点2的路径;求这两条路径(两个链表)的(最后一个)公共结点。保存路径时,要根据访问的结点内容时刻增删链表上的结点。

【转载请注明文章出处:http://blog.csdn.net/iamthezbl/article/details51274534


61、长度为n的数字里数字都是0到n-1的,找出任意一个重复的数字输出。P261
tips:先排序然后找重复的。

tips+:建立一个长度为n的数组作为哈希表,每次读到一个数字都看哈希表中是否已存在这个数字。需要开辟空间。

tips++:从第0个开始扫描,看下标为i的数字是不是i,如果不是i,例如是k,便将下标为i的数字与下标为k的数字交换,继续遍历。直到找到一个数字i,下标为k,但下标为i的数字也是i,也就是数字i在下标为k和下标为i的位置同时出现了,则是一个重复数字。


62、给定一个数组A,构建数组B使得B中每个元素B[I]是A中除了A[i]外所有元素相乘的结果,不能使用除法。P263
tips:将乘积看成两部分(i之前和i之后),分别用数组C和D表示这两部分乘积,C和D可以分别用递推式计算。不使用额外空间时,直接在B上进行一次自上而下和自下而上的运算,模拟求解CD的过程。


63、用·和*做字符串模式匹配。P265





64、判断一个字符串是否是数字。P267







65、字符流中第一个不重复的字符。P269

tips:建立一个256的int数组作为哈希表,初始化为-1。当读到的字符未出现,赋值为其下标;出现过,赋值为-2;最后下标加一。设置一个函数遍历数组得到内容非负且最小的值,其对应的下标即为寻找的第一个不重复的字符的ASCII码。

【转载请注明文章出处:http://blog.csdn.net/iamthezbl/article/details51274534


66、链表中环的入口结点。P270

tips:双指针一快、一慢相交代表有环;在交汇处开始再走一圈到交汇,得到环中结点数n;一个指针先走n步,另一个指针从开头开始,然后共同走到交汇就是环的入口。注意每次访问next域都要判断是否NULL。


67、在一个排序链表中删除重复结点(重复的均删除,而不是保留一个)。P273

tips:三个指针分别指示pre、node和next。node不为NULL进入大循环,设定flag确定需要删除的第一个结点,默认为flase。当node与next值相同,flag变为true。!flag时继续后移;flag为真时用一个delete指针指向要删除的点,循环内判断每一个delete是否与要删除的值相等,若相等则删除,next指向下一个结点,delete不断后移直到不相等,跳出循环。如果pre为NULL,代表头结点删除,新的头结点为当前next;否则pre指向next,新的node为next,继续下一次大循环循环。


68、一个二叉树有指向父结点指针,给定一个结点,找到中序遍历的下一个结点。P275

tips:如果结点有右儿子,找到右子树中的最左结点;如果有父亲,向上不断找父结点直到有一个结点是其父结点的左结点;否则是NULL。


69、判断一棵二叉树是不是对称的。P277

tips:判断一棵树的前序遍历与对称前序遍历(先访问右再访问左)是否相同,树的值均相等可能会产生对称假象,因此要把NULL也考虑进来。递归调用时,均为NULL是true,只有一个NULL是false。


70、从上到下按层打印二叉树,每层打印成一行。P278

tips:用队列保存结点,需要两个变量分别记录本层还未打印的结点数与下一层有多少结点。将根入队,初始化变量1和0;当儿子存在时入队,下层结点数+1;打印根结点值,本层结点数-1;本层为0时,换行,本层结点值=下层,下层结点数变0。

【转载请注明文章出处:http://blog.csdn.net/iamthezbl/article/details51274534


71、按之字形打印二叉树,第一行从左到右,第二行从右到左……。P280

tips:两个栈轮流保存一层结点,打印本层时,将其子结点存入另外一个栈(根据层的奇偶性决定哪个栈从左到右存储,另个栈从右到左存储,可以用两个一直在0、1间交替的变量标识),直到两个栈都为空。


72、序列化和反序列化二叉树。P283

tips:先前序遍历,结点间用逗号分隔,NULL指针用¥表示。反序列化时从根结点开始构建,判断遇到的是否是¥。


73、二叉搜索树的第k个结点。P285

tips:中序遍历得到排序顺序;将k作为引用传入,先访问左子,若返回NULL就访问根,k为1时赋值根,否则k-1,若仍返回空就访问右子。


74、数据流中的中位数。P286

tips:建立最大堆和最小堆(使得最大堆数均小于最小堆,且两个堆元素数目最多差1)。总数是奇数时插入最大堆(若该数>最小堆顶,先插入最小堆,再将最小堆顶插入最大堆);总数是偶数时插入最小堆(若该数<最大堆顶,先插入最大堆,再将最大堆顶插入最小堆)。结束时判断元素总数,为0、奇数个、偶数个情况。


75、滑窗问题。P290

tips:两个栈实现一个队列,且实现可以求最大值的栈。

tips+:双向队列,一个滑窗范围内(如果新数字大于队尾,队尾弹出),新数字下标入队。从第一个滑窗结束到数组末尾:队头为可能最大值,如果新数字大于队尾,队尾弹出,如果数字滑出滑窗,队头弹出。

【转载请注明文章出处:http://blog.csdn.net/iamthezbl/article/details51274534


76、矩阵中的路径。P294

tips:回溯法。栈保存路径,还需要和矩阵等大的bool型矩阵标识是否被访问过。


77、m×n的矩阵中,机器人从零点开始走,不能进入行列和大于k的格子,求机器人的运动范围。P296

tips:判断机器人能否进入新的格子,当周围都不能进入时,返回上个格子。

【转载请注明文章出处:http://blog.csdn.net/iamthezbl/article/details51274534
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: