leetcode
包括但不限于leetcode的刷题记录
zhazha_hui
我是一条狗,常在岸上走。
展开
-
二分碎碎念
在刷Leetcode中,常常见到一个序列,被切分为处于不同状态s1s_1s1和s2s_2s2的两个部分,例如[s1,s1,s1...s2,s2,s2][s_1,s_1,s_1...s_2,s_2,s_2][s1,s1,s1...s2,s2,s2],要求找到s1s_1s1和s2s_2s2切分点。这里记录一下。可以利用如下模板进行解题。 // left和right分别为列表的左边界和右边界 int left = 0, right = nums.length - 1; // 注意这原创 2021-06-18 16:12:45 · 253 阅读 · 0 评论 -
934. 最短的桥
这里先用深度优先遍历,先找到第一座岛屿,将它们的邻接的海洋全部保存,然后从这些邻接海洋出发,利用广度优先遍历。直到遇到另外一座岛屿。class Solution { int[][] A; Queue<List<Integer>> area = new LinkedList(); // 记录第一个岛的所有邻接点 int[] dir = new int[]{0, 1, 0, -1, 0}; public int shortestBridge(in原创 2021-02-05 11:03:32 · 114 阅读 · 0 评论 -
剑指 Offer 43. 1~n 整数中 1 出现的次数
遇到难题就要面对复制粘贴编程。嘿嘿嘿。《剑指offer》上面的解题方法太复杂。Krahets大佬的题解就要简单多了,思考,复制粘贴,跑路。一气呵成。背下来就完事了。题解链接class Solution { public int countDigitOne(int n) { int cur = n % 10; int high = n / 10; int low = 0; int res = 0; int digit =原创 2021-02-05 09:37:12 · 90 阅读 · 0 评论 -
417. 太平洋大西洋水流问题
典型的搜索算法,不过这次搜索的目标有两个,到达太平洋和大西洋。需要写两个dfs递归函数。算法流程遍历图中所有点,调用深度递归函数。如果该点既能到达太平洋又能够到达大西洋。那么将这个点加入结果集。返回结果集合这里的递归算是深度优先遍历的模板步骤吧。不过这里标记是否访问用了一个小技巧,修改原数组,因为河流高度不能为0,所以将访问过的点记录为-1,表示已经访问。递归流程边界判定:判定越界以及是否访问返回条件:点处于边界递归工作:搜索高度比目前高度低的邻近节点,调用dfs。有一个点能够到达边原创 2021-02-03 12:13:26 · 276 阅读 · 2 评论 -
323. 无向图中连通分量的数目
基本上算是一道并查集的模板题目了。这题有两种解法:转化成一个图,用邻接链表表示。然后用DFS或者BFS去搜索用并查集去搜索。求连通分量一般都用这招。并查集相关的复杂度。相关的解释可以看看零神的讲解——借这个问题科普一下并查集各种情况下的时间复杂度优化平均时间复杂度最坏时间复杂度无优化O(logn)O(\log n)O(logn)O(n)O(n)O(n)路径压缩O(α(n))O(\alpha(n))O(α(n))O(logn)O(\log n)O(logn原创 2021-02-02 11:41:02 · 661 阅读 · 0 评论 -
剑指 Offer 41. 数据流中的中位数
用一个最大堆和一个最小堆来记录数据,保证最大堆的数据的最大值小于最小堆的最小值。并且保持两者的数目之差不超过1。这样便可以以O(1)O(1)O(1)的时间里找到中位数。class MedianFinder { private PriorityQueue<Integer> maxHeap; private PriorityQueue<Integer> minHeap; /** initialize your data structure here. */原创 2021-02-02 09:32:10 · 75 阅读 · 0 评论 -
剑指 Offer 38. 字符串的排列
这里可以用排序并且比较来剪枝。和题目47. 全排列 II非常类似。复杂度分析时间复杂度:O(n!)O(n!)O(n!),排序字符串转换成的字符数组需要O(nlogn)O(n\log n)O(nlogn),而遍历所有的子串需要O(n!)O(n!)O(n!)。空间复杂度:O(n)O(n)O(n),递归栈的最大深度为字符串的长度,额外使用了长度为nnn的标记访问数组visitvisitvisit和字符数组charscharschars// 很典型的回溯算法,用一个visit数组标记是否访问// 注原创 2021-01-30 09:26:08 · 108 阅读 · 0 评论 -
剑指 Offer 37. 序列化二叉树
一道比较难的题目,构建二叉树的层序遍历序列倒是不难,难在反而行之,用二叉树的层序遍历序列构建二叉树。前者不必多说。后者解题技巧为:构建一个队列,同时维护一个遍历序列指针i。将序列第一个元素构建节点压入队列,记为head,i = 1。并反复进行如下操作,直到队列为空。最后返回head即可。队首出队,记作node构建node左子树,i++,并且将其入队构建node右子树,i++,并且将其入队算法复杂度:时间复杂度:O(n)O(n)O(n),nnn为节点的个数,需要访问所有的节点和额外的空节点。而原创 2021-01-29 09:39:59 · 69 阅读 · 0 评论 -
LeetCode 340. 至多包含 K 个不同字符的最长子串
用滑动窗口的办法来做这道题,用set保存当前集合的不同字母的个数。用长度为128的数组保存字符个数。// 试图用窗口法来做,用set来保存当前集合的字母的多少// k = 0的情况,s = null的情况class Solution { public int lengthOfLongestSubstringKDistinct(String s, int k) { int maxLen = 0; // 记录当前最长子串,初始为0 int l = 0;原创 2021-01-28 09:07:36 · 264 阅读 · 0 评论 -
剑指 Offer 35. 复杂链表的复制
这道题目的关键在于,每个节点还包含一个随机节点,随机地指向链表中的一个节点。我们不但要深度拷贝所有原链表的节点,还需要准确地让拷贝链表中随机节点的对应关系和原链表保持一致。假如我们先拷贝原链表,然后查找源链表中的随机节点对应关系,借此来拷贝随机节点。那么每个查找每个对应关系都需要付出O(n)O(n)O(n)的时间复杂度。拷贝整个链表的随机节点关系就需要O(n2)O(n^2)O(n2)的时间复杂度。哈希表先把节点全部复制一遍,并且将原节点和拷贝后的节点一一对应存储再哈希表中。再遍历一遍源链表和拷贝链表,根原创 2021-01-27 09:58:56 · 50 阅读 · 0 评论 -
剑指 Offer 34. 二叉树中和为某一值的路径
特别Ugly的回溯代码。太丑了。没有眼睛看。换一个吧class Solution { List<List<Integer>> res = new LinkedList(); public List<List<Integer>> pathSum(TreeNode root, int sum) { if(root == null) return new LinkedList(); Linke原创 2021-01-25 10:22:27 · 71 阅读 · 0 评论 -
剑指 Offer 33. 二叉搜索树的后序遍历序列
一个前序遍历序列和一个后序遍历序列都能够唯一确定一颗搜索二叉树。因为线索二叉树的特殊性质,左节点<父节点<右节点。而后序遍历序列为左节点|右节点|父节点。可以通过序列末尾的父节点,与序列的元素进行比较,就可以分割左节点遍历序列和右节点遍历序列。以此递推,就可以建立一颗搜索二叉树。递归工作递归参数:遍历序列的开始位置start和结尾位置end。将遍历序列posterorder作为成员变量供递归函数共享。终止条件:start >= end。说明该序列为空或者只有一个元素递归工作:从末原创 2021-01-25 09:21:55 · 126 阅读 · 0 评论 -
剑指 Offer 32 - III. 从上到下打印二叉树 III
这里将每一层的节点用双端队列temp保存起来,用tag标记奇偶层,当为奇数层时,节点陆续在队尾加入队列,而为偶数层时,节点陆续在堆首加入队列。class Solution { public List<List<Integer>> levelOrder(TreeNode root) { if(root == null) return new LinkedList(); boolean tag = true;原创 2021-01-24 09:48:20 · 59 阅读 · 0 评论 -
剑指 Offer 32 - II. 从上到下打印二叉树 II
这里需要分层打印,所以需要两个变量toPrint和nextPrint来记录剩余打印节点和下一层打印节点。再结合广度优先遍历搞定。class Solution { public List<List<Integer>> levelOrder(TreeNode root) { if(root == null) return new LinkedList<List<Integer>>();原创 2021-01-23 09:59:42 · 80 阅读 · 0 评论 -
剑指 Offer 32 - I. 从上到下打印二叉树
一枪秒了,有什么好说的。这种要按照层完成操作的,一般都需要用到广度优先遍历。class Solution { public int[] levelOrder(TreeNode root) { if(root == null) return new int[0]; Queue<TreeNode> print = new LinkedList(); ArrayList<Integer> res = new原创 2021-01-23 09:37:30 · 67 阅读 · 0 评论 -
剑指 Offer 31. 栈的压入、弹出序列
之前写的又不会了。难过。用一个栈来模拟进出序列,最后模拟栈为空,返回true。算法复杂度时间复杂度:O(n)O(n)O(n),虽然有两个循环,但是一个元素最多被操作两次,一次入栈,一次出栈空间复杂度:O(n)O(n)O(n),要新建一个堆栈,最坏的情况下,先全部入栈,再全部出栈。class Solution { public boolean validateStackSequences(int[] pushed, int[] popped) { Stack<Inte原创 2021-01-23 09:17:19 · 98 阅读 · 0 评论 -
剑指 Offer 30. 包含min函数的栈
做过了一遍再做一遍,思路掌握了基本莫得问题。这里要用两个堆栈来实现,一个保存最小值,一个保存数据。关键在于入栈操作,如何保证最小栈的栈顶始终是最小值。最小栈是否为空,为空,直接压入不为空,判断最小栈栈顶元素top和压入元素x的值若top < x,那么压入top反之,则压入xclass MinStack { Stack<Integer> minStack; //用于存放最小值的栈 Stack<Integer> dataStack; //用于原创 2021-01-22 09:15:54 · 106 阅读 · 4 评论 -
剑指 Offer 29. 顺时针打印矩阵
来源于Krahets大神的题解Krahets,永远的神。这种利用边界值来解决打印问题,既直观泛化性又好。妈妈再也不用担心我的打印了。用上下左右四个边界,并且在打印的过程中内缩边界。通过边界之间的比较来确定打印范围,避免了大量的索引比较操作,同时也不用分配访问数组来标记是否打印。代码简洁效率又高。妙哉,真的是神仙代码。别的不服,就服你了。class Solution { public int[] spiralOrder(int[][] matrix) { if(matrix ==原创 2021-01-22 08:57:32 · 118 阅读 · 1 评论 -
剑指 Offer 28. 对称的二叉树
一颗对称的二叉树,需要满足以下几个条件之一为空子节点均为空左子树的左节点等于右子树的右节点,而且左子树的右节点等于右子树的左节点根据这个性质可以递归,自顶向下,逐个节点进行判定递归递归参数:两个子节点leftleftleft,rightrightright终止条件:两个节点均为空,返回true,只有一个为空,返回false。递归条件:判断left.val==right.valleft.val == right.valleft.val==right.val,不相等,直接返回false,相原创 2021-01-21 14:37:38 · 101 阅读 · 0 评论 -
剑指 Offer 27. 二叉树的镜像
这题可以新建一个二叉树,进行复制粘贴的操作,不过这样比较麻烦.还可以在原地进行修改.通过后序遍历的方法,给每个节点都进行镜像操作.用后序遍历递归的方法递归参数:树节点root终止条件:root为空,返回递归工作:交换root的左右节点.class Solution { public TreeNode mirrorTree(TreeNode root) { mirror(root); return root; } private void原创 2021-01-19 08:29:25 · 68 阅读 · 0 评论 -
剑指 Offer 26. 树的子结构
参考大佬题解这题需要遍历A的每一个节点a,判断B是否为a的子结构。因此可以分为两步来做遍历A的每一个节点node函数isSubStructure,这里采用先序遍历判断B是否为node的子结构isSubisSubStructure函数递归参数:两个树节点返回条件:A或者B为空时,直接返回false。因为A为空B不为空,那么B必然不是A的子结构;B为空,空树不是任何树的子结构返回值:当满足以下三个条件之一,返回true。B是A的子结构B是A左子树的子结构B是A右子树的子结构i原创 2021-01-19 07:57:23 · 110 阅读 · 0 评论 -
剑指 Offer 25. 合并两个排序的链表
这题用递归和迭代都可以做,先看看迭代class Solution { public ListNode mergeTwoLists(ListNode l1, ListNode l2) { ListNode res = new ListNode(-1); ListNode head = res; while(l1 != null && l2 != null){ if(l1.val < l2.val){原创 2021-01-18 09:09:44 · 89 阅读 · 0 评论 -
剑指 Offer 24. 反转链表
可以用堆栈法和双指针法class Solution { public ListNode reverseList(ListNode head) { if(head == null) return null; ListNode node = head; Stack<ListNode> stack = new Stack(); // 将节点全部压入栈中 while(nod原创 2021-01-18 08:39:39 · 65 阅读 · 0 评论 -
剑指 Offer 22. 链表中倒数第k个节点
这种有序的序列多半是可以用双指针法的建立快指针slowslowslow和fastfastfast,分别表示当前元素和当前元素之后的k−1k-1k−1个元素slowslowslow和fastfastfast同时右移,直到遇到fastfastfast没有下一个元素返回slowslowslow// 链表也能双指针// 考虑k>链表长度和k<=0的情况class Solution { public ListNode getKthFromEnd(ListNode head, int原创 2021-01-17 09:17:01 · 72 阅读 · 0 评论 -
剑指 Offer 21. 调整数组顺序使奇数位于偶数前面
头尾指针法大体思想如下建立头指针leftleftleft和尾指针rightrightright,分别指向当前最左侧奇数元素的索引和最右侧的偶数元素的索引。初始化leftleftleft和rightrightright,分别为首索引和尾索引leftleftleft不断右移,直到指向偶数元素rightrightright不断左移,直到指向奇数元素交换nums[left]nums[left]nums[left]和nums[right]nums[right]nums[right]// 可以考虑用头原创 2021-01-17 09:01:43 · 49 阅读 · 0 评论 -
剑指 Offer 16. 数值的整数次方
这个问题毫无疑问应该用快速幂来做,不过问题在于考虑各种临界情况,比如底数为0的情况,这里默认全部返回0指数为负数的情况指数太大越界情况,这里题目不要求考虑指数处于临界值的情况。这是个大坑,如n=−2147483648n=-2147483648n=−2147483648Java 代码中 int32 变量 n∈[−2147483648,2147483647]n \in [-2147483648, 2147483647]n∈[−2147483648,2147483647],因此当 n=−21474原创 2021-01-14 08:43:07 · 100 阅读 · 0 评论 -
剑指 Offer 15. 二进制中1的个数
这里要注意的是可能输入的数是负数假如没有考虑到,就会有如下失败的解法class Solution {public: int NumberOf1(int n) { int cnt = 0; while(n){ if(n % 2) cnt++; n /= 2; } return cnt; }};这只能统计正数的1的个数,当原创 2021-01-13 09:17:46 · 81 阅读 · 0 评论 -
剑指 Offer 14- II. 剪绳子 II
和剪绳子1一样,这里问题在于求幂的问题很容易溢出。因此选择long作为开始的返回值,然后再转换为int类型返回。这里自己写的求幂运算非常耗时。算法复杂度时间复杂度:O(n)O(n)O(n),用于求解幂运算空间复杂度:O(1)O(1)O(1),没有使用额外空间class Solution { public int cuttingRope(int n) { if(n <= 1) return 0; if(n == 2)原创 2021-01-13 08:46:42 · 88 阅读 · 0 评论 -
剑指 Offer 14- I. 剪绳子
可以直接用动态规划来做这件事,记录f(i)f(i)f(i)为长度为iii的绳子的剪断所得最大乘积。那么转移方程为f(i)=max(f(i−j)∗f(i))f(i) = max(f(i - j)*f(i))f(i)=max(f(i−j)∗f(i))。算法复杂度时间复杂度:O(n2)O(n^2)O(n2),需要f(1)∼f(n)f(1)\sim f(n)f(1)∼f(n),每个长度段绳子又得遍历f(1)∼f(n2)f(1)\sim f(\frac{n}{2})f(1)∼f(2n)。空间复杂度:O(n)原创 2021-01-12 12:14:31 · 69 阅读 · 3 评论 -
78. 子集
又是一个回溯问题。class Solution { // 用于返回结果 List<List<Integer>> res = new LinkedList(); int[] nums; public List<List<Integer>> subsets(int[] nums) { this.nums = nums; subsets(0, new LinkedList<Integer&原创 2021-01-08 10:02:18 · 92 阅读 · 0 评论 -
剑指 Offer 12. 矩阵中的路径
这里使用了回溯算法,回溯算法是一种比较特别的DFS,它需要在达到搜索条件后,回溯上一次,继续搜索。普通的DFS适合找路径是否存在的问题,而回溯算法适合解决有几条路径的问题。这里给出DFS的模板dfs的模板```cppdfs(){ // 第一步,检查下标是否满足条件 // 第二步:检查是否被访问过,或者是否满足当前匹配条件 // 第三步:检查是否满足返回结果条件 // 第四步:都没有返回,说明应该进行下一步递归 // 标记 dfs(下一次)原创 2021-01-08 09:02:26 · 130 阅读 · 0 评论 -
剑指 Offer 11. 旋转数组的最小数字
这种有序的序列,或者被划分为两种状态的序列,很适合用二分法。不过这题的二分法很考验技术。以往的二分法都是==就返回,这题不一样,要规避掉重复数字的情况。另外,和谁比较也是个令人头痛的问题。一般比较的对象数组中的元素和目标值,例如在数组查找目标值数组中的元素和相邻元素比较这题比较的是右侧的元素,比较奇葩原本我以为比较左侧也行,但是无法解决不减序列的问题。算法复杂度时间复杂度:最坏情况全部都是重复数字O(n)O(n)O(n),一般情况是O(lgn)O(\lg n)O(lgn)空间复杂度:原创 2021-01-07 11:30:06 · 56 阅读 · 0 评论 -
剑指 Offer 10- II. 青蛙跳台阶问题
很简单的动态规划问题(我信你个鬼),跳nnn级台阶只能从n−1n-1n−1级台阶跳一步或者从n−2n-2n−2级台阶跳两部才行,所以我们就得到了状态转移方程f(n)=f(n−1)+f(n−2)f(n) = f(n - 1) + f(n - 2)f(n)=f(n−1)+f(n−2),剩下的就是和斐波拉契数列一样的求法了。算法复杂度时间复杂度:O(n)O(n)O(n),遍历n个数空间复杂度:O(1)O(1)O(1),没有引入额外的空间// 很简单的动态规划问题,设跳n级台阶跳法为f(n)// 那原创 2021-01-05 09:43:38 · 89 阅读 · 0 评论 -
剑指 Offer 10- I. 斐波那契数列
题目链接很简单的题目,可以用如下几种方法暴力解法,这个就不提了递归解法迭代算法快速幂不过需要注意的是,这种多次叠加的情况,要考虑到溢出。下面是迭代算法。时间复杂度O(n)O(n)O(n),空间复杂度O(1)O(1)O(1)// 需要考虑溢出的情况class Solution { public int fib(int n) { if(n <= 0) return 0; if(n == 1) re原创 2021-01-04 13:22:22 · 75 阅读 · 0 评论 -
剑指 Offer 09. 用两个栈实现队列
题目链接很简单的题目,用两个辅助栈实现一个队列,能够从头部删除和从尾部添加元素。从尾部添加元素和堆栈的Push方法一致。而从头部删除元素用一个栈式无法实现的。下面是两个辅助栈栈1和栈2实现队列的步骤。若栈1为空,直接返回-1逐一将栈1中的元素出栈,再压入栈2。栈2得到元素顺序翻转的栈1将栈2的栈顶出栈将栈2的元素逐一出栈,并压入到栈1中。算法复杂度:时间复杂度:O(n)O(n)O(n),需要两次遍历堆栈空间复杂度:O(n)O(n)O(n),需要两个栈存储元素class CQueue原创 2021-01-02 11:40:32 · 64 阅读 · 0 评论 -
剑指 Offer 08.二叉树的下一个节点
题目链接很简单的一道题,二叉树中根据当前节点求出由中序遍历的下一个节点,分情况讨论假如有右子树,那么右子树的最左节点就是要求的假如没有右子树,那么就找祖先节点,直到找到一个祖先节点,使得该节点在该祖先节点的左子树中。找到这样的祖先节点,直接返回,找不到,则返回Null/** * Definition for a binary tree node. * public class TreeNode { * int val; * TreeNode left; * T原创 2021-01-02 11:22:26 · 178 阅读 · 0 评论 -
剑指 Offer 07. 重建二叉树
先序遍历顺序[根节点|左子树|右子树]中序遍历顺序[左子树|根节点|右子树]例如题目中前序遍历划分 [ 3 | 9 | 20 15 7 ]中序遍历划分 [ 9 | 3 | 15 20 7 ]基于这两个序列的性质,我们可以设计如下算法按序访问前序遍历序列,得到根节点root在中序遍历序列查找root,得到[左子树,root,右子树]根据2中得到的左右子树长度,可以将前序遍历序列划分为[root,左子树,右子树]这里2中查找root的方法有两种,一种是直接按序查找中序遍历序列,时间复杂原创 2020-12-31 11:40:15 · 84 阅读 · 0 评论 -
剑指 Offer 06. 从尾到头打印链表
分析没有啥好说的,三种解法,这里只写第一种逆序操作基本上就是堆栈了。遍历List,将其数据压到栈中。再将栈中的数据弹出到数组,完。当然,也可以两次遍历List,第一次遍历得到list的大小,分配对应大小的数组,第二次遍历,倒序地将数插入数组即可。基于递归基于堆栈/** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * Lis原创 2020-12-30 09:31:47 · 106 阅读 · 0 评论 -
剑指 Offer 05. 替换空格
剑指 Offer 05. 替换空格函数搞定用String的replaceAll函数class Solution { public String replaceSpace(String s) { return s.replaceAll(" ", "%20"); }}基于字符串加法的解粗暴的解法直接初始化一个空的目标串res,遍历源串每个元素s若s不为空格直接复制,res+=sres += sres+=s否则,res+="%20"res += "\%20"r原创 2020-12-30 08:51:04 · 87 阅读 · 0 评论 -
剑指Offer 不修改数组找出重复的数字
acwing链接这里题目要求不修改原来的数组,并且空间复杂度要求为O(1)O(1)O(1)。这里可以用二分查找的方法。设leftleftleft是搜索范围的开始,rightrightright为搜索范围的结束。midmidmid为它们的中间值统计数组中[left,mid][left, mid][left,mid]之间数字的次数cntcntcnt,与left和right之间的数字个数mid−left+1mid- left + 1mid−left+1进行比较。left=rightleft = right原创 2020-12-29 09:54:36 · 131 阅读 · 0 评论