1-10

序号 题目 描述 解法 难度
1 剑指 Offer 03.数组中重复的数字 长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,不知道有几个数字重复,也不知道每个数字重复了几次,找出数组中任意一个重复的数字。 (1)Map
遍历数组记录{val:count},再次遍历count>1的元素就是重复元素
时间O(N),空间O(N)
(2)Set
遍历数组,遇到元素val时判断set中是否已经存在val,若存在则重复,否则set.add(val)
时间O(N),空间O(N)
(3)抽屉原理
遍历数组每个元素,元素值nums[i]该位于i位置,
首先判断nums[i]是否已经在正确的位置,nums[i] == i
若不在正确位置,则判断其正确位置上是否已经有正确的值,若有则重复nums[nums[i] == nums[i]
时间O(N),空间O(1)
简单
2 剑指 Offer 04.二维数组中的查找 hot100的
240. 搜索二维矩阵 II

中等
3 剑指 Offer 05.替换空格 实现一个函数,把字符串 s 中的每个空格替换成”%20” (1)此题若输出是字符串,那么只能开辟一个新数组来逐个扫描并替换
(2)若输入是一个char[]数组,并且本身长度已经是替换后的总长度,那么可以在输入数组上实现原地替换
双指针p1指向原数组字符末尾charLen-1,p2指向数组末尾len-1
,从后向前扫描原字符串(为了不把还没扫描到的原始字符覆盖到),既p1向左移动,然后填充使用p2向左移动来填充,当P1==P2时结束扫描,O(1)时间
简单
4 剑指 Offer 06.从尾到头打印链表 输入一个链表的头节点,从尾到头反过来返回每个节点的值(用数组返回) (1)从头到尾扫描链表,头插法插入到结果数组res[],空间O(1)
(2)栈
扫描数组将每个节点值依次入栈,
然后出栈尾插法插入结果集。
简单
5 剑指 Offer 07.重建二叉树 hot100
105. 从前序与中序遍历序列构造二叉树

中等
6 剑指 Offer 09.用两个栈实现队列 用两个栈实现一个队列,实现它的两个函数
void appendTail(int val) {}
int deleteHead(){}
(1)stack1,stack2,入队列就push进入stack1,出队列就stack2.pop(),若stack2的size为0,则将stack1的所有元素都pop出来然后push进入stack2 简单
7 剑指 Offer 10- I.斐波那契数列 实现Fibonacci数列,从0、1开始,结果取模 10,0000,0007,若结果为1000000008则返回1。 (1)
dp[n] = dp[n-1] + dp[n-2]
注意在计算过程中,dp[i]每次都要取模,防止dp[n]太大超过int型数组上限,
dp[i] = dp[i]%10_0000_0007
简单
8 剑指 Offer 10- II.青蛙跳台阶问题 剑指 Offer 10- I.斐波那契数列
简单
9 剑指 Offer 11.旋转数组的最小数字 旋转数组,输入数组可能有重复值,找最小值 (1)二分
left=0, right = len-1,
因为有重复值,判断左右两端是否相等,若是则left++;
然后判断整个序列是否是升序的,若是,则最小值就是left位置,
若整个序列不是有序,那么最小值在右部分,
然后判断mid是否在左部分,还是右部分,nums[mid]>=nums[left],则mid在左部分,则left = mid+1,若则mid在右部分,则right = mid。
循环条件left<right
简单
10 剑指 Offer 12.矩阵中的路径 Hot100
79. 单词搜索

中等

11-20

11 剑指 Offer 13.机器人的运动范围 在一个m*n矩阵中,坐标范围从(0,0)到(m-1,n-1),机器人从(0,0)开始移动,可像上、下、左、右移动,但其不能进入行坐标和列坐标的数位之和大于k的格子,问机器人最多能进入多少个格子 优化:机器人从(0,0)出发只需要像下、右移动即可达到所有可到达的格子
(1)搜索+回溯
BFS:queue+循环(循环条件queue.size()>0)
或DFS:向下、向右递归
从(0,0)开始向右、下搜索和回溯,状态数组防止多次进入,范围check防止溢出格子,全局累计进入格子数
(2)动态规划
设dp[i][j]表示机器人是否可以达到(i,j)位置,因为机器人只需要向下或向右移动,所以dp[i][j]可从左、上转移状态,当(i,j)满足数位之和小于k时
dp[i][j] = dp[i-1][j] || dp[i][j-1]
中等
12 剑指 Offer 14- I.剪绳子 343. 整数拆分
给定一个正整数 n,将其拆分为至少两个正整数的和,并使这些整数的乘积最大化。 返回你可以获得的最大乘积
n≤3 时,按照规则应不切分,但由于题目要求必须剪成 m>1段,因此必须剪出一段长度为 11 的绳子,即返回 n - 1
当 n>3 时,求 n除以 3的 整数部分 a和 余数部分 b(即 n = 3a + b),并分为以下三种情况:
当 b = 0时,直接返回 3^a
当 b = 1时,要将一个 1 + 3 转换为 2+2,因此返回 3^(a-1)4
当 b = 2时,返回 3^a
2
中等
13 剑指 Offer 14- II.剪绳子 II 类似剑指 Offer 14- I.剪绳子,但是这次结果要1000000007取模,所以计算过程中可能会溢出,既超过int型最大值 使用快速幂求余
https://leetcode-cn.com/problems/jian-sheng-zi-ii-lcof/solution/mian-shi-ti-14-ii-jian-sheng-zi-iitan-xin-er-fen-f/
中等
14 剑指 Offer 15.二进制中1的个数 输入是一个无符号整数(以二进制串的形式),返回其二进制表达式中数字位数为 ‘1’ 的个数
类似:
461.汉明距离
191. 位1的个数
(1)循环统计最低位bit是否为1
n = n&1
(2)使用Brian W. Kernighan算法
n = n & (n-1)
注意:输入是一个无符号数,所以不能在循环中判断是否大于0,而是判断是否等于0
简单
15 剑指 Offer 16.数值的整数次方 实现 pow(x, n) ,不需要考虑大数问题
类似50. Pow(x, n)
快速幂乘法
(1)递归快速幂
(2)迭代快速幂
注意:负数转化为正数时,Integer.MIN_VALUE没有对应的正数,要用long型来存
中等
16 剑指 Offer 17.打印从1到最大的n位数 输入数字 n,按顺序打印出从 1 到最大的 n 位十进制数。比如输入 3,则打印出 1、2、3 一直到最大的 3 位数 999 (1)考虑大数问题
使用字符串表示数字
全排列生成,n个数位,每个位置可选择[0-9],
简单
17 剑指 Offer 18.删除链表的节点 给定单向链表的头指针和一个要删除的节点的值,定义一个函数删除该节点 (1)双指针+help节点
pVal指向要删除的节点,pre为pVal的前驱,因为要删除的节点可能时head节点,所以使用help节点
初始化:
help.next = head;
pVal = head; pre = help;
当pVal.val等于val时,找到
简单
18 剑指 Offer 19.正则表达式匹配

困难
19 剑指 Offer 20.表示数值的字符串

中等
20 剑指 Offer 21.调整数组顺序使奇数位于偶数前面 输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有奇数位于数组的前半部分,所有偶数位于数组的后半部分。 (1)两边遍历重建,空间O(N)
分配一个新的res[],扫描输入数组nums
第一遍扫描把所有奇数插入res
第一遍扫描把所有偶数插入res的奇数后面
(2)双指针交换,空间O(1)
left=0,right = len-1,left向右扫描,right向左扫描,若left为偶数则停止,若right为奇数则停止,然后交换left和right,当left等于right时停止循环
简单

21-30

21 剑指 Offer 22.链表中倒数第k个节点 输入一个链表,输出该链表中倒数第k个节点,链表的尾节点是倒数第1个节点 (1)双指针
p1、p2都指向head,p2先跑k-1步,然后一起跑,当p2为尾节点(不为null)时,p1就是倒数第k个节点
(2)单指针+两遍遍历
第一遍遍历找到链表长度为n,
第二遍走n-k步即可
简单
22 剑指 Offer 24.反转链表 同Hot100
206.反转链表

简单
23 剑指 Offer 25.合并两个排序的链表 输入两个递增排序的链表,合并这两个链表并使新链表中的节点仍然是递增排序的。 (1)双指针
help节点,p=help
p1,p2分别指向list1、list2,每次选择p1、p2中较小的尾插入到p后面。直到p1和p2都为空
简单
24 剑指 Offer 26.树的子结构 输入两棵二叉树A和B,判断B是不是A的子结构。(约定空树不是任意一个树的子结构)
B是A的子结构, 即 A中有出现和B相同的结构和节点值
(1)双递归
遍历树A的每个节点rootA,判断rootA是否包含树B,
rootA包含树B就是说,树B的每个位置有的节点rootA都得有且值还一样
中等
25 剑指 Offer 27.二叉树的镜像 输入一个二叉树,输出它的镜像。
(1)递归
得到root的left孩子的mirrorLeft和right孩子的mirrorRight,然后
root.left = mirrorRight
root.right = mirrorRight
简单
26 剑指 Offer 28.对称的二叉树 同Hot100101. 对称二叉树
简单
27 剑指 Offer 29.顺时针打印矩阵 输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字。 (1)外圈->内圈打印
(2)旋转打印
从(0,0)开始,以向右->向下->向左->向上方向打印,转向条件是当前移动是否超出矩阵范围
简单
28 剑指 Offer 30.包含min函数的栈 同Hot100 155.最小栈
简单
29 剑指 Offer 31.栈的压入、弹出序列 输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序 (1)栈模拟
压入序列pushSeq,弹出序列popSeq
扫描pushSeq将其逐一压入stack中,指针p初始为0,指向popSeq的popSeq[p]元素,若栈顶元素等于popSeq[p]时,栈元素,p++,直到栈顶元素不等于popSeq或栈为空或p>=popSeq.length
最后栈为空则合法
注意:当pushSeq和popSeq不一样长时肯定popSeq不合法
中等
30 剑指 Offer 32 - I.从上到下打印二叉树 从上到下打印出二叉树的每个节点,同一层的节点按照从左到右的顺序打印。 (1)队列+循环
层次遍历
中等

31-40

31 剑指 Offer 32 - II.从上到下打印二叉树 II 同Hot100
102. 二叉树的层序遍历

简单
32 剑指 Offer 32 - III.从上到下打印二叉树 III 103. 二叉树的锯齿形层序遍历,看二叉树打印系列
中等
33 剑指 Offer 33.二叉搜索树的后序遍历序列

输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历结果。是返回 true,否返回 false。假设输入的数组的任意两个数字都互不相同。
类似98.验证二叉搜索树
本题输入是一个数组,所以直接遍历数组判断左右子树所有节点是否满足值范围,然后递归判断左右子树是否为BST
leetcode-98输入是一个根节点root,采用dfs(root, lowborder,highborder)的方式判断左右子树是否满足要求
(2)递归
后序遍历顺序 “左、右、根”,
BST树”左子树< 根 <右子树”
根节点值rootVal = postOrder[len-1], 从左向右遍历, 找到第一个大于rootVal的下标为i, 则[i, len-2]为右子树范围, [0, i-1]为左子树范围,
保证[0, i-1]元素值<len-1<[i, len-2]元素值,且[0, i-1、[i,len-2]为BST, 则说明该序列为BST的后序序列
因为遍历过程已保证[0,i-1]元素值len-1即可
中等
34 剑指 Offer 34.二叉树中和为某一值的路径 113. 路径总和 II (1)dfs+回溯
dfs函数有一个参数target,每次递归减去当前节点值,然后在本层判断若target为0,则当前路径加入全局结果集res
中等
35 剑指 Offer 35.复杂链表的复制 复制一个复杂链表。在复杂链表中,每个节点除了有一个 next 指针指向下一个节点,还有一个 random 指针指向链表中的任意节点或者 nul (1)DFS+hash表
map保存{oldNode:newNode},
向next、random两个方向dfs即可
(2)BFS+hash表
map保存{oldNode:newNode}
queue+循环,BFS
(3)复制+拆分原表
每个节点node都复制一个副本copyNode,并将copyNode插入到node和node.next之间,然后处理random指针,空间(1)
(4)沿着next复制+map
map保存{oldNode:newNode},
沿着next指针复制即可,主要处理random指针,
当map.get(oldNode)存在时newNode = map.get(oldNode),
nowNode.random=copy(oldnode.randome);
map.put(oldNode.random, newNode.random)
中等
36 剑指 Offer 36.二叉搜索树与双向链表 输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的循环双向链表。要求不能创建任何新的节点,只能调整树中节点指针的指向。转化完成后,节点左指针指向前驱,右指针指向后继,返回链表中的第一个节点的指针。 (1)中序遍历
递归或循环中序遍历,维护一个pre指针,head指针,每次遍历当前节点node,要处理pre的后继和node的前驱
pre.right = node; node.left = pre
更新pre, pre = node;递归完成后,处理头节点和尾节点的前驱、后继关系
中等
37 剑指 Offer 37.序列化二叉树 同Hot100
297.二叉树的序列化与反序列化

困难
38 剑指 Offer 38.字符串的排列 输入一个字符串,打印出该字符串中字符的所有排列。 (1)排序+递归+循环+去重:
全排列+去重
中等
39 剑指 Offer 39.数组中出现次数超过一半的数字 同Hot100
169. 多数元素
(1)摩尔投票
(2)map
简单
40 剑指 Offer 40.最小的k个数 输入整数数组 arr ,找出其中最小的 k 个数
类似347.前 K 个高频元素
(1)排序
时间O(NlogN)
(2)k大小的最大堆,top-k
时间O(Nlogk)
(3)快速选择
时间O(N)
简单

41-50

41 剑指 Offer 41.数据流中的中位数 得到一个数据流中的中位数?奇数个数,中位数就是排序后中间的数值。偶数个数值,中位数就是排序后中间两个数的平均值。 (1)双heap
leftHeap最大堆、rightHeap最小堆,leftHeap存放排序后左半部分数,rightHeap存放排序后右半部分数,若数据总数为偶数则左右堆各一半,为奇数个数就右堆多一个
算法步骤:
1.若当前已经有的数字为偶数,则需要将左半部分和新来的数字中的最大值加入rightHeap:后面来的数字add进leftHeap,然后将leftHeap.poll()出的数字add进rightHeap
2.若当前已经有的数字为奇数,则需要将右半部分和新来的数字中的最小值加入rightHeap:后面来个数字add进rightHeap,rightHeap.poll()出来的数字add进leftHeap
困难
42 剑指 Offer 42.连续子数组的最大和 同Hot10053. 最大子序和
简单
43 剑指 Offer 43.1~n 整数中 1 出现的次数

困难
44 剑指 Offer 44.数字序列中某一位的数字 数字以0123456789101112131415…的格式序列化到一个字符序列中。在这个序列中,第5位(从下标0开始计数)是5,第13位是1,第19位是4,等等。
请写一个函数,求任意第n位对应的数字。

中等
45 剑指 Offer 45.把数组排成最小的数 输入一个非负整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。 (1)排序
若字符串”xy”对应的数字小于”yx”则x排在y前面,证明比较复杂
中等
46 剑指 Offer 46.把数字翻译成字符串 给定一个数字,我们按照如下规则把它翻译为字符串:0 翻译成 “a” ,1 翻译成 “b”,……,11 翻译成 “l”,……,25 翻译成 “z”。一个数字可能有多个翻译。请编程实现一个函数,用来计算一个数字有多少种不同的翻译方法。 (1)动态规划
中等
47 剑指 Offer 47.礼物的最大价值 在m*n矩阵中,从(0,0)走到(m-1,n-1),每个位置都有一个价值,求价值最大的路径的价值
类似62.不同路径64.最小路径和
(1)动态规划
设dp[i][j]表示到(i,j)的最大价值
dp[i][j] = max(dp[i-1][j], dp[i][j-1]) + nums[i][j]
中等
48 剑指 Offer 48.最长不含重复字符的子字符串 同Hot100
3. 无重复字符的最长子串

中等
49 剑指 Offer 49.丑数 只包含质因子 2、3 和 5 的数称作丑数(Ugly Number)。求按从小到大的顺序的第 n 个丑数。 (1)最小堆
每次从堆中弹出最小元素,然后乘2、3、5再加入堆中,可能会有重复元素,所以用set去重,第n次弹出的元素就是第n小的丑数
(2)动态规划
中等
50 剑指 Offer 50.第一个只出现一次的字符 在字符串 s 中找出第一个只出现一次的字符。如果没有,返回一个单空格。 s 只包含小写字母。 (1)hash表
{key:frequency}
第一遍扫描字符串统计所有key的frequency
第二遍扫描字符串,若字符x满足map.get(x)==1则x就是答案
简单

51-60

51 剑指 Offer 51.数组中的逆序对 在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。 (1)归并排序
困难
52 剑指 Offer 52.两个链表的第一个公共节点 同Hot100
160.相交链表

简单
53 剑指 Offer 53 - I.在排序数组中查找数字 I 统计一个数字在排序数组中出现的次数。 (1)遍历
O(N)
(2)二分
找到数字target在nums中第一次和最后依次出现的位置
简单
54 剑指 Offer 53 - II.0~n-1中缺失的数字 一个长度为n-1的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围0~n-1之内。在范围0~n-1内的n个数字中有且只有一个数字不在该数组中,请找出这个数字。 (1)二分搜索
递增排序数组,只需要找出nums[i]!=i的第一个i
简单
55 剑指 Offer 54.二叉搜索树的第k大节点 给定一棵二叉搜索树,请找出其中第k大的节点。 (1)反向中序遍历
反向中序遍历,统计第k个节点即为第k大的节点
简单
56 剑指 Offer 55 - I.二叉树的深度 输入一棵二叉树的根节点,求该树的深度。从根节点到叶节点依次经过的节点(含根、叶节点)形成树的一条路径,最长路径的长度为树的深度 (1)递归
设depth(node)为node 的深度
depth(node) = max(depth(node.left),depth(node.right))+ 1
简单
57 剑指 Offer 55 - II.平衡二叉树 输入一棵二叉树的根节点,判断该树是不是平衡二叉树。
一棵平衡二叉树的任意节点的左右子树的深度相差不超过1
(1)递归
设计一个函数depth(node),返回其深度,在函数中判断depth(node.left)和depth(node.right)是否深度插超过1,全局布尔变量isValid
简单
58 剑指 Offer 56 - I.数组中数字出现的次数 一个整型数组 nums 里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1) (1)位运算
两个相等的元素异或的结果为 0,而 0 与任意数 x 异或的结果都为 x。
两个不相等的元素在位级表示上一定会有所不同,因此这两个元素异或得到的结果 diff 一定不为 0
位运算 diff & -diff 能得到 diff 位级表示中最右侧为 1 的位
所以设x = diff & -diff
数组中所有元素在x位上为1和为0的分别对别分到两个数的不同组,然后分别异或就可以得到对应的数了
中等
59 剑指 Offer 56 - II.数组中数字出现的次数 II 在一个数组 nums 中除一个数字只出现一次之外,其他数字都出现了三次。请找出那个只出现一次的数字。 (1)排序
情况1: ABBBCCC
情况2: BBBACCC
情况3: BBBCCCA
(2)map
(3)位运算?
中等
60 剑指 Offer 57.和为s的两个数字 输入一个递增排序的数组和一个数字s,在数组中查找两个数,使得它们的和正好是s。如果有多对数字的和等于s,则输出任意一对即可。 (1)双指针 简单

61-70

61 剑指 Offer 57 - II.和为s的连续正数序列
简单
62 剑指 Offer 58 - I.翻转单词顺序 简单
63 剑指 Offer 58 - II.左旋转字符串 简单
64 剑指 Offer 59 - I.滑动窗口的最大值 困难
65 剑指 Offer 59 - II.队列的最大值 中等
66 剑指 Offer 60.n个骰子的点数 中等
67 剑指 Offer 61.扑克牌中的顺子 简单
68 剑指 Offer 62.圆圈中最后剩下的数字 0,1,···,n-1这n个数字排成一个圆圈,从数字0开始,每次从这个圆圈里删除第m个数字(删除后从下一个数字开始计数)。求出这个圆圈里剩下的最后一个数字。 (1) 简单
69 剑指 Offer 63.股票的最大利润 同Hot100
121.买卖股票的最佳时机
(1)股票系列通解
T[i][k][0]
T[i][k][1]
中等
70 剑指 Offer 64.求1+2+…+n 求 1+2+…+n ,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。 (1)递归代替循环+利用逻辑||短路代替if判断
public int sumNums(int n) {
int sum = 0;
boolean flag = n==0 || (sum=(n+sumNums(n-1)))>0;
return sum;
}
}
(2)递归+利用逻辑&&短路
public int sumNums(int n) {
int sum = 0;
boolean flag = n>0 && (sum=(n+sumNums(n-1)))>0;
return sum;
}
中等

