剑指offer题目整理

2. Singleton模式

题目:设计一个类,我们只能生成该类的一个实例。

3. 数组中重复的数字

一:找出数组中重复的数字。

题目:在一个长度为n的数组里的所有数字都在0~n-1的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是重复的数字2或3。
解法:扫描下标为i的数字(设为m)与i是否相等,若不相等则查看与第m个数字是否相等,若不相等则交换,若相等则找到一个重复数字。(时间复杂度O(n),空间复杂度O(1))

二:不修改数组找出重复的数字。

题目:在一个长度为n+1的数组里的所有数字都在0~n的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。例如,如果输入长度为8的数组{2,3,5,4,3,2,6,7},那么对应的输出是重复的数字2或者3。
解法一:创建一个辅助数组,将原数组逐一复制过来,如数字m将其复制至下标为m的位置,若冲突了则找到一个重复数字。(时间复杂度O(n),空间复杂度O(n))
解法二: 使用二分法,例如8分为1 ~4,若遍历整个数组发现1 ~4的个数大于4则说明重复的数字在1 ~4之间,反之亦然。继续二分至一个数则能发现一个重复数字。(该方法不能发现所有重复的数字)(时间复杂度O(nlogn),空间复杂度O(1))

4. 二维数组中的查找

题目:在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都会按照从上到下递增的顺序排序。请完整一个函数,输入这样的二维数组和一个整数,判断数组中是否含有该整数。
解法:根据题目描述,可以从右上角或左下角入手,以右上角为例,若要查找的数字小于右上角的数说明这一列的数都大于要查找的数,则可将这一列剔除,若要查找的数字大于右上角的数说明这一行的数都小于要查找的数,则可将这一行剔除,以此来缩小范围,直到找到或查找范围为空。(时间复杂度O(n),空间复杂度O(1))

5. 替换空格

题目:请实现一个函数,把字符串中的每个空格替换成"%20"。例如,输入"We are happy.",则输出"We%20are%20happy."。
解法:如果可以创建新的字符串,则从前往后在新字符串上填充即可。
如果只能在原来的字符串上进行替换(假设空间足够),则从前往后填充的方式移动字符的次数太多(时间复杂度O(n2))。可以换一种思维方式,从后往前填充,这样每个字符都只要移动一次(时间复杂度O(n))。

6.从尾到头打印链表

题目:输入一个链表的头节点,从尾到头反过来打印出每个节点的值。
解法一: 使用栈
解法二:利用递归(如果链表很长会导致函数调用的层级很深,有可能会导致函数调用栈溢出)

7.重建二叉树

题目:输入某二叉树的前序遍历和中序遍历的结果,请重建该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如,输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建一个二叉树。
解法:利用递归和分治的思想以及二叉树遍历的特性,每次取先序遍历的第一个数,然后寻找这个数在中序遍历序列中的位置从而分出左右子树,对其分别进行递归。

8.二叉树的下一个节点

题目:给定一棵二叉树和其中的一个节点,如何找出中序遍历序列的下一个节点?树中的节点除了有两个分别指向左、右子节点的指针,还有一个指向父节点的指针。
解法:需要理解中序遍历的特点,可以画图辅助。然后通过循环来遍历寻找。
特点:如果这个节点有右子树,那么这个右子树的最左节点就是下一个节点。如果没有右子树,而节点的父节点的左子节点是这个节点,那么节点的父节点就是下一个节点,如果不是则继续向上遍历,直到找到一个是它父节点的左子节点的节点。

9.用两个栈实现队列

题目:用两个栈实现一个队列。请实现它的两个函数appendTail和deleteHead,分别在队列尾部插入节点和在队列头部删除节点的功能。
解法:在需要取数据时,将栈1的数据以此pop到栈2,然后再从栈2pop,则实现了先进先出的功能。

10.斐波那契数列

题目:求斐波那契数列的第n项。
解法一:利用斐波那契数列的特点自上而下进行递归。
解法二: 由于自上而下递归会有很多重复的计算,而且递归有可能会栈溢出。所以可以自下而上利用循环计算。

11.旋转数组的最小数字

题目:把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如,数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。
解法:因为此数组的特点是一个数组分成了左右两个递增的数组,且前面数组的所有数>=后面数组的所有数。可以利用二分法的思想,在数组首位设置两个flag,取中间值mid,则mid必会落到两个数组之一上,若落在前一个数组上则说明最小值在mid后,若落在后一个数组上则说明最小值在mid前。

12.矩阵中的路径

