剑指Offer
本专栏是本人在刷剑指Offer时做的笔记,主要Java语言实现。
我的Coder小屋
本科阶段:延安大学(2015-2019),摸鱼四年。
研究生阶段:西北大学(2019-2022),还未结束,主编程语言Java
展开
-
50.第一个只出现一次的字符
50 第一个只出现一次的字符1 题目描述 在字符串 s 中找出第一个只出现一次的字符。如果没有,返回一个单空格。 s 只包含小写字母。示例:s = "abaccdeff"返回 "b"s = "" 返回 " "2 题目分析 两次遍历,第一次用长度为26大小的数组存储每个字符出现的次数,第二遍遍历找第一个次数为1的字符即可。3 代码public char firstUniqChar(String s) { if (s == null || s.length() == 0原创 2021-08-15 09:13:57 · 100 阅读 · 1 评论 -
49.丑数
49 丑数1 题目描述 我们把只包含质因子 2、3 和 5 的数称作丑数(Ugly Number)。求按从小到大的顺序的第 n 个丑数。示例:输入: n = 10输出: 12解释: 1, 2, 3, 4, 5, 6, 8, 9, 10, 12 是前 10 个丑数。2 题目分析 动态规划思想:dp[i]表示第i个丑数,从1开始计数,有dp[1]=1,根据丑数的定义我们知道dp数组中每个元素乘2,乘3,乘5都还是丑数。不妨设置三个指针p2,p3,p5,初始时分别指向位置1,表示当前位置原创 2021-08-15 09:13:30 · 146 阅读 · 0 评论 -
48.最长不含重复字符的子字符串
48 最长不含重复字符的子字符串1 题目描述 请从字符串中找出一个最长的不包含重复字符的子字符串,计算该最长子字符串的长度。示例输入: "abcabcbb"输出: 3 解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。2 题目分析 思路:动态规划思想:定义f(i)表示以第i个字符为结尾的不包含重复字符的子串最长长度。 初始:f(0) = 1; 转移方程:当i没出现过时,f(i) = f(i - 1) + 1当i出现过,先找到与上一个出现过位置原创 2021-08-15 09:12:29 · 167 阅读 · 0 评论 -
47.礼物的最大价值
47 礼物的最大价值1 题目描述 在一个 m*n 的棋盘的每一格都放有一个礼物,每个礼物都有一定的价值(价值大于 0)。你可以从棋盘的左上角开始拿格子里的礼物,并每次向右或者向下移动一格、直到到达棋盘的右下角。给定一个棋盘及其上面的礼物的价值,请计算你最多能拿到多少价值的礼物?示例:输入: [ [1,3,1], [1,5,1], [4,2,1]]输出: 12解释: 路径 1→3→5→2→1 可以拿到最多价值的礼物2 题目分析 根据题意,对于每个格子,其可以从上方或者原创 2021-08-15 09:11:53 · 88 阅读 · 0 评论 -
46.把数字翻译成字符串
46 把数字翻译成字符串1 题目描述 给定一个数字,我们按照如下规则把它翻译为字符串:0 翻译成 “a” ,1 翻译成 “b”,……,11 翻译成 “l”,……,25 翻译成 “z”。一个数字可能有多个翻译。请编程实现一个函数,用来计算一个数字有多少种不同的翻译方法。示例:输入: 12258输出: 5解释: 12258有5种不同的翻译,分别是"bccfi", "bwfi", "bczi", "mcfi"和"mzi"2 题目分析 思想:对于每个元素都有两种情况,第一种单独翻译;第二种原创 2021-08-15 09:11:25 · 110 阅读 · 0 评论 -
45.把数组排成最小的数
45 把数组排成最小的数1 题目描述 输入一个非负整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。示例:输入: [10,2]输出: "102"2 题目分析考查自定义排序使用情况,具体如下:将nums转化为字符串数组类型,定义一种排序规则(x,y) -> ((x+y).compareTo(y+x)),原则:高位的尽可能小直接StringBuilder相加即可2 代码public String minNumber(int[] nums) {原创 2021-08-15 09:10:22 · 158 阅读 · 0 评论 -
44.数字序列中某一位的数字
44. 数字序列中某一位的数字1 题目描述 数字以0123456789101112131415…的格式序列化到一个字符序列中。在这个序列中,第5位(从下标0开始计数)是5,第13位是1,第19位是4,等等。请写一个函数,求任意第n位对应的数字。2 题目分析 思路:找规律题。 将数字序列如下划分,很容易将1-9,10-99,100-999,…这些类别,digit表示位数,start表示起始元素,则每个区间元素总数就有如下规律:count = start * 9 * digit解题步原创 2021-08-15 09:09:49 · 111 阅读 · 1 评论 -
43.1~n整数中1出现的次数
43 1~n整数中1出现的次数1 题目描述 输入一个整数 n ,求1~n这n个整数的十进制表示中1出现的次数。例如,输入12,1~12这些整数中包含1 的数字有1、10、11和12,1一共出现了5次。2 题目分析求1-n整数中1出现的次数。这是个数学题,对于一个整数n,要找1~n所有的1,可以将某一位固定为1,然后找所有小于该数的排列组合,从最低位往最高位依次找,最终的结果就是1的总数了。具体对于当前位有三种情况:cur = 0,cur = 1, cur > 1,还需要知道当前指向元素的原创 2021-08-15 09:09:19 · 88 阅读 · 0 评论 -
42.连续子数组的最大和
42. 连续子数组的最大和1 题目描述 输入一个整型数组,数组中的一个或连续多个整数组成一个子数组。求所有子数组的和的最大值。要求时间复杂度为O(n)。2 题目分析思路:对于数组nums,动态规划思想;f(i)表示以i结尾的最大连续和,那么对于每个元素都有两种可能:与前面部分组合为一段;单独为一段 f(0) = nums[0] f(i) = Math.max(f(i - 1) + nums[i], nums[i]); 返回dp数组中的最大值即可。优化,将数组去掉,用pr原创 2021-08-15 09:08:45 · 62 阅读 · 0 评论 -
41.数据流中的中位数
41 数据流中的中位数1 题目描述 如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。例如,[2,3,4] 的中位数是 3[2,3] 的中位数是 (2 + 3) / 2 = 2.5设计一个支持以下两种操作的数据结构:void addNum(int num) - 从数据流中添加一个整数到数据结构中。double findMedian() - 返回目前所有元原创 2021-08-15 09:08:00 · 65 阅读 · 0 评论 -
40.最小的K个数
40. 最小的K个数1 题目描述 输入整数数组 arr ,找出其中最小的 k 个数。例如,输入4、5、1、6、2、7、3、8这8个数字,则最小的4个数字是1、2、3、4。2 题目分析最小的k个数,直观思路就是先排序然后找出前k个,这种解法当然不行,下面介绍两种海量数据中的topK问题思路:1. 用大顶堆:首先将前k个元素加入大顶堆中,然后从第k+1个开始将当前元素和堆顶元素比较,把较小者加入堆中(Java中用优先队列实现,如果不需要自己手动写堆的话)2. 快排partition思想:以基准p原创 2021-07-27 08:31:31 · 118 阅读 · 0 评论 -
39.数组中出现次数超过一半的数字
39. 数组中出现次数超过一半的数字1 题目描述 数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。你可以假设数组是非空的,并且给定的数组总是存在多数元素。2 题目分析 最直观的解法是先排序然后去找数组中间的元素即可,但时间复杂度就是O(nlogn),这里使用摩尔投票法:定义一种投票机制,初始化vote等于0,x=0,遍历数组,当vote等于0时将x更新为当前值,对当前值num和x进行比较,如果相等则vote加1,不相等则减一,循环结束返回x即可。3 代码public int原创 2021-07-27 08:30:50 · 89 阅读 · 0 评论 -
38.字符串的排列
38. 字符串的排列1 题目描述 输入一个字符串,打印出该字符串中字符的所有排列。你可以以任意顺序返回这个字符串数组,但里面不能有重复元素。2 题目分析字符串的排列问题:全排列,考虑用回溯法,注意需要对重复元素去重(主要方法是对字符数组进行排序,保证每次加入的字符是重复字符中的最后一个)在递归块中做如下判断 if (vis[j] || (j > 0 && vis[j - 1] && s[j - 1] == s[j])) continue;用于限制每原创 2021-07-27 08:30:21 · 80 阅读 · 0 评论 -
37.序列化二叉树
37. 序列化二叉树1 题目描述 请实现两个函数,分别用来序列化和反序列化二叉树。你需要设计一个算法来实现二叉树的序列化与反序列化。这里不限定你的序列 / 反序列化算法执行逻辑,你只需要保证一个二叉树可以被序列化为一个字符串并且将这个字符串反序列化为原始的树结构。2 题目分析将二叉树编码成一个字符串形式(注:这里序列化按层序遍历进行序列化,剑指Offer中是前序遍历),例: 1 / \原创 2021-07-27 08:29:46 · 94 阅读 · 0 评论 -
36.二叉搜索树和双向链表
36. 二叉搜索树和双向链表1 题目描述 输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的循环双向链表。要求不能创建任何新的节点,只能调整树中节点指针的指向。例:2 题目分析 由于二叉搜索树也是一种排序的数据结构,因此我们只要改变指针指向返回头结点即可。问题是如何维护双向链表的左右指向以及头结点,对于一个节点去同时维护左边和右边显得没有必要,因此我们可以对二叉树进行中序遍历每次只改变当前节点和pre节点之间的指向(即pre.right=cur,cur.left=pre),好了指针指向解原创 2021-07-27 08:28:54 · 51 阅读 · 0 评论 -
35.复杂链表的复制
35. 复杂链表的复制1 题目描述 请实现 copyRandomList 函数,复制一个复杂链表。在复杂链表中,每个节点除了有一个 next 指针指向下一个节点,还有一个 random 指针指向链表中的任意节点或者 null。示例输入:head = [[7,null],[13,0],[11,4],[10,2],[1,0]]输出:[[7,null],[13,0],[11,4],[10,2],[1,0]]2 题目分析 根据题目描述可知,该链表除过next指针域还有一个额外的random原创 2021-07-27 08:27:51 · 106 阅读 · 0 评论 -
34.二叉树中和为某一值的路径
34. 二叉树中和为某一值的路径1 题目描述 输入一棵二叉树和一个整数,打印出二叉树中节点值的和为输入整数的所有路径。从树的根节点开始往下一直到叶节点所经过的节点形成一条路径。示例:给定如下二叉树,以及目标target=22, 5 / \ 4 8 / / \ 11 13 4 / \ / \ 7 2 5 1返回原创 2021-07-23 08:40:15 · 200 阅读 · 1 评论 -
33.二叉树的后续遍历序列
33. 二叉树的后续遍历1 题目描述 输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历结果。如果是则返回 true,否则返回 false。假设输入的数组的任意两个数字都互不相同。参考下面这颗二叉搜索树: 5 / \ 2 6 / \ 1 3输入: [1,3,2,6,5]输出: true输入: [1,6,3,2,5]输出: false2 题目分析 思路:根据题意,容易看出数组的最后一个元素就是根节点,二叉搜索树的定义又可知左边的小于根节点原创 2021-07-23 08:38:43 · 167 阅读 · 0 评论 -
32.从上到下打印二叉树
32. 从上到下打印二叉树1 题目描述 从上到下打印出二叉树的每个节点,同一层的节点按照从左到右的顺序打印。2 题目分析 用队列就能简单实现,层序遍历3 代码public int[] levelOrder(TreeNode root) { if (root == null) return new int[0]; List<Integer> ans = new ArrayList<>(); // 队列用于层序遍历 Deque<原创 2021-07-23 08:35:35 · 70 阅读 · 0 评论 -
31.栈的压入弹出序列
31. 栈的压入弹出序列1 题目描述 输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如,序列 {1,2,3,4,5} 是某栈的压栈序列,序列 {4,5,3,2,1} 是该压栈序列对应的一个弹出序列,但 {4,3,5,1,2} 就不可能是该压栈序列的弹出序列。2 题目分析贪心思路:将pushed队列中的每个数都push到栈中,同时检查这个数是不是popped序列中下一个要pop的值,如果是则pop出来。最后检查是不是所有该po原创 2021-07-23 08:33:18 · 58 阅读 · 0 评论 -
30.包含min函数的栈
30 包含min函数的栈1 题目描述 定义栈的数据结构,请在该类型中实现一个能够得到栈的最小元素的 min 函数在该栈中,调用 min、push 及 pop 的时间复杂度都是 O(1)。示例MinStack minStack = new MinStack();minStack.push(-2);minStack.push(0);minStack.push(-3);minStack.min(); --> 返回 -3.minStack.pop();minStack.top();原创 2021-07-23 08:28:16 · 88 阅读 · 1 评论 -
29.顺时针打印矩阵
29.顺时针打印矩阵1 题目描述 输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字。2 题目分析 没有什么复杂的数据结构,按层去模拟即可。3 代码/* 顺时针打印矩阵:模拟添加即可,需要四个初始遍历left,right,top,bottom分别表示左右上下的界限,画图参考食用最佳按层模拟 */public List<Integer> spiralOrder(int[][] matrix) { List<Integer>原创 2021-07-21 08:44:29 · 45 阅读 · 0 评论 -
28.对称的二叉树
28. 对称的二叉树1 题目描述 请实现一个函数,用来判断一棵二叉树是不是对称的。如果一棵二叉树和它的镜像一样,那么它是对称的。 例如,二叉树 [1,2,2,3,4,4,3] 是对称的。2 题目分析 空节点和只有一个节点肯定是对称的,然后递归判断左右子节点是否相等即可。3 代码// 递归判断左边的左边和右边的右边是否相等以及//左边的右边和右边的左边是否相等public boolean isSymmetric(TreeNode root) { if (root == n原创 2021-07-21 08:43:49 · 79 阅读 · 0 评论 -
27.二叉树的镜像
27. 二叉树的镜像1 题目描述 请完成一个函数,输入一个二叉树,该函数输出它的镜像。例如输入: 4 / \ 2 7 / \ / \1 3 6 9镜像输出: 4 / \ 7 2 / \原创 2021-07-20 15:55:27 · 62 阅读 · 0 评论 -
26.树的子结构
26. 树的子结构1 题目描述输入两棵二叉树A和B,判断B是不是A的子结构。(约定空树不是任意一个树的子结构)B是A的子结构, 即 A中有出现和B相同的结构和节点值。例如:给定的树 A: 3 / \ 4 5 / \ 1 2给定的树 B: 4 / 1返回 true,因为 B 与 A原创 2021-07-20 15:54:56 · 51 阅读 · 0 评论 -
25.合并两个排序的链表
25. 合并两个排序的链表1 题目描述 输入两个递增排序的链表,合并这两个链表并使新链表中的节点仍然是递增排序的。2 题目分析// 合并两个有序链表,有两种思路,一种是用迭代的思想与遍历,另一个就比较妙了,采用递归思想,代码也及其简单,先看迭代版本的// 二者的时间和空间复杂度都是O(m+n)3 代码// 迭代public ListNode mergeTwoLists1(ListNode l1, ListNode l2) { ListNode yummy = new ListN原创 2021-07-20 15:54:20 · 64 阅读 · 0 评论 -
24.反转链表
24. 反转链表1 题目描述 定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点。2 题目描述 链表反转很简单,只需要改变指针的指向即可。画一个图就很明显了: 一直移动curr和pre指针,每次使curr.next=pre,然后往前更新直到curr为null时返回pre即可。3 代码public ListNode reverseList(ListNode head) { ListNode pre = null, curr = head; whi原创 2021-07-20 09:44:22 · 50 阅读 · 0 评论 -
23.链表中环的入口节点
23. 链表中环的入口节点1 题目描述 如果一个链表中包含环,如何找出环的入口节点?例如下图所示的链表中,环的入口节点是3。2 题目分析 本题剑指Offer的思路是先遍历一遍统计环中的结点个数n,然后先让快指针走n步,然后快慢一针一起走,这样两个指针相遇的点就是入环点了。代码不就不贴了,说一种力扣上的思路:同样是快慢指针解法,不同的是快指针走两步,慢指针走一步当两个指针相遇的时候即slow==fast,此时让slow和指向头结点的指针一起走,相遇点就是入环点,具体为什么这样看如下图解释:原创 2021-07-20 09:43:50 · 53 阅读 · 0 评论 -
22.链表中倒数第k个节点
21. 链表中倒数第k个节点1 题目描述 输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。 例如,一个链表有 6 个节点,从头节点开始,它们的值依次是 1、2、3、4、5、6。这个链表的倒数第 3 个节点是值为 4 的节点。2 题目分析 找倒数第k个节点,即找地n-k-1个节点,利用快慢指针的思想3 代码/** * Definition for singly-linked list. * public clas原创 2021-07-20 09:42:48 · 43 阅读 · 0 评论 -
21.调整数组顺序使奇数位于偶数前面
21. 调整数组顺序使奇数位于偶数前面1 题目描述 输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有奇数位于数组的前半部分,所有偶数位于数组的后半部分。2 题目分析 交换奇偶元素位置,首先就会想到双指针分别从数组的首尾往中间扫描,碰到偶数<奇数则交换3 代码class Solution { public int[] exchange(int[] nums) { if (nums == null || nums.length == 0) ret原创 2021-07-20 09:42:08 · 52 阅读 · 0 评论 -
20.表示数值的字符串
表示数值的字符串1题目描述 请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。数值(按顺序)可以分成以下几个部分:若干空格一个 小数 或者 整数(可选)一个 ‘e’ 或 ‘E’ ,后面跟着一个 整数若干空格小数(按顺序)可以分成以下几个部分:(可选)一个符号字符(’+’ 或 ‘-’)下述格式之一:至少一位数字,后面跟着一个点 ‘.’至少一位数字,后面跟着一个点 ‘.’ ,后面再跟着至少一位数字一个点 ‘.’ ,后面跟着至少一位数字整数(按顺序)可以.原创 2021-07-20 09:41:47 · 65 阅读 · 0 评论 -
19.正则表达式匹配
19. 正则表达式匹配1 题目描述 给你一个字符串 s 和一个字符规律 p,请你来实现一个支持 ‘.’ 和 ‘*’ 的正则表达式匹配。‘.’ 匹配任意单个字符‘*’ 匹配零个或多个前面的那一个元素所谓匹配,是要涵盖 整个 字符串 s的,而不是部分字符串。2 题目分析 假设主串为 A,模式串为 B 从最后一步出发,需要关注最后进来的字符。假设 A 的长度为 n ,B 的长度为 m ,关注正则表达式 B 的最后一个字符是谁,它有三种可能,正常字符、* 和 .(点),那针对这三种情况讨论即可原创 2021-07-20 09:38:15 · 104 阅读 · 0 评论 -
18.删除链表的节点
18. 删除链表的节点1 题目描述 给定单向链表的头指针和一个要删除的节点的值,定义一个函数删除该节点。返回删除后的链表的头节点。2 题目分析 遍历链表,删除目标节点只需要将其前一个节点的next指针指向目标节点的next即可。需要注意的是几个边界条件:头结点为空:直接返回null头结点就是要删除的元素:返回head.next3 代码/** * Definition for singly-linked list. * public class ListNode { *原创 2021-07-20 09:35:41 · 59 阅读 · 0 评论 -
17.打印从1到最大的n位数
17 打印从1到最大的n位数1 题目描述 输入数字 n,按顺序打印出从 1 到最大的 n 位十进制数。比如输入 3,则打印出 1、2、3 一直到最大的 3 位数 999。2 题目分析 第一种解法:直接计算出n位数的最大值,然后依次输出即可。但是面试中往往会让你考虑大数问题,因此就有了第二种解法:采用字符串和全排列来解决。/* 注意:这里LeeCode给出的返回值是int型数组,因此不存在大数问题,但是一般面试的时候肯定是想让你 考虑大数问题的,故采用字符串全原创 2021-07-15 17:26:57 · 68 阅读 · 0 评论 -
16.数值的整数次方
16 数值的整数次方1 题目描述 实现 pow(x, n) ,即计算 x 的 n 次幂函数。不得使用库函数,同时不需要考虑大数问题。2 题目分析 考虑次方的正负问题,分治的思想:2 2^2 2^4 2^8…这样能将时间复杂度降到O(logn)具体实现采用递归思想,测试用例:x=0,1n=0,1n为负数边界溢出正常用例3 代码public double myPow(double x, int n) { long N = n; return原创 2021-07-15 17:26:23 · 47 阅读 · 0 评论 -
15.二进制中1的个数
15 二进制中1的个数1 题目描述 请实现一个函数,输入一个整数,输出该数二进制表示中1的个数。2 题目分析 之前写的一篇文章位运算的巧妙应用提到n&(n-1)可以消除掉最末尾的1,可以根据这个特性来计算1的个数public int hammingWeight(int n) { // return Integer.bitCount(n); int count = 0; while (n != 0) { count++; n &原创 2021-07-17 10:20:40 · 47 阅读 · 0 评论 -
14.剪绳子
14 剪绳子1 题目描述 给你一根长度为 n 的绳子,请把绳子剪成整数长度的 m 段(m、n都是整数,n>1并且m>1),每段绳子的长度记为 k[0],k[1]…k[m-1] 。请问 k[0]k[1]…k[m-1] 可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。2 题目分析思路一:动态规划思想dp[i]表示长度为i的绳子剪成若干段后的最大乘积。初始dp[2] = 1,dp[3] = 2,当i>=2时,假设原创 2021-07-15 17:25:56 · 41 阅读 · 0 评论 -
13.机器人的运动范围
13 机器人的运动范围1 题目描述 地上有一个m行n列的方格,从坐标 [0,0] 到坐标 [m-1,n-1] 。一个机器人从坐标 [0, 0] 的格子开始移动,它每次可以向左、右、上、下移动一格(不能移动到方格外),也不能进入行坐标和列坐标的数位之和大于k的格子。例如,当k为18时,机器人能够进入方格 [35, 37] ,因为3+5+3+7=18。但它不能进入方格 [35, 38],因为3+5+3+8=19。请问该机器人能够到达多少个格子?2 题目分析 同样的二维数组的遍历问题,考虑回溯法解原创 2021-07-15 17:24:43 · 91 阅读 · 0 评论 -
12.矩阵中的路径
12. 矩阵中的路径1 题目描述 请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一格开始,每一步可以在矩阵中向左、右、上、下移动一格。如果一条路径经过了矩阵的某一格,那么该路径不能再次进入该格子。例如,在下面的3×4的矩阵中包含一条字符串“bfce”的路径(路径中的字母用加粗标出)。[[“a”,“b”,“c”,“e”],[“s”,“f”,“c”,“s”],[“a”,“d”,“e”,“e”]] 但矩阵中不包含字符串“abfb”的路径,因为原创 2021-07-14 17:20:39 · 144 阅读 · 0 评论 -
11.旋转数组的最小数字
旋转数组的最小数字1 题目描述 把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如,数组 [3,4,5,1,2] 为 [1,2,3,4,5] 的一个旋转,该数组的最小值为1。示例:输入:[3,4,5,1,2]输出:12 题目分析 这道题最直观的解法就是依次遍历数组,直到碰到下一个元素比当前元素小的值时,返回下一个元素;或者遍历完都没碰到,说明所有元素相同,返回第一个即可。 其实根据示例很容易发现,旋转原创 2021-07-14 17:20:07 · 63 阅读 · 0 评论