* 两数相乘可以用加法和位运算来替换

  1. int quickMulti(int A, int B) {
  2. int ans = 0;
  3. for ( ;B>0; B >>= 1) {
  4. if ((B & 1)==1) {
  5. ans += A;
  6. }
  7. A <<= 1;
  8. }
  9. return ans;
  10. }

71-75

71 剑指 Offer 65.不用加减乘除做加法 写一个函数,求两个整数之和,要求在函数体内不得使用 “+”、“-”、“*”、“/” 四则运算符号 (1)数学
a^b可以得到无进位的和
(a&b)<<1可以得到进位
将”和” 和”进位循环求和、进位可以得到最终结果
简单
72 剑指 Offer 66.构建乘积数组 同Hot100
238. 除自身以外数组的乘积
构建左右乘积列表 中等
73 剑指 Offer 67.把字符串转换成整数 8. 字符串转换整数 (atoi) (1)模拟
1. 从左到右找到数字开始的第一个下标i,查看i前面是否有负号
1. 从i开始一直读取到非数字或到达末尾,得到数字的子序列numberSeq
1. 对numberSeq删除所有的前导0
1. 若numberSeq为负数,查看其是否小于”-2147483648”,则截断为-2147483648
1. 若numberSeq为正数,查看其是否大于”2147483647”,则阶段为2147483647
中等
74 剑指 Offer 68 - I.二叉搜索树的最近公共祖先 给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先 (1)可以使用leetcode-236的两种解法
(2)利用BST的性质+递归:
若node1、node2的值都小于root.val则最近祖先在root的左子树;若node1、node2的值都大于root.val则最近祖先在root的右子树;否则,root就是最近祖先。
简单
75 剑指 Offer 68 - II.二叉树的最近公共祖先 同Hot100
236.二叉树的最近公共祖先
(1)递归
(2)保存父节点+map
简单