![](https://img-blog.csdnimg.cn/20190918140213434.png?x-oss-process=image/resize,m_fixed,h_224,w_224)
《剑指Offer》-Java实现
文章平均质量分 82
用于实现《剑指Offer》书籍中的题目
不会街健的bboy不是一个好程序员
这个作者很懒,什么都没留下…
展开
-
数组 - 1.数组中的重复数字
int[] numbers:待排序数组length : 范围int[] duplication:找出重复数字后,把数字放置到此数组的0号下标。思路1:数组排序后,从前到后扫描数组即可,如果第i位和第i+1位数字相等,返回即可。时间复杂度:O(n log n)空间复杂度:O(1)public static boolean duplicate1(int numbers[],int len...原创 2020-01-30 17:49:29 · 143 阅读 · 3 评论 -
数组 - 2.二位数组中的查找
思路1:遍历数组,实现非常简单,嵌套for循环挨个查找。时间复杂度:O(n^2)空间复杂度:O(1);思路2:“删除”一行,或者一列,直到相等为止。我们从右上角开始查询,根据题目给的性质,我们可以这样进行判断,如果给定的数字比右上角小,那么就证明最右边的一列就不需要查询了,我们就可以“删除它”,因为最右边的一列从上到下就是递增的,如果比第一个个数还小,那么就说明这一列都比这个数大,所以...原创 2020-01-30 18:39:01 · 79 阅读 · 0 评论 -
字符串 - 3.替换空格
最简便思路:使用StringBulider工具类,一次遍历即可,遍历到空格直接append(%20)即可。public String replaceSpace(StringBuffer str) { StringBuilder stringBuilder = new StringBuilder(); for(int i = 0 ; i < str.leng...原创 2020-01-30 19:12:15 · 86 阅读 · 0 评论 -
链表 - 4.从尾到头打印链表
ArrayList保存的是链表中的元素。思路1:逆置链表通过三个指针保存节点,pre = null,current = listNode,next = listNode.next。从头到尾的遍历,next保存current.next(下一个节点),current.next 指向 pre 节点,pre 节点再指向current,current 指向 next,next 指向cur...原创 2020-01-31 17:20:50 · 81 阅读 · 0 评论 -
树 - 5.重建二叉树
思路:首先,我们要清楚前序遍历和中序遍历的性质,前序遍历的第一个节点,一定是根节点,并且在遍历完左子树和根之后,才会开始遍历右子树。中序遍历,先遍历左子树,在遍历根,再遍历右子树。所以不难发现前序遍历和中序遍历时,是同时遍历完左子树和根节点的,所以下标是重合的,也就是说前序遍历{1,2,4,6,3,5,6,8},中序遍历{4,7,2,1,5,3,8,6};根节点是1,那么在中序遍历中...原创 2020-01-31 20:12:19 · 79 阅读 · 0 评论 -
树 - 6.二叉树的下一个节点
思路:首先我们要清楚中序遍历的性质,遍历左子树,根,右子树。如果这个节点有右子树,那么下一个遍历的就是右子树的最左节点。如果这个节点是普通节点,并且它没有右子树,但是他是其父节点的左孩子,那么下一个节点就是父节点。如果这个节点是普通节点,没有右子树,并且是其父节点的右孩子。那么我们就需要向上遍历查找,找到一个节点(b)是其父节点(a)的左孩子,那么下一个节点就是 a 。如果找不到这样的节...原创 2020-01-31 21:10:26 · 82 阅读 · 0 评论 -
栈/队列 - 7.两个栈实现一个队列
思路1:栈的特点是后进先出,队列的特性是先进先出。如果在出栈前,把栈逆置,那么不就可以实现先进先出了么。我们在进行出队时,先把栈给逆置,也就是出栈再入到辅助栈中,最后辅助栈的栈顶出栈,那么就是出队了,入队就与入栈一样。 public class Solution { Stack<Integer> stack1 = new Stack<Integer>()...原创 2020-02-01 19:35:43 · 83 阅读 · 0 评论 -
递归/循环 - 8.斐波那契数列
思路1:递归实现,从给定的数字开始向前推算,推算的次数就等于它是第几项了。但是这个种算法会存在很大的问题,会存在冗余的计算,如果这个项数是比较大的,那么很有可能就会栈溢出了。 public int Fibonacci(int n) { if(n <= 0) return 0; if(n == 1) return 1; ...原创 2020-02-01 20:36:25 · 248 阅读 · 0 评论 -
数组 - 9.旋转最小数字
思路1:遍历,既然给定的数组是递增序列, 那么只要确定从那个数字开始后一位小于自己即可。然后再搬运回去输出0号下标即可。时间复杂度 O(n);空间复杂度O(1);效率相对低下。 public int minNumberInRotateArray(int [] array) { if(array.length == 0 || array == null) return 0; ...原创 2020-02-01 22:55:09 · 89 阅读 · 0 评论 -
回溯法 - 10.矩形中的路径
思路,一开始完全没有思路,这道题我都没怎么看懂,官方给定的函数名 和 成员变量属实把我看傻了,所以我们先对变量进行解读。public boolean hasPath(char[] matrix, int rows, int cols, char[] str)char【】 matrix :这是给定的矩形数组int rows : 这个数组有几行int cols : 这个数组有几列char【...原创 2020-02-02 23:46:38 · 339 阅读 · 0 评论 -
递归/循环 - 11.青蛙跳台阶
最简单的情况就是只有一级台阶,所以只有一种跳法,就是跳一级,如果有两级台阶,那么就有两种跳法,分两次跳和,一次跳一级,一次跳两级。我们再来分析一下一般情况。我们把 n 阶台阶看成 n 的函数,记为f(n),当 n > 2 时,第一次跳的时候就有两种不同的选择,1.第一次跳一级,此时跳法的数目等于后面剩下的 n-1 阶台阶的跳法数目,也就是 f(n-1);2.第一次跳两级,此时跳法...原创 2020-02-03 00:09:56 · 108 阅读 · 0 评论 -
回溯法 - 12.机器人的运动范围
思路,同样是回溯法,我们延续同一种题,也就是 回溯法 - 10.矩形中的路径 进行改变即可。机器人从0,0号下标开始探索,首先每一个格子同样有四个方向可以去走,每走一个格子,就可以判断这个格子是否满足条件,满足则 count+1 ,并且进行标记,这位置已经判断过了(所以我们还是需要一个boolean类型的数组去标记)接着判断四周的格子,满足则count +1;同样是递归的思想。判断是否满足条...原创 2020-02-03 01:50:54 · 115 阅读 · 0 评论 -
动态规划、贪婪算法 - 13.剪绳子
剪绳子是动态规划和贪婪算法是典型问题。动态规划分析:我们先来简短的讲解什么是动态规划?动态规划是将一个大问题分解为小问题,小问题种还有小问题,我们对最小单位的小问题求最优解之后,那么向上推进,大问题也就是最优解了。动态规划的运用范围:如果面试题是求一个问题的最优解(通常是求最大值和最小值),而且该问题的能够分成若干个子问题,并且子问题之间还有重叠的更小的子问题,那么就可以考虑使用动态规...原创 2020-02-03 13:56:09 · 130 阅读 · 0 评论 -
位运算 - 14.二进制中1的个数
思路1,Java中有带符号右移,我们只要判断最后一位是否为1,进行统计即可。判断方法就是和 1 做与运算。 public int NumberOf1(int n) { int count = 0; while(n!=0){ count += n & 1; n = n >>>1; ...原创 2020-02-04 02:07:37 · 369 阅读 · 0 评论 -
数学问题 - 15.数值的整数次方
也就是实现Math.pow(x,y);思路1:当底数为正数时,直接进行乘积即可。但是但指数为负数时,我们要把它变为整数,进行乘积,然后再求倒数。既然有了求倒数,那么分母是不能为0的。所以我们要在进行运算前特殊值要进行判断。public double Power(double base, int exponent) { if(exponent == 0) return 1;...原创 2020-02-04 02:45:26 · 84 阅读 · 0 评论 -
数学/字符串- 16.打印从1到最大数n的位数
题目:输入数字n,按顺序打印从1到最大 n 位十进制数,比如输入3,则打印1、2、3一直到999思路1:直接打印获得最高位数打印即可。 public void solution1(int n ){ if(n <= 0) return; int Max = (int)Math.pow(10,n+1); for(int i = 1; i &l...原创 2020-02-05 19:10:23 · 105 阅读 · 0 评论 -
链表 - 17.删除链表节点
题目:在O(1)的时间内删除链表节点。给定单链表的头节点和指定删除的节点。定义一个在函数O(1)的时间内删除该节点。思路:一般来说删除链表节点是遍历下一个节点是否和删除的节点相等,然后直接改动引用即可。但是如果给定了删除的节点,也就相当于给定了删除节点的下一引用。我们无法获得前一个节点,那么我们就把内容于下一个节点做一个交换,这样我们就获得了删除节点的前一个节点了。常规的改动引用即可。但是有...原创 2020-02-05 19:10:24 · 86 阅读 · 0 评论 -
链表 - 18.删除有序链表中的重复节点
题目:在一个有序的链表中,重复的节点。思路:定义两个指针一个pre一个current,两个相邻一起遍历,当current和pre的val相等时,current接着向后移动,直到不相等为止此时pre直接指向current直到current为null为止 public ListNode deleteRepeatNodes(ListNode head){ if(head =...原创 2020-02-06 00:09:42 · 84 阅读 · 0 评论 -
正则表达式 - 19.正则表达式匹配
思路:“.”就很简单了,如果模式的字符是"." 那么就默认相等。当模式中的第二个字符是“ * ”时:如果字符串第一个字符跟模式第一个字符不匹配,则模式后移2个字符,继续匹配。如果字符串第一个字符跟模式第一个字符匹配,可以有3种匹配方式:1.模式后移2字符,相当于x* 被忽略,相等于x出现0次例如:字符串:aaa, 模式:ab * aa,第一个字符已经匹配,现在匹配第二个字符,现在发...原创 2020-02-06 02:05:26 · 280 阅读 · 0 评论 -
字符串 - 20.表示字符的字符串
思路:数字分为:整数部分,小数部分,指数部分。[[A].[B]] ,[[A]e\E[A]] 或者 [[A.B]e\E[A.B]]我们可以对每一个部分写一个检索函数,整数部分:第一个字符可以是“+”或者“-”;在遇到下一个符号之前(必须扫描至少一位),扫描的字符只要都是0-9,那么整数部分就是正确的。小数部分:当扫描到字符“.”时我们要进行判断,小数点的前后仅有一个为止没有数字都可以表示正...原创 2020-02-06 14:26:47 · 323 阅读 · 0 评论 -
数组 - 21.调整数组的顺序使奇数位于偶数之前
思路1:直接遍历数组遇到偶数就移动到下一个奇数后(类似插入排序)但是这种算法的时间复杂度会很高,每碰到一个偶数就要移动O(n)个单位。算法复杂度O(n^2);非常低效率优化思路1,提供一个相同长度的大小的数组,原数组遍历,遇到偶数放到新数组的最后一个位置,奇数顺序放入新数组。这样算法复杂度会降低到O(n),但需要空间复杂度O(n)的支持再度优化,直接定义两个指针,一个从前向后找偶数,一个从后...原创 2020-02-06 22:50:11 · 89 阅读 · 0 评论 -
链表 - 22.链表中倒数第K个节点
思路:直接定义两个指针间隔是k-1。后面的指针指向为最后一个节点时,返回前面的指针即可。 public ListNode FindKthToTail(ListNode head,int k) { if(head == null || k == 0 )return null; ListNode left = head; ListNode rig...原创 2020-02-06 23:04:03 · 69 阅读 · 0 评论 -
链表 - 23.输出链表的入环节点
思路1:首先入环节点一定要保证有环。所以第一步我们先检测它是否有环。我们可以想想一下在操场的跑道上,一个人走的块,一个人走的慢,他们总会相遇。根据这个定律,我们编写出检测出是否有环,并且能找到环中的一个节点。所以我们定义两个指针,一个fast快指针,一个slow慢指针,快指针一次走两个节点,慢指针走一个节点。当fast指向null时证明没有环,那么我们就直接返回,当fast和slow相遇时返...原创 2020-02-07 00:32:03 · 174 阅读 · 0 评论 -
链表 - 24.反转链表
思路:定义三个指针,一个保存前一个节点(pre),一个保存当前节点(current),一个保存下一个节点(next)。我们只需要遍历修改指向就可以了。pre初始值为 null,current 初始值为 head,next 为currnet的next节点。此时current的next指向pre,pre 指向current,current 指向next,next 指向current;直...原创 2020-02-07 00:50:34 · 80 阅读 · 0 评论 -
链表 - 25.合并两个有序链表
思路我们按照分治的思想(归并排序),我们只需要挨个遍历头节点,谁小谁链入新节点,链入的节点指向向后移动,直到两个链表都为空为止。 public ListNode Merge(ListNode list1,ListNode list2) { if (list1 == null && list2 == null) return null; ...原创 2020-02-07 01:57:49 · 79 阅读 · 0 评论 -
树 - 26.树的子结构
思路:第一步,在A树种先找到与B树根节点相同的节点。第二步,同时遍历B的同时遍历A查看结构是否相同,如果相同则B是A树的子结构。如果不是,返回第一步继续寻找,如果遍历结束了都没有找到再匹配的,那么B就不是A的子结构。public boolean HasSubtree(TreeNode root1,TreeNode root2) { if( root1 == null || ...原创 2020-02-09 22:51:56 · 67 阅读 · 0 评论 -
树 - 27.二叉树的镜像
思路:我们通过题目可以了解到镜像树实际上就是左右子树的交换,如果是叶子节点,也就是没有子树了,那就就直接退出即可。 public void Mirror(TreeNode root) { if(root == null) return; contrctMirror(root); } public void contrctMirror(Tree...原创 2020-02-09 23:13:46 · 81 阅读 · 0 评论 -
树 -28.对称二叉树
思路:我们可以思考一下如果一个二叉树是镜像的,那么根节点的左右孩子是相等的,并且第一层一直到叶子节点,左子树上的左节点和右子树上的有节点相同。我们思考一下遍历的方式,如果我们对左子树进行前序遍历的方式,也就是根,左子树,右子树,这样的方式遍历,对应右子树是什么样的呢?右子树对应的是根,右子树,左子树。所以我们就对左右子树进行不同的遍历方式一起遍历,如果一直到叶子节点都全部相同,那么这个树...原创 2020-02-10 00:27:00 · 103 阅读 · 0 评论 -
数组 - 29.顺时针打印矩阵
思路:这道题看似很简单实际上数组最头疼的就是边界问题,这道题目体现的淋漓尽致。顺时针打印,那么我们就根据四个方向去打印即可,关键是怎么控值打印的边界问题,以及怎么控值那些方向该打打印,那些方向不该打印。我们定义四个变量辅助我们去顺时针打印。left:左边界right:右边界top:上边界end:下边界如图所示,我们把数组分成了4个方向的箭头去打印。第一步:从左到右的打印也就...原创 2020-02-12 00:40:54 · 132 阅读 · 0 评论 -
栈 - 30.包含min函数的栈。
思路:首先要明白栈的时间复杂度如果为O(1),那么就只能是出栈或者栈顶元素。所以一开始最小值就在栈顶。但是实际上我们是按顺序入栈的,所以我们该怎么样直接找到栈中的最小值呢?创建一个辅助栈min’!每一次入栈时,就去找到当前栈的最小值,放到min的栈顶,不就可以了么。具体实现:第一次入栈那么最小值就是它,直接入到min栈即可。第二次入栈,对比当前的值和min的栈顶的大小,如果min栈的...原创 2020-02-12 00:57:24 · 57 阅读 · 0 评论 -
栈 - 31.栈的压入、栈的弹出
思路:直接创建一个辅助栈模仿入栈出栈的过程。先把压入序列的第一个数字压入辅助栈,此时判断弹出序列,如果此时栈顶是弹出序列的当前值,那么就弹出,压入序列和弹出序列的判断指针分别向后移动一个单位。此时向辅助栈中压入,压入序列的当前指针下标。判断此时辅助栈的栈顶是否是弹出序列指针对应的值,如果不是,那么就接着压入,直到找到栈顶为弹出序列的指针对应值为止。如果压入序列都遍历完了。那么弹出序列一定不对应...原创 2020-02-12 01:54:48 · 172 阅读 · 0 评论 -
树 - 32.从上向下打印二叉树
思路:这道题考察的还是遍历,是按层遍历,所以我们的思想就是当我们遍历道1节点时,我们要向容器里添加左右节点2和3,遍历到2节点时,在容器里添加 4 和 5 。此时容器里时3,4,5。遍历到3节点时,在容器里添加6和7。此时容器剩余4、5、6、7。由于是叶子节点所以没有子节点可以添加。此时我们不难发现,这就是个队列。这样子实现起来就容易了。public ArrayList<Integ...原创 2020-02-12 02:18:39 · 66 阅读 · 0 评论 -
树 - 32.从上到下打印二叉树 - 分层打印
思路:根据上一道题的思想,我们依旧采取队列的存储,只是将子节点存储到新的队列,把当前队列中的值全部获取存储到一个容器中,再存储到结果集中罢了。第一步,我们先把根节点放入队列,第二步,遍历队列,把队列中的节点的值放到一个容器中,用来保存当前这样一层打印的结果,判断这些节点是否有孩子节点,如果有孩子节点,顺序放入一个新的队列中。并且处理完一个节点就让一个节点出队。第三步,判断这一行的结果是否...原创 2020-02-12 14:27:59 · 122 阅读 · 0 评论 -
树 - 32.从上到下打印二叉树 - 之字形打印
思路,首先把根节点存储,这是树的第 0 层。第二层,由于要从右向左打印,而在第一层保存数据的时候,我们就需要把当前的节点的右孩子放入队列,左孩子后放入队列。第二次,由于要从左向右打印,所以,我们必须先获取第一层的左节点的左节点。所以在第二层我们就不能使用队列了,而是栈!重新梳理思路,第二层我们依旧左子节点先入栈,右子节点后入栈,这样,在第三层时我们就可以先获得右子节点。第三层,当前栈顶...原创 2020-02-12 15:24:36 · 130 阅读 · 0 评论 -
树 - 33.二叉搜索树的后序遍历序列
思路:二叉搜索树的特点:左孩子比根节点小,右孩子比根节点大。而后续遍历的特点是:先遍历左子树,再遍历右子树,再遍历根。所以根据二叉搜索树和后序遍历的特点,我们就能发现,根节点一定是最后一个元素。从头向后找第一个比根节点大的元素之前,是左子树,而找到的那个元素之后一直到根节点之前是右子树。第一步:找到根节点。也就是最后一个下标对应的值。第二步:找到右子树的第一个值,从前向后找到第一个比根节...原创 2020-02-12 21:35:09 · 155 阅读 · 0 评论 -
树 - 34.二叉树中和为某一值的路径
思路:首先根据题目条件,我们要获得一条路径的要求有两个,第一必须路劲和为目标值,第二,结束节点必须是叶子节点。并且由于给定的TreeNode并没有获得父节点的方法,所以我们在选择路径时必须把节点的值给添加到路径值中,否则无法获取到上一个节点的值。由于是最后添加的节点,无论找到与否,我们都要进行回溯寻找吓一条合理的路线,回溯之后就要删除,所以是后进先出,我们可以用栈来保存路径。递归方法我们...原创 2020-02-13 12:01:34 · 96 阅读 · 0 评论 -
链表 - 35.复杂链表的复制
思路1 :最简单的思路就是直接复制以next为一链的链表,在之后遍历原链表获取random的链表值,在复制链表上去从头到尾的寻找对应的random值。举例:原链表的节点 A ,在复制链表上节点为 A’ ,A的random指向C,那么 A’ 节点的random也应该指向C‘,但是是A‘链表所在的C’,所以我们需要从头到尾的去寻找C’这样时间复杂度就为O(n^2)思路2:我们依旧直接复制原...原创 2020-02-13 20:59:01 · 221 阅读 · 0 评论 -
树/链表 -36.二叉搜索树与双向链表
思路:我们不难发现二叉搜索树的中序遍历实际上就是一个排序的序列。中序遍历:左子树 ,根, 右子树我们可以将节点的 left 指向比它小的的节点,right 指向 比它大的节点。由于题目提供的节点类并没有指向父节点的引用,所以我们必须要保存好上一个比它小的节点,否则 left 就没有办法引用了。所以我们将 pre 也就是比它小的节点引用定义为全局变量。由于是一个双向链表,那么我们就要把...原创 2020-02-13 23:38:57 · 89 阅读 · 0 评论 -
树 - 37序列化二叉树
思路:序列化,我们可以从题目上看出序列化是按照层数去序列化,并且是从左到右的打印。所以很明显。这种序列化就是按层打印二叉树。使用队列,将当前节点放入队列,再遍历左右孩子再入队,最后toString返回即可。当然StringBulider效率会更高反序列化:反序列化我们同样采用一个队列,我们遍历字符串。index 表示遍历的字符下标初始值为0。第一步 :index下标的值new一个节点,也就是...原创 2020-02-18 02:32:00 · 95 阅读 · 0 评论 -
全排列 - 38. 字符串的排列
思路:全排列的算法思想:递归剪枝。把一个数组分为两段,一段是第一个元素,一段是全部元素。首先要确定第一个元素,而第一个元素可以实待排列的数组中的任意值。而除了第一个元素之外,的其余元素,做全排列。讲解以下为什么要这么做所以其实全排列就是做了两件事第一把所有数组中的元素与第一个交换,剩下的数组做全排列,如果没有元素可以全排列了。那么当前的一种情况,就诞生了,把它加入到结果集中。如果有重...原创 2020-02-20 16:22:27 · 106 阅读 · 0 评论