题目:请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一格开始,每一步可以在矩阵中向左、右、上、下、移动一格。如果一条路径经过了矩阵的某一格,那么该路径不能再次进入该格子。例如,在下面的3X4的矩阵中包含一条字符串“bfce”的路径。但距阵中不包含字符串“abfb”的路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入这个格子。
a b t g
c f c s
j d e h
解法:可以利用回溯法,对矩阵中的每一个字符进行遍历,其中每一个字符都有上下左右四个方向,设置int pathLength型路径长度变量和boolean visited数组(若路径已进入则置为true)对其进行递归寻找

13.机器人的运动范围

题目:地上有一个m行n列的方格。一个机器人从坐标(0,0)的格子开始移动,它每次可以向左、右、上、下移动一格,但不能进入行坐标和列坐标的数位之和大于k的格子。例如,当k为18时,机器人能够进入方格(35,37),因为3+5+3+7=18。但它不能进入方格(35,38),因为3+5+3+8=19.请问该机器人能够到达多少个格子?
解法:跟上一题类似使用回溯法。不同的是此题是计算总数而不是路线,不需要重置,因此回溯时不需要将visited数组重新置为false,也不需要设置路径变量。

14.剪绳子

题目:给你一根长度为n的绳子,请把绳子剪成m段(m、n都是整数,n>1并且m>1),每段绳子的长度记为k[0],k[1],…,k[m]。请问k[0]Xk[1]X…Xk[m]可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。
解法:可以使用动态规划,因为从上至下递归会有大量不必要的计算,可以从下至上进行计算。需要注意的是在长度小于等于3以及大于3的情形时需要用不同的计算方式。

15.二进制中1的个数.

题目:请实现一个函数,输入一个整数,输出该数二进制表示中1的个数。例如,把9表示成二进制是1001,有2位是1。因此,如果输入9,则该函数输出2。
解法:二进制运算的特点是,与1相与都为其本身,与1相或都为1,数字相同异或为0。所以此题可以利用此特性,将一个整数它他减去1后的结果相与则会将该整数最右边的1变为0,那么该整数的二进制表示中有多少个1就可以进行多少次这样的操作。

16.数值的整数次方

题目:实现函数double power(double base, int exponent),求base的exponent次方。不得使用库函数,同时不需要考虑大数问题。
解法:需要考虑到指数为正数和负数和0的情况,以及底数为0的情况。
由于n次方有如下规律:
n为偶数时,an=an/2 *an/2
n为奇数时,an=a(n-1)/2 *a(n-1)/2*a
所以我们可以利用递归来解决,对指数每次>>1来进行递归。

17.打印从1到最大的n位数

题目:输入数字 n,按顺序打印出从1到最大的n位十进制数。比如输入3,则打印出1、2、3一直到最大的3位数999。
解法:此题的考点在于对大数的处理,可以利用字符数组来存储。可以把问题转换成数字排列的解法,利用递归求解。
注意:为了符合阅读习惯、用户需求,打印的时候需要去除数字前面的零。

18.删除链表的节点

题目:给定单向链表的头节点和一个要删除的节点,定义一个函数在O(1)时间内删除链表节点。
解法:如果要删除节点i,先把i的下一个节点j的内容复制到i,然后把i指向节点j的下一个节点。需要注意的是当链表中只有一个节点和要删除的节点位于链表的尾部的情况。

21.调整数组顺序使奇数位于偶数前面

题目:输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有奇数位于数组的前半部分,所有偶数位于数组的后半部分。
解法:使用两个指针,一个指向数组头一个指向数组尾。
注意:有时面试官会需要我们提供一个解决同类型的通用办法,如负数在非负数前面等,此时需要将函数解耦,把判断的标准变成一个函数。(利用lambda表达式)

22.链表中倒数第k个节点

题目:输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点,例如,一个链表有6个节点,从头结点开始,它们的值依次是1、2、3、4、5、6。这个链表的倒数第3个节点是值为4的节点。
解法:当我们用一个指针遍历不能解决时可以考虑使用两个指针,一个先向前走k-1步,另一个指向头结点,然后两个指针一起移动,当第一个指针指向尾结点时,第二个指针就指向了倒数第k个节点。
注意:此题的另一考点在于所写代码的鲁棒性,如当头结点为空,节点总数小于k,输入的k等于零等,我们需要预先处理这些异常。

23.链表中环的入口节点

