剑指offer75道题-leetcode-最优解
75道题的解题思路和最优解
!Y_M!
这个作者很懒,什么都没留下…
展开
-
剑指 offer 68 - 2 二叉树的最近公共祖先(递归)
1 题目描述2 算法思路思路:这题和上一题不同,上一题由于二叉搜索树的特性,因此更为简单,本题更为一般,因此必须遍历整个二叉树才行。递归算法:终止条件; 当超过叶子节点,返回null 当root等于q或者p,直接返回root 递归工作 递归左子树,返回值记为left 递归右子树,返回值记为right 返回值: 当left和right都为空,说明,root的左右子树中都不含p,q,返回null 当left和right都不为,说明p,q在两侧,返回r.原创 2020-07-28 07:46:49 · 125 阅读 · 0 评论 -
剑指 offer 68 - 1 二叉搜索树的最近公共祖先(递归)
1 题目描述2 算法思路思路:两个节点p,q,最近的公共祖先root,只会存在下面三种可能:p , q 在root的子树中,并且一定存在于两侧 p == root ,q在左右子树中 q == root,p在左右子树中算法:循环搜索,当root等于空跳出 当p,q都在右子树,就root,right 当p,q都在左子树,就root.left 否则说明找到了,跳出 3 代码class Solution { public TreeNode .原创 2020-07-28 07:46:40 · 136 阅读 · 0 评论 -
剑指 offer 67 把字符串转换成整数(越界问题)
1 题目描述2 算法思路思路:先去掉首部的空格 再看遇到的第一个字符 如果是正负号,那么就设置一个falg = 1代表正号,-1代表负号,0代表无符号 如果是数字,那么就开始进行计算,res = 0; res = res * 10 + num; 处理数字越界的情况,每次进行计算时,先判断res的大小是否越界 越界的情况分为两个,边界 = 2147483647 一个是res * 10的时候就越界了,boundary =2147483647 / 10 =...原创 2020-07-28 07:46:26 · 264 阅读 · 0 评论 -
剑指 offer 66 构建乘积数组(乘积数组)
1 题目描述2 算法思路思路:如果是直接乘,复杂度是O(n²),因此需要用一个更好的方法 这里就使用乘积数组。 正向遍历 在正向遍历时,让B[i] 等于 A[0] 一直乘到 A[i - 1] 也就是 B[i] = B[i - 1] * A[i - 1] 反向遍历 最后一个元素不用乘自己本身 因此B[i - 1] *= A[a.length] 一直到乘到A[i] 3 代码class Solution { public int[] c...原创 2020-07-28 07:46:20 · 127 阅读 · 0 评论 -
剑指 offer 65 不用加减乘除法做加法(位运算)
1 题目描述2 算法思路思路:十进制加法,可以分为三步走 1.先做不进位加法 2. 做进位 3.把前两个的结果相加 二进制同样适用。5---101 17---10001 1. 先做不进位加法 --- 10100 2, 记下进位 --- 10 3 把前两个相加 10110 --- 22 答案正确。 算法;不进位加法,可以得知结果是和异或的结果一致 进位和与运算的结果左移1位 ,相同对于负数的处理:在计算机系统中,数值一律用补码来...原创 2020-07-28 07:46:14 · 142 阅读 · 0 评论 -
剑指 offer 64 求1到n的和(短路效应)
1 题目描述2 算法思路思路:本题使用递归 终止条件的判断使用短路效应 n > 1 && sumNums(n - 1) // 当 n = 1 时 n > 1 不成立 ,此时 “短路” ,终止后续递归3 代码class Solution { int res = 0; public int sumNums(int n) { boolean x = ( n > 1 && sumNums(n - 1原创 2020-07-28 07:46:05 · 101 阅读 · 0 评论 -
剑指 offer 63 股票的最大利润(动态规划)
1 题目描述2 算法思路动态规划:状态定义:dp[i],代表以i结尾的子数组最大利润 转移方程: 前i日最大利润 = 前i - 1天的最大利润 和 第i天价格 - 前i天最低价格 的最大值 dp[i] = max(dp[i - 1], prices[i] - min(price[i] , minPrice) 初始化,dp[0] = 0, 返回值 dp[n - 1]3 代码class Solution { public int maxProfi..原创 2020-07-27 14:42:28 · 274 阅读 · 0 评论 -
剑指 offer 62 圆圈中最后剩下的数字(数学方法)
1 题目描述2 算法思路数学方法:每次筛选后,数组的长度就会-1,因此在进行下次索引判断的时候,就需要模上n - 1 那么反推,求出最后一个数字,在一开始那个数组中的索引即可 反推的流程 第一次,加上m个位置,然后对2取余 第二次,加上m个位置,然后对3取余 。。。 总结一下反推的过程,就是(当前index + m) % 上一轮剩余数字的个数。3 代码class Solution { public int lastRemaining(i...原创 2020-07-27 14:29:56 · 114 阅读 · 0 评论 -
剑指 offer 61 扑克牌中的顺子(Set + max - min < 5)
1 题目描述2 算法思路思路:一个顺序,例如,3,4,5,6,7,那么肯定有max - min < 5 因此再添加元素时,只要保证添加的元素不是重复的,且max - min < 5即可 接下来讨论大王出现的次数,算法;遍历5个数 遇到大小王直接跳过 遇到数字,添加进set,更新max,min 最后判断max - min <5 满足,返回true 不满足,返回false 3 代码class Solution { ..原创 2020-07-27 14:02:58 · 206 阅读 · 0 评论 -
剑指 offer 60 n个骰子的点数(动态规划)
1 题目描述2 算法思路动态规划:状态定义:dp[]为n个骰子的点数概率数组,pre[] 为n-1个骰子的点数概率数组 初始化:pre[] = {1/6,1/6,1/6,1/6,1/6,1/6} 状态转移方程: dp[x+y] = pre[x] * num[y]; //从最小值,一直更新到最大值 3 代码class Solution { public double[] twoSum(int n) { double[] pre ...原创 2020-07-27 13:50:20 · 131 阅读 · 0 评论 -
剑指 offer 59 - 2 队列的最大值(单调栈)
1 题目描述2 算法思路思路:首先思考,这个题目,为啥就和上一题是一个大题呢?原创 2020-07-27 13:26:26 · 162 阅读 · 0 评论 -
剑指 offer 59 - 1 滑动窗口的最大值(单调栈)
1 题目描述2 算法思路思路:使用一个单调栈来装最大值,每次窗口移动,都进行更新,栈首存放最大值 在往窗口添加元素后 先判断,当前窗口移出的元素是不是栈首的元素,如果是,就移出栈首,如果不是,就不管 然后将当前元素num[j] 从栈底添加,弹出所有比nums[j] 小的元素 此时栈顶就是最大值,更新res 3 代码class Solution { public int[] maxSlidingWindow(int[] nums, int k) ..原创 2020-07-27 07:57:17 · 197 阅读 · 0 评论 -
剑指 offer 58 - 2 左旋转字符串(substring函数、stringbuilder 和 string)
1 题目描述2 算法思路本题介绍三种思路,分别使用了substring函数、stringbuilder 和 string,由于string的不可变性,因此效率依次降低2.1 substring将str按照n的长度切分成两个部分,s1,s2 在s2 +s1即可2.2stringbuilder当substring不允许的时候,就使用这种方法 新建一个stringbuilder,先添加第n+1至末位的字符 再添加0到n的字符 将res转化为字符串返回...原创 2020-07-27 07:57:07 · 126 阅读 · 0 评论 -
剑指 offer 58 - 1 翻转单词顺序(双指针)
1 题目描述2 算法思路算法分析:s.trim() ---- 删除头尾空格 倒序遍历字符串,记录单词左右指针i , j 每确定一个单词的边界,就添加到res3 代码class Solution { public String reverseWords(String s) { s.trim(); int i = s.length() - 1; int j = i; StringBuilder ..原创 2020-07-27 07:56:58 · 138 阅读 · 0 评论 -
剑指 offer 57 - 2 和为s的连续正数序列(滑动窗口)
1 题目描述2 算法思路思路:设滑动窗口的左边界i,右边界为j 初始化 i = 1 ,j = 1 当窗口内的和 > target 时,窗口需要减小,i++ 当窗口内的和 <target 时,窗口需要增加,j++ 当窗口内的和 = target 时,需要添加当前的结果到res 终止条件:i < target / 23 代码class Solution { public int[][] findContinuousSequence(int..原创 2020-07-27 07:56:50 · 107 阅读 · 0 评论 -
剑指 offer 57 和为s的两个数字(双指针+正确性说明)
1 题目描述2 算法思路思路:利用两个指针,一头一尾,当和小于target,left++,当和大于target,right--算法:初始化双指针i , j 指向nums的双端 循环遍历:当i 和j 相遇就退出 s = nums[i] + nums[j] 如果s > target j-- 如果s < target, i++ 如果相等,返回 正确性说明:由下图可知,当s(i,j) 发生改变,变成s(i+1,j)的时候 排除掉的只是橘色..原创 2020-07-27 07:56:36 · 157 阅读 · 0 评论 -
剑指 offer 56 - 2 数组中数字出现的次数2(位运算)
1 题目描述2 算法思路思路:将所有数字,按位相加,然后对3取余,那么结果就是只出现了一次的数字。因为其它数字为1的位都可以被3整除。接下来的重点就是如何在遍历时,计算位数:有限状态机实在难以理解,因此选择了更简单的处理方式。有兴趣可以自己看使用与运算,获取num的最后一位, n1 = num & i 无符号右移, num = num >>>1 利用int[] count = new int[32]进行统计 然后对count对3求余,再利用左..原创 2020-07-26 17:05:45 · 135 阅读 · 0 评论 -
剑指 offer 56 - 1 数组中数字出现的次数(异或操作的妙用)
1 题目描述2 算法思路思路:题目要求数组出,唯二,只出现一次的数字,其它数字都存在两个 可以先分析,如果数组中只存在一个 出现一次的数字,那么全员异或,可以得到该数字。 可是本题存在两个,因此需要做一些改变。 第一个改变,就是希望找一个方法,可以将两个数字分到两个组中,这样就可以分别求出了 进行分组的方法 1、先进行全员异或,那么最后的结果相当于,存在的两个数a,b的异或结果,那么只要找到异或结果中为1的位,就可以找到a,b两个数在某一位上的差异,然后根.原创 2020-07-26 15:46:08 · 127 阅读 · 0 评论 -
剑指 offer 55 - 2 平衡二叉树(递归)
1 题目描述2 算法思路思路:此题基于上一题,《求二叉树的深度》,在此基础上只需要进行稍微修改即可 同样是后序遍历二叉树,求出每个节点的左右子树的深度, 如果深度差 < 2,就返回深度 如果深度差 > 2,就返回 -1 递归算法:终止条件 当root == null 时,代表越过了叶子节点,返回0 如果左右子树的深度 == -1,代表已经不是平衡二叉树了,直接返回-1 返回值: 当 深度差 < 2时,返回深度 max(depthL.原创 2020-07-26 14:55:53 · 150 阅读 · 0 评论 -
剑指 offer 55 - 1 二叉树的深度(递归,层序遍历)
1 题目描述2 算法思路思路:求深度,肯定需要遍历一遍所有的节点,这里采取后序遍历(也可以层序遍历,时间复杂度都是 n)使用递归:当前节点的深度 = 左子树的深度 和 右子树的深度 的较大值 再加上 1递归思路:终止条件:当root为空时,说明已经到了叶子节点,返回0 递归工作:(后序遍历) 计算左子树的深度 depth1 计算右子树深度 depth2 返回值,max(depth1,depth2) + 13 代码class Solutio...原创 2020-07-26 14:43:20 · 109 阅读 · 0 评论 -
剑指 offer 54 二叉搜索树的第k大节点(中序逆遍历)
1 题目描述2 算法思路思路:二叉搜索树,中序遍历的结果是递增的 中序遍历---左、根、右,因此如果我们逆着中序遍历的流程进行遍历 每次遍历k--,当k == 0时,就是res递归流程:终止条件,当节点root == null 时返回 递归右子树,dfs(root.right) 递归“根”,完成操作 如果k == 0直接返回 k-- 如果此时k == 0,res = root.val 递归左子树,dfs(root.left)3 代码..原创 2020-07-26 14:34:16 · 170 阅读 · 0 评论 -
剑指 offer 53 - 2 0~n-1中缺失的数字(二分)
1 题目描述2 算法思路思路:解决排好序的数字查找问题,可以使用二分 此处二分的条件并非是比较大小,而是看num[i] 是否等于i算法:初始化i = 0 , j = len - 1 循环二分,当 i > j 时跳出 m = i + (j - i) / 2; 如果nums[m] = m,说明在右半边 如果num[m] != m ,说明在左边 返回值:返回左边界 i3 代码class Solution { public int mi..原创 2020-07-26 14:24:15 · 127 阅读 · 0 评论 -
剑指 offer 53 - 1 在排序数组中查找数字 1(二分查找 + 取巧)
1 题目描述2 算法思路算法:初始化 左边界i = 0 ,右边界 j = len - 1 二分查找,当[i ,j] 碰到退出 m = i + (j - i) / 2 如果nums[m] < target ,代表target在右边,i = m + 1 如果nums[m] > target ,代表target在左边,j = m -1 如果nums[m] = target 查找右边界,i = m + 1 查找左边界,j = m - 1 返回值:.原创 2020-07-26 12:36:09 · 135 阅读 · 0 评论 -
剑指 offer 52 两个链表的第一个公共节点(双指针)
1 题目描述2 算法思路思路:用两个指针A,B分别指向两个链表的头,然后以相同的速度遍历,如果指针到了末尾,就从对方的头再来 由题目可知,链表不存在循环,因此只要存在交点,就一定可以通过这种追赶的方式找到。 两个链表长度分别为L1+C、L2+C, C为公共部分的长度,按照楼主的做法: 第一个人走了L1+C步后,回到第二个人起点走L2步;第2个人走了L2+C步后,回到第一个人起点走L1步。 当两个人走的步数都为L1+L2+C时就两个家伙就相爱了 如果不存在,那么最后,no.原创 2020-07-26 12:22:35 · 162 阅读 · 0 评论 -
剑指 offer51 数组中的逆序对(分治算法,归并排序)
1 题目描述2 算法思路思路:根据一个特例可知,当数组是升序的,那么逆序对就是0 当数组是降序的,那么每一个数字,都是与后面的数构成逆序对 因此,我们可以采取分治思路,将一个数组,分成若干个有序的数组,然后对其进行逆序对的计算 由上图可知,这种思想其实就是归并排序的思想,O(n) = nlogn对两个分别有序数组,求逆序对的过程进行分析:创建一个和数组等大的辅助数组,用来存放合并前的数字,因此空间复杂度 O(n) = n 合并思路 两个指针i 和 j 分别指...原创 2020-07-26 11:49:14 · 188 阅读 · 0 评论 -
剑指 offer 50 第一个只出现一次的字符(有序哈希表)
1 题目描述2 算法思路算法:初始化一个LinkedHashMap,注意此处的value 设置为Boolean,稍微优于integer 遍历字符串中所有的字符c 如果哈希表中不含c,就put(c,true) 如果已经包含了,就put(c,false) 由于是有序哈希表,记录了装入的顺序...原创 2020-07-26 11:02:24 · 89 阅读 · 0 评论 -
剑指 offer 49 丑数(动态规划)
1 题目描述2 算法思路思路:首先要通过性质可知,每一个丑数,必定可以由前面的丑数 乘以 2 / 3 / 5所得 因此,当前丑数,应该是前面的 xa *2 / xb * 3 / xc * 5中的最小值 那么如何求得xa,xb,xc呢 可知,丑数是按照顺序存储的,因此当前丑数必定满足三个条件,即 xa * 2 > x > xa-1 * 2 //3 和 5的省略 动态规划算法:状态定义:dp[i] 代表第i + 1个丑数 转移方程: 首...原创 2020-07-25 21:55:36 · 123 阅读 · 0 评论 -
剑指 offer 48 最长不含重复字符的字符串(双指针+哈希表, 动态规划)
1 题目描述2 算法思路原创 2020-07-25 21:27:46 · 135 阅读 · 0 评论 -
剑指 offer 47 礼物的最大价值(动态规划)
1 题目描述2 算法思路动态规划解析:状态定义,dp[i][j] 代表从左上角开始,到(i,j)时,礼物的最大价值 转移方程: 当i = 0, j = 0,为起始元素 当i = 0,j != 0, 代表是第一行的元素,只能从左到右到达 当i != 0,j = 0,代表是第一列的元素,只能从上到下到达 当i != 0,j != 0 ,可以从左边,或者上边到达 初始状态: dp[0][0] = grid[0][0] dp[0][j] = grid[0]...原创 2020-07-25 06:21:42 · 172 阅读 · 0 评论 -
剑指 offer 46 把数字翻译成字符串(动态规划)
1 题目描述2 算法思路思路:假设第i - 1个数字,翻译的方法是dp[i - 1] 那么dp[i] 就拥有两个情况,如下图 一个是当前的数字可以和前一个数字进行翻译,也就是属于[0,25] ,那么dp[i] = dp[i -1] + dp[i -2] 如果当前的数字不可以和前一个数字进行翻译,那么dp[i] = dp[i - 1] 动态规划:状态定义:dp[i] 代表第i个数字的翻译数量 转移方程: 一个是当前的数字可以和前一个数字进行翻译,也就是属于[0,2.原创 2020-07-25 06:21:35 · 111 阅读 · 0 评论 -
剑指 offer 45 把数组排成最小的数(特殊的排序)
1 题目描述2 算法思路思路:这题实际上,本质上,竟然是一个排序题,只是在排序时,需要重新制定排序规则原创 2020-07-25 06:21:26 · 159 阅读 · 0 评论 -
剑指 offer 44 数字序列中某一位的数字(数学规律)
1 题目描述2 算法思路思路:根据下图可得规律,因此将找数分为三个步骤找n属于哪一个数位区间,也就是看它属于哪个[start,end] 确定它是哪一个数字,例如 456 最后确定 是456中的哪一个数字,例如:5算法流程:确定位数 循环的减去每个位数含有的数位数量count,当n <= count 时跳出, 此时状态,位数digit,起始数字start,往后第n个位数 确定所求数位所在的数字 这个区间一个数,占digit 个数位 求出是区间...原创 2020-07-25 06:21:16 · 118 阅读 · 0 评论 -
剑指 offer 43 1~n整数中1出现的次数(按位计算)
1 题目描述2 算法思路感觉是一个找数学规律的题,可以分别求出每位上1出现的次数,再相加数学规律如下:如图cur == 0high * digitcur == 1high * digit + low + 1cur > 1(high + 1) * digit3 代码class Solution { public int countDigitOne(int n) { int digit = 1; /...原创 2020-07-24 19:56:34 · 103 阅读 · 0 评论 -
剑指 offer 42 连续子数组的最大和(动态规划)
1 题目描述2 算法思路动态规划:状态定义:dp[i] 代表以第 i 个元素结果的数组的最大和 转移方程:dp[i] = Max( dp[i - 1] + nums[i] , nums[i]) 初始状态:dp[0] = nums[0] 返回值:返回dp列表中的最大值3 代码class Solution { public int maxSubArray(int[] nums) { int res = nums[0]; for(i.原创 2020-07-24 18:51:29 · 98 阅读 · 0 评论 -
剑指 offer 41 数据流中的中位数(两个堆)
1 题目描述2 算法思路思路:中位数的查找,本质就是找一个有序数组的中间的数,那么可以先对无序数组进行排序。 还有一个思路就行,在进行添加时,就按照顺序进行添加,如此一来就可以减小查找时的时间复杂度 本题就使用两个堆来实现,一个大顶堆,一个小顶堆 小顶堆用来存放较大的那一半数,如果是奇数,那么多出来的一个放在小顶堆 大顶堆用来存放较小的那一半 查找时: 如果是奇数,直接返回小顶堆 堆顶元素 如果是偶数,直接返回(大顶堆顶 + 小顶堆顶) / 2 算法: 设..原创 2020-07-24 15:35:00 · 163 阅读 · 0 评论 -
剑指 offer 40 最小的k个数(堆 + 快排)
1 题目描述2 算法思路2.1 堆思路:利用堆数据结构来辅助得到最小的k个数 堆的性质是可以找到最大或者最小的元素 我们可以使用一个大小为k的大顶堆,将元素依次遍历加入堆,如果堆的大小超过了k,就将最大的元素弹出 如此就保证了堆中的元素都是当前最小的k个元素 时间复杂度 nlogk 这里的大顶堆选择的是 Java中的PriorityQueue PriorityQueue默认是小顶堆,需要重写比较器,使其变为最大堆2.2 快速排序(选择)2.3 比较...原创 2020-07-24 15:11:55 · 126 阅读 · 0 评论 -
剑指 offer 39 数组中出现次数超过一半的数字(摩尔投票法)
1 题目描述2 算法思路本题常见解法如下:1.哈希表统计法:遍历数组| nums , 用HashMap统计各数字的数量,最终超过数组长度一半的数字则为众数。 此方法时间和空间复杂度均为O(N)。 2.数组排序法:将数组nums 排序,于众数的数量超过数组长度-半, 因此数组中点的元素-定为众数。仿法时间复杂度O(Nlog2N)。 3.摩尔投票法:核心理念为 “正负抵消”;时间和空间复杂度分别为O(N)和O(1) ;是本题的最佳解法。摩尔投票法: 假设众数是 x投票...原创 2020-07-24 10:26:19 · 140 阅读 · 0 评论 -
剑指 offer 38 字符串的排列(回溯)
1 题目描述2 算法思路思路:深度优先遍历,先固定第一个字符,再固定第二个字符,最后固定第n个字符,然后回溯得到所有情况。剪枝:当字符串中存在重复的字符时,就可以会出现重复的方案。因此在固定字符时,可以确保每个字符只在此处固定一次,即遇到重复的字符不交换,直接跳过。递归流程:当x = len(c) - 1 ,就代表固定完所有位了,将当前组合添加到res 递归参数:当前的固定位x 递归工作:初始化一个Set ,用来存储已经固定的字符,可以排除重复的字符,将第x位字符和 .原创 2020-07-24 10:12:05 · 129 阅读 · 0 评论 -
剑指 offer 37 序列化二叉树(层序遍历BFS)
1 题目描述2 算法思路2.1 序列化序列化思路:序列化,其实就是层序遍历,为了能够完整的体现序列化二叉树的信息,这里选择将所以叶子节点的null 节点都输出,因此会比题目中的null 要多,例如 4 和5 的null 值都会出现,但是这个并不影响序列化算法:特值:当root为空时,直接返回空列表 初始化:队列queue, 包含root 层序遍历:当queue为空时,就跳出 节点出队,记作node 如果节点不为空,就打印,并将左右子节点加入queue 如果为.原创 2020-07-24 08:33:37 · 172 阅读 · 0 评论 -
剑指 offer 36 二叉搜索树与双向链表( 递归中序遍历)
1 题目描述2 算法思路思路:使用中序遍历遍历每个结点 在遍历的途中,构建前驱指针 遍历结束后,最后构建头和尾的循环指针即可算法:特值:当结点为null时,直接返回 初始化空结点pre 转化为双向链表,递归进行中序遍历 终止条件,当cur为空 递归左子树dfs(cur.left) 构建链表 当pre为空时,代表这在访问头节点head 当pre不为空时,修改双向结点引用,pre.right = cur; cur.left = pre; 保存cur原创 2020-07-24 08:33:30 · 95 阅读 · 0 评论