剑指offer
剑指offer专栏
Minkey_Myy
这个作者很懒,什么都没留下…
展开
-
剑指 Offer 41: 数据流中的中位数
首先,因为是三个函数都要使用的队列,所以声明要写在函数外,类体的开头。还有个要注意的,由于两个队列存的值都是int类型,所以除出来的结果也是整数,最后才会转为double,所以得除2.0!还有最好用offer()和poll()而不是add()和remove()!addNum\textit{addNum}addNum: O(logn)O(\log n)O(logn),其中 nnn 为累计添加的数的数量。空间复杂度:O(n)O(n)O(n),主要为优先队列的开销。原创 2023-07-06 22:04:16 · 134 阅读 · 0 评论 -
剑指 Offer 17: 打印从1到最大的n位数
这里注意,1是List的实现是用ArrayList,2是从1开始(10是第一个二位数的值),3是终止条件是当前长度等于位数限制,4是递归调用每次在结尾加一个小的值,并且当前长度也要加1,5是注意字符串的拼接方便和最后使用parseInt将其转化为int值的过程,6是在返回的时候想取List中的值要用for来做。首先,List类型的要么存Integer,要么存int[],并且后面实现是用ArrayList!这边的i和j应该从1开始,因为0不能做高位也不能做位数。原创 2023-07-06 17:41:10 · 205 阅读 · 0 评论 -
剑指 Offer 37: 序列化二叉树
然后从前往后判断,由于层序遍历的规则很简单,第一个的子节点是2,3,如果为空那么这个节点就是根节点没有这个子节点(约等于跳过)。Integer中的parseInt可以将字符串转为int类型的变量,很好用。这道题很复杂,首先需要发现是层序遍历,因为只有层序遍历才是这个顺序,并且new就可以调用,说明这里里面就生成了一个新的String(可以new一个String),给StringBuilder初始化一个],然后给队列加进根节点,和经典层序遍历不同的是这里为空也要加进队列,只是不给它分配左右节点。原创 2023-07-06 17:18:29 · 104 阅读 · 0 评论 -
剑指 Offer 67: 把字符串转换成整数
这道题使用的办法是先判断是否有符号,把空格消除,然后用一个标志位判断是否为负数。当遇到这种问题记得调用Integer中的MIN_VALUE和MAX_VALUE这样最方便。原创 2023-07-06 16:26:29 · 84 阅读 · 0 评论 -
剑指 Offer 43: 1~n 整数中 1 出现的次数
2 3 0 4 千位和百位可以选00 01 02....22 十位可以取到1( 形如[00|01..|22]1[0-9] 都是<2304 ) 个位可以选0-9 共有 23 * 10 中排列 当千位和百位取23,如果十位取1 那就是形如 231[0-9] > 2304,所以当千位和百位取23,十位只能能取0,个位取0-4即 2300 2301 2302 2303 2304 但是2301不应该算进来,这个1是 单独 出现在个位的(而11,121,111这种可以被算多次) 即 23*10。这样的确是最简单的。原创 2023-07-06 11:21:29 · 69 阅读 · 0 评论 -
剑指 Offer 44: 数字序列中某一位的数字
这里很奇怪,但是在调试的时候发现是越界了!导致nine*plus超出了int的表示范围。这里要不用Long类型的值来表示,要不用除法代替乘法。下次一定要注意大数越界的事情!这道题的规律是10*1,90*2,900*3,9000*4……这道题只需要判断每一个临界点,找规律找的很对。这里错的原因是因为没有仔细审题,下标5对应的值是5,所以应该是减去9才对。原创 2023-07-06 10:34:59 · 73 阅读 · 0 评论 -
剑指 Offer 62: 圆圈中最后剩下的数字
这有点像一个首尾相连的链表,但是那样时间复杂度太高了,这道题找规律,很巧妙,从尾部的结束状态开始往开头推,每次判断当前被保留下来的数在上一轮循环里是几?只要加上走了的路径除以那个时候的长度状态就可以。原创 2023-07-05 22:41:16 · 41 阅读 · 0 评论 -
剑指 Offer 57 - II: 和为s的连续正数序列
首先看到返回值和题给条件明确这是一个滑动窗口任务/可变数组 ,所以要使用List,然后其中元素又是数组,所以类型为int[],构造使用ArrayList,left不能从0开始,返回的时候由于是List,所以创立一个和vec一样大小的一维数组后面不给值,按照Int[]的元素遍历后写入res中然后返回。这里从0开始先然是不对的!滑动窗口思想很好用,相等了必定移动左边!原创 2023-07-05 22:02:49 · 117 阅读 · 0 评论 -
剑指 Offer 14- II: 剪绳子 II
这道题错的原因在于res在存储过程中会越界,最轻微的上溢是 INT_MAX + 1 :结果是 INT_MIN。最严重的上溢是 INT_MAX + INT_MAX :结果是 -2。最轻微的下溢是 INT_MIN - 1 :结果是 INT_MAX。最严重的下溢是 INT_MIN + INT_MIN :结果是 0。最大值加1时,反而变成范围的最小值,加2变成范围的次小值,这种情况可以想象出一个循环。这道题不能使用动态规划来解决,因为会越界。用贪心算法找规律可以得到答案(3越多越好,小于等于4取本身的值)原创 2023-07-05 19:06:55 · 254 阅读 · 0 评论 -
剑指 Offer 14- I: 剪绳子
这个地方使用dp数组来存储当值为i时候的结果,当到达n的时候说明答案就是这个。这里错的原因是一旦dp多申请了一个空间,那么就需要在循环的时候加个等于号!每次[i-j]和[j]就是在试组合!循环从4开始,因为小于4的结果都被返回了。这道题在代码随想录看到过,就是拆分成若干个2和3的乘积,然后另一个数不能再分的时候是最大的。但是那样太主观了,这道题正常思路就是使用动态规划来解决。原创 2023-07-05 17:50:03 · 48 阅读 · 0 评论 -
剑指 Offer 66: 构建乘积数组
这道题第一反应就是暴力解法,从左往右遍历两个for循环,这种情况的时间复杂度是O(N^2),明显不是个好办法。用左右乘积列表来表示是个非常好非常巧妙的办法!每个元素都设定一个左右的乘积数组,最后答案就是将这两个值相乘即可。上面的空间复杂度是O(N),但其实空间是可以省略下来的,只要使用两个循环,先从左往右乘一遍,再往右从左乘一遍(右往左的时候用一个值保存右往左的乘积和)。原创 2023-07-05 17:23:10 · 49 阅读 · 0 评论 -
剑指 Offer 39: 数组中出现次数超过一半的数字
或者可以先排序,中位数就是要找的数(因为占了一半所以排序后的数组中间值一定为答案)。这样的时间复杂度是O(N*log(N))有个很妙的方法,就是分为大数和其它两个,使用count来区分。这样的时间复杂度是O(n)。看到这道题两个想法,一个是暴力(用一个临时数组存);原创 2023-07-05 16:55:14 · 51 阅读 · 0 评论 -
剑指 Offer 19: 正则表达式匹配
这道题就是状态的判断:是否两个都为0?只有两个都为0才为true,并且判断*,有两个情况:为*或者不为*,不为*直接前移判断比较,否则就两种情况,忽略或者比较二选其一,只要两个或有一个成立即可!可能存在一个现象,就是aaab,然后a*ab,那么这样*只能代表一个a。这道题可以使用动态规划的方式来解决。原创 2023-07-04 10:56:32 · 61 阅读 · 0 评论 -
剑指 Offer 56 - II: 数组中数字出现的次数 II
这里的方法很巧妙,也是运用位运算的方法,因为每个数它在每个位上1的个数至多为1,而如果重复了3次那么这个就是为0或者3,所以可以使用一个length*32的双重循环来解决。并且还是要注意优先级的问题!这道题比上道题复杂得多,因为是3所以不能使用异或的方法。原创 2023-07-03 16:11:38 · 34 阅读 · 0 评论 -
剑指 Offer 56 - I: 数组中数字出现的次数
这道题有点想先排序,但是看到空间复杂度只有O(1),所以也不能使用哈希表。但是思路和之前那个找出所有数中唯一的数一样,全局异或开始!千万要记住,&的优先级比==还要低,所以以后在写符号运算类的题目时记得要打上括号或者和等号分开,一旦看到这种错误立马想到优先级问题!找出两个数中不同位的数!原创 2023-07-03 15:05:07 · 108 阅读 · 0 评论 -
剑指 Offer 65: 不用加减乘除做加法
1、公式是 s = n + c,但是题目不能用加号,所以只能循环套娃直到进位为0,这样就不需要加号了,此时s = n。2、为什么可以循环运算,因为求进位的时候左移了1位,低位补了0,所以最终结果进位一定会左移到0这个值,如果进位不能左移到0,那这题用这个思路就做不出来了。就是两个相同位置数值都为1的时候,那么就会往前进一位;两个都错开没有进位的时候,一个异或运算就可以得到结果(因为1+0=1,并且最后数字的表示也是1/0形式)。这道题想着使用一位一位进位的方法做,写这个的时候忘记了异或运算符!原创 2023-07-03 11:23:50 · 92 阅读 · 0 评论 -
剑指 Offer 15: 二进制中1的个数
这道题明显要用符号运算,傻逼了这还要想半天。注意:n&1的结果和c++不同,是1/0不是true/false!并且这里的n可能是负数!只能说不等于0绝对不可以说大于等于1!这里错了一个很重要的地方!就是右移首先用>>>才行,这样补0,如果用>>可能会高位补1!原创 2023-07-03 10:38:30 · 40 阅读 · 0 评论 -
剑指 Offer 60: n个骰子的点数
这道题就是按照上图思想,每一个后置(n)和前置(n-1)概率是相关的,就是对于每个位置上的概率再乘1/6然后加上,j+k就是这个索引的位置,所以不会漏掉。类似滑动窗口的,不是找规律!还有可以直接相等,将大小6的数组指向更大的就行!这道题首先看的出来概率两边是对称的,并且概率也是按照筛子个数上升的。原创 2023-07-03 09:57:12 · 46 阅读 · 0 评论 -
剑指 Offer 49: 丑数
这道题看了解答才知道原来是用基数(1)不断与(2,3,5)相乘,然后再将得出的结果重复这个操作。所以记录乘到哪个位置很重要。因为要记住,每个节点都有一次乘2/3/5的机会,所以从dp[0]开始给这个机会。然后判断是哪个位置的哪个(*2,*3,*5)项目被调用了,指针后移一位。这个用动态规划,本质上也是因为后面的丑数是由前面的生成的,所以对于乘几的处理这里非常巧妙,公用三个变量,移动到哪就使用哪个(使得这个数最小)。原创 2023-07-02 17:32:34 · 45 阅读 · 0 评论 -
剑指 Offer 48: 最长不含重复字符的子字符串
这道题本质上始终有用的就是左边界。而且这个左边界是取原来左边界和新重复边界的最大值。原创 2023-07-02 11:10:40 · 44 阅读 · 0 评论 -
剑指 Offer 46: 把数字翻译成字符串
除了使用一直除外,还可以使用s.substring(i-2,i)和compareTo()来比较是否大于等于"10"和小于等于"25"就可以了。当发现有两种操作方法,说明当前方法出错误了,必须立即想别的办法!假设3个数,第一个第二个可以组,第二个第三个可以组。123 && 字母3 && 1字母。原创 2023-07-02 10:18:38 · 175 阅读 · 0 评论 -
剑指 Offer 42: 连续子数组的最大和
这道题多了第二句,并且作为nums[i],当前值一定是要取的,只是要不要加上前面那个而已。原创 2023-07-01 23:02:38 · 84 阅读 · 0 评论 -
剑指 Offer 47: 礼物的最大价值
用for循,由于横纵坐标是固定的值(一条直线),所以i=0,j所有情况这种组合可以全部计算出来,当i=1时,j=0这里也会有值,那么j=1以及之后都有了(依赖[1][0],[0][1]),然后按照这个规律递推即可。都不为0可能是往两个方向来的,那么同样的节点后续节点权值是一样的,所以只要考虑前面走过来最小即可。这道题看的出来只能往右往下只能走4步。应该是每次i或者j加1!原创 2023-07-01 22:50:32 · 97 阅读 · 0 评论 -
剑指 Offer 63: 股票的最大利润
这里max设置0就会导致先行进入大于max的判断语句!不可以有前一项的影响,只能用来对比并不叠加。无语了,自己把问题想的太复杂了!原创 2023-07-01 22:27:46 · 66 阅读 · 0 评论 -
剑指 Offer 10- II: 青蛙跳台阶问题
首先一定要记住,第n个表示的是数组中的n+1个,所以这个等于不可以少!这道题和前面的很像。原创 2023-07-01 17:08:02 · 52 阅读 · 0 评论 -
剑指 Offer 10- I: 斐波那契数列
这个动态规划的后两个值和前面的那个值有关,所以循环从2开始,并且由于从2开始所以开始要有一个if判断值小于等于2的情况。可以使用常量空间来得到结果,但是sum必须和前面的sum无关!(因为dy已经起到了记录之前sum状态的作用)。原创 2023-07-01 16:52:54 · 43 阅读 · 0 评论 -
剑指 Offer 40: 最小的k个数
while循环在交换完毕i和j的值结束,然后进入下一轮比较交换->这个基数的循环比较才结束。这里的右边终点是length-1而不是length!原创 2023-07-01 11:50:23 · 50 阅读 · 0 评论 -
剑指 Offer 61: 扑克牌中的顺子
这道题的第一反应是先进行排序,然后遍历。Arrays类的sort对数组进行排序!也可以用HashSet排除重复情况。原创 2023-07-01 11:00:25 · 38 阅读 · 0 评论 -
剑指 Offer 45: 把数组排成最小的数
下面是安装传统快排模板写的,但是对于大数/需要排列数的比较还是需要使用字符串数组的方式,并且调用String中的valueOf函数来将所有数据类型转为字符串。这里千万注意,是设立的临时变量来移动!right和left万年不变!字符串也可实现比较,设定临时变量的原因是需要两个指针不停移动。原创 2023-07-01 10:39:20 · 37 阅读 · 0 评论 -
剑指 Offer 51: 数组中的逆序对
这道题归根结底就是一个归并问题,逆序对本质上就是比较大小,如果两边作为一个整体比较过那么就可以排序合并(因为这个过程每一步都计算了count的值,所以合并起来是可以的)。临时的数组十分重要,它的范围在每个循环的left到数组结尾的tmpK。下面的k应该是mid+1(从中间的右边一位开始取,不能等于)。原创 2023-06-30 12:45:40 · 304 阅读 · 0 评论 -
剑指 Offer 33: 二叉搜索树的后序遍历序列
这里有三个重要的地方, 第一个是当Start值大于等于End(即为只有一个节点时,此时已经满足条件);三是在递归中应该是Start而不是0,因为第二轮递归开始的时候(c -> End-1),这里的Start就又变成了c,所以Start值不是永远为0!这里有一个问题,这道题和上一道题不太一样,不用维护两个列表的值,因为前面和后面唯一的关系就是需要比较值,没有什么查找之类的操作。这道题运用的后序遍历一个很重要的性质:最后一个值是根节点,并且二叉搜索树的性质使得右子树的数全都大于左子树。原创 2023-06-29 18:18:40 · 97 阅读 · 0 评论 -
剑指 Offer 07. 重建二叉树
这里的思路是不太对的,因为root的位置时时在变动,只有位置很难去定位,而是通过一个HashMap来记录,每次键值是值,value是位置,只有知道了位置才可以计算左右子树的长度。如果没有这个就需要去每次线性的搜寻,非常花时间。下面这里搞错了找的值,找的应该是preorder中的root值!不是inorder的!并且左子树大小是根节点的值 - inStart。最后答案:根据中序分左右的特性,以及前序遍历和中序遍历节点计算的方法,使用类中定义HashMap的方式,以及依照性质递归的方法解决。原创 2023-06-29 16:53:01 · 100 阅读 · 0 评论 -
剑指 Offer 16: 数值的整数次方
这里要注意,Java中小数的除法(double)可以得到精确的结果,所以转换思路,使用二进制运算来加快运算速度。如果是负数就转为自己的倒数,正数只需要不停计算记录前面那个数的值,乘以标志位的最后一位即可。原创 2023-06-29 12:12:09 · 55 阅读 · 0 评论 -
剑指 Offer 38: 字符串的排列
这道题一看就是使用回溯法,但是由于是排列,所以索引和组合不同(组合的索引是从start标志位开始),排列是从下标为0开始,并且需要借助排序!所以需要先转化成将String转化为char[]类型,然后借用Arrays类的sort函数进行排序,排除可能重复的项目。而且String类型的数组每个元素等于一个对String列表使用get()函数,然后将对应元素存进String数组中。StringBuilder和StringBuffer的区别不仅只有速度和线程安全性的区别,还有调用函数的区别!原创 2023-06-27 16:22:20 · 113 阅读 · 0 评论 -
剑指 Offer 68 - II: 二叉树的最近公共祖先
这道题难度就加大了,因为不是搜索二叉树,这就意味着不能通过左右这种方法来解决问题。所以通过深度优先搜索算法,首先明白树的特点,其实和前面那题一样,也是如果左边有右边没有就是左边第一个,左边有右边也有就是根节点。然后只要利用深度优先搜索的特点,在第一个匹配的地方即设置返回条件(不遍历完全)。就可以和是不是搜索二叉树没有关系了!千万要记住,递归的方法对于全局变量是很不友好的!所以在这种情况下需要每次调用的时候声明一次才不会被覆盖,不然假设左边循环用完了到了右边那么那个得到的结果就会被覆盖掉。原创 2023-06-26 18:38:48 · 43 阅读 · 0 评论 -
剑指 Offer 68 - I: 二叉搜索树的最近公共祖先
所以就变得简单清晰了起来,只需要知道两个值和当前root比较哪个大,是一个还是两个大,如果都大于/小于就往那一边转方向,如果一个大一个小或者有一个是等于说明就是当前节点!千万要注意前面的一个判断条件并不是想表达的意思,a>b || c>b这个只要有一个满足就是true,所以千万不能是或,而应该是异或(有且只有一个条件满足)。这里其实第一个条件是有点多余的,主要还是不熟这个递归的方法,因为只有两个节点都在同一边的时候才会需要继续,不然就是找到正确结果(等于/有一个在左边一个在右边)原创 2023-06-26 17:27:41 · 43 阅读 · 0 评论 -
剑指 Offer 64: 求1+2+…+n
所以这里可以利用&&的性质来写题目,因为&&在前面为false后就不会进入第二个表达式,所以只需要第一个表达式想个办法判断n满不满足条件即可。这里就可以根据java的特性,n>1来判断,因为这个的返回结果就是true或者false,没有赋值的含义。后面再写个表达式搞个递归。搞个全局变量sum来接受每次得到的值。这道题看的第一反应就是使用个中间东西存起来,但是也不容易,因为不能使用while和for的话遍历是一个很大的问题。最奇葩的就是手动递归:)因为10000是上限,所以最多移动14次。原创 2023-06-25 18:14:32 · 51 阅读 · 0 评论 -
剑指 Offer 55 - II: 平衡二叉树
这道题第一眼看到的时候觉得和前面一道题很像,但是有区别,因为前面的针对左右两颗大的子树,这里是要求每颗子树都满足。有一个想法是可以对之前的程序加以改进,两边找一个最小和最大,相差不大于1即可。但是,这种想法的出发点就是错的!因为看深度只看两边最大值!并且递归每一个子树都满足才可以!这里思路错了,因为4,3,3,2这种也是对的但是显示就会出错!不是最小值,而是应该两边都找最大值,比较绝对值是否大于1。下面是正确的方法,这个递归用的很巧妙,只要不断下去让每个子树都进行一遍isBalanced的判断就可以。原创 2023-06-25 16:29:38 · 46 阅读 · 0 评论 -
剑指 Offer 55 - I: 二叉树的深度
看到这道题的第一反应就是使用类似层序遍历的方法,使用一个队列来储存各个节点,然后对于每一层使用一个遍历,这个遍历的上界是一次性定义的,就是使用java函数中对Queue的size()函数来获取队列的长度。然后对每一层的计数加一就可以得到最终这个树的层数(深度)。这个方法就说广度优先搜索的算法。还有一种更好的办法是使用递归的方法,这种方法的代码行数非常简洁。根部的值为1,再往上走就会迭代得到最想要的值。原创 2023-06-25 15:25:07 · 40 阅读 · 0 评论 -
剑指 Offer 54: 二叉搜索树的第k大节点
第一大也要走到右边节点一次,千万要记住别忘了要么新设置一个值要么this等于一下,还有如果在k==1的循环中不减去k,那么回到根节点的时候又满足条件,结果就会覆盖!并且可能会出现k减到负数的情况,为了不对正常k产生影响需要先添加一个判断。原创 2023-06-24 19:24:07 · 37 阅读 · 0 评论