题目:如果一个链表中包含环,如何找出环的入口节点?
解法:可以分成三个步骤,通过两个指针一块一慢遍历找出环中任意一个节点,循环找到的节点计算出环中节点数n,通过让一个指针先走n步再和另一个指针一起遍历若相遇则是入口。
注意:代码的鲁棒性,如链表没有环或头节点为空。

26.树的子结构

题目:输入两颗二叉树A和B,判断B是不是A的子结构。
解法:将问题分为两步。一,在树A中找到和树B的根节点值一样的节点R。二,判断树A种以R为根节点的子树是不是包含和树B一样的结构。利用递归可以很方便解决。

28.对称的二叉树

题目:请实现一个函数,用来判断一颗二叉树是不是对称的。如果一颗二叉树和它的镜像一样,那么它是对称的。
解法:可以对树同时进行前序遍历和对称前序遍历来判断。

29.顺时针打印矩阵

题目:输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字。
解法:可以将其分为四步来求解。一:从左到右打印一行;二:从上到下打印一列;三:从右到左打印一行;四:从下到上打印一列。需要注意最后一圈有可能退化成只有一行、一列、甚至只有一个数字,所以需要仔细分析打印时的前提条件。

30.包含min函数的栈

题目:定义栈的数据结构,请在该类型中实现一个能够得到栈最小元素的min函数。在该栈中,调用min、push及pop的时间复杂度都是O(1)。
解法:利用一个辅助栈,在每次push时判断如果小于辅助栈中的最小元素则在将其push入辅助栈,如果大于辅助栈中的最小元素则将辅助栈栈顶元素push入辅助栈,保证每次辅助栈的栈顶元素都是数据栈中的最小元素。

31.栈的压入、弹出序列

题目:输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如,序列{1,2,3,4,5}是某栈的压栈序列,序列{4,5,3,2,1}是该压栈序列对应的一个弹出序列,但{4,3,5,1,2}就不可能是该压栈序列的弹出序列。
解法:在循环中将压栈序列和弹出序列相比较后依次压入栈,若数字相等则跳过。然后依次弹出,比较弹出时的顺序是否一致。

32.从上到下打印二叉树

题目一:不分行从上到下打印二叉树
从上到下打印出二叉树的每个节点,同一层的节点按照从左到右的顺序打印。
解法:即树的层次遍历,可以将每一个打印的节点的子节点存储在队列中,利用队列先进先出的特点依次打印。
题目二:分行从上到下打印二叉树
从上到下按层打印二叉树,同一层的节点按从左到右的顺序打印,每一层打印到一行。
解法:设置两个变量,一个为当前层需要打印的节点数,一个为下一层需要打印的节点数。
题目三:之字形打印二叉树
请实现一个函数按照之字形顺序打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右到左的顺序打印,第三行再按照从左到右的顺序打印,其他行依次类推。
解法:通过画图等可以得出每一层的打印顺序是相反的,所以可以利用栈先进后出的特点来存储,且在奇数层时先存左子节点再存右子节点,在偶数层时先存右子节点再存左子节点。

33.二叉搜索树的后续遍历序列

题目:输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历结果。如果是则返回true,否则返回false。假设输入数组的任意两个数字都互不相同。例如,输入数组{5,7,6,9,11,10,8},则返回true。如果输入的数组是{7,4,6,5},则由于没有哪颗二叉搜索树的后序遍历结果是这个序列,因此返回false。
解法:画图可知,数组的最后一位是这个树的根节点,根据二叉搜索树的特点,比这个根节点小的都是左子树,比这个根节点大的都是右子树,可以依此将数组中的数分成两部分,再进行递归求解。

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

题目:输入一棵二叉树和一个整数,打印出二叉树中节点值的和为输入整数的所有路径。从树的根节点开始往下一直到叶节点所经过的节点形成一条路径。
解法:利用递归以及树的前序遍历求解。

35.复杂链表的复制

题目:复制一个复杂链表,在复杂链表中,每个节点除了有一个next指针指向下一个节点还有一个sibling指针指向链表中的任意节点或null。
解法:在面对这样的复杂问题时,我们可以将其分解成几个小问题来解决;
一:为链表中的每个节点N创建副本N’,然后将N’链接到N后面。
二:遍历链表,若节点N有sibiling不为空,如N的sibling为S,则N’的sibling为S’。
三:拆分链表,把奇数位的节点链接起来就是原始链表,把偶数位的节点链接起来就是复制的链表。

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

题目:输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的节点,只能调整书中节点指针的指向。
解法:利用树的中序遍历来递归实现。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值