8/4 刷题记录
题目顺序来自于 LeetCode 刷题攻略
数组(全部):
1. 二分法:
明确区间,边界条件设定
- 704.二分查找
题目链接:https://leetcode-cn.com/problems/binary-search/ - 35.搜索插入位置
题目地址:https://leetcode-cn.com/problems/search-insert-position/
根据大小关系直接在指定位置插入
2. 双指针
-
27.移除元素
题目地址:https://leetcode-cn.com/problems/remove-element/
双指针赋值 -
283.移动零
题目地址:https://leetcode-cn.com/problems/move-zeroes/
交换元素,值得记录。 -
26.删除有序数组中的重复项
题目地址:https://leetcode-cn.com/problems/remove-duplicates-from-sorted-array/
双指针赋值,从1开始简化代码 -
844.比较含退格的字符串
题目地址:https://leetcode-cn.com/problems/backspace-string-compare/
从后向前进行比较的思路,有点特色,值得记录。 -
977.有序数组的平方
题目地址:https://leetcode-cn.com/problems/squares-of-a-sorted-array/
一个从前开始,一个从后开始,依次比较从末尾填入新数组 -
209.长度最小的子数组
题目地址:https://leetcode-cn.com/problems/minimum-size-subarray-sum/
双指针 滑动窗口 好题
3. 其他
- 59.螺旋矩阵 II 中等题
题目地址:https://leetcode-cn.com/problems/spiral-matrix-ii/
坚持循环不变量原则,确定合适的循环体条件。
8/5 刷题记录
链表
-
203.移除链表元素 简单题
https://leetcode-cn.com/problems/remove-linked-list-elements/
结点删除操作 虚拟一个头结点 简化删除操作 -
707.设计链表 中等题
https://leetcode-cn.com/problems/design-linked-list/
单链表 双链表 的代码实现 规范化遍历 活用双链表双向遍历的性质
8/6 刷题记录
链表
-
206.反转链表 简单题
题目地址:https://leetcode-cn.com/problems/reverse-linked-list/
反转链表 双指针迭代 递归 -
24.两两交换链表中的节点 中等题
题目地址:https://leetcode-cn.com/problems/swap-nodes-in-pairs/
两两交换,很考验链表的指针移动操作 要画图进行分析才能明白交换和遍历过程
8/7 刷题记录
链表(刷完)
-
19.删除链表的倒数第N个节点 中等题
题目地址 :https://leetcode-cn.com/problems/remove-nth-node-from-end-of-list/
双指针保持间距n进行遍历 虚拟头结点处理删除头结点情况 -
面试题 02.07. 链表相交 简单题
题目地址:https://leetcode-cn.com/problems/intersection-of-two-linked-lists-lcci/
根据数学规律进行遍历判断 -
142.环形链表II 中等题
题目地址:https://leetcode-cn.com/problems/linked-list-cycle-ii/
快慢指针 非常数学的分析计算
8/11 刷题记录
哈希表
-
242.有效的字母异位词 简单题
题目地址:https://leetcode-cn.com/problems/valid-anagram/
建立一个从长度26的哈希表,比较两者的字母出现次数 -
1002.查找常用字符 简单题
题目地址:https://leetcode-cn.com/problems/find-common-characters/
每个词建立一个26长度哈希表,求出单词间字母出现次数的最小值 -
349.两个数组的交集 简单题
题目地址:https://leetcode-cn.com/problems/intersection-of-two-arrays/
用集合set去掉重复出现的数字,比较找出两个数组间的交集
8/12 刷题记录
哈希表(完)
-
202.快乐数 简单题
题目地址:https://leetcode-cn.com/problems/happy-number/
集合判断是否有重复 提取数字每一位进行运算的数学方法和字符串方法 -
1.两数之和 简单题
题目地址:https://leetcode-cn.com/problems/two-sum/
map储存数和下标,通过差值判断是否存在 -
454.四数相加II 中等题
题目地址:https://leetcode-cn.com/problems/4sum-ii/
两两分组,分别遍历 n平方的时间复杂度;31分组复杂度达到三次方会超时。 -
383.赎金信 简单题
题目地址:https://leetcode-cn.com/problems/ransom-note/
只有小写字母 手动26个字母数组作哈希表 有点跟之前的题重复 -
15.三数之和 中等题
题目地址:https://leetcode-cn.com/problems/3sum/
顺序遍历过程中 left和right指针遍历所有可能组合 时间复杂度n平方
值得记录的题 还有一些细节优化要注意 -
18.四数之和 中等题
题目地址:https://leetcode-cn.com/problems/4sum/
比三数之和多一层循环,做法相同,左右指针
并且要考虑target为负时不能通过大于target的判断直接跳出循环
8/14 刷题记录
字符串
- 344.反转字符串 简单题
题目地址:https://leetcode-cn.com/problems/reverse-string/
双指针 首尾交换进行反转
8/15 刷题记录
字符串
-
541.反转字符串II 简单题 需要注意
题目地址:https://leetcode-cn.com/problems/reverse-string-ii/
定义一个子函数负责反转 双指针
字符串不能直接通过下标进行修改等操作, 得先拆分成数组形式才可以 -
剑指Offer 05.替换空格 简单题
题目地址:https://leetcode-cn.com/problems/ti-huan-kong-ge-lcof/
先规划好新数组的长度再操作 有利用提高效率
8/26 刷题记录
字符串
- 题目:剑指Offer58-II.左旋转字符串 简单题
题目地址:https://leetcode-cn.com/problems/zuo-xuan-zhuan-zi-fu-chuan-lcof/
前半部分反转,后半部分反转 再全部反转 实现题目要求的左旋转字符串效果
局部反转 + 全部反转
8/26 刷题记录
字符串
-
28.实现 strStr()
题目地址:https://leetcode-cn.com/problems/implement-strstr/
KMP模式匹配 文本串中定位模式串 -
459.重复的子字符串
题目地址:https://leetcode-cn.com/problems/repeated-substring-pattern/submissions/
利用KMP计算长度 有点复杂
双指针法
都是上面做过的原题:
- 数组移除元素
- 反转字符串
- 替换空格
- 反转字符串里的单词
- 反转链表
- 删除链表倒数第N个节点
- 链表相交节点
- 链表中环的入口
- 三数之和
- 四数之和
栈与队列
-
232.用栈实现队列
题目地址:https://leetcode-cn.com/problems/implement-queue-using-stacks/
两个栈实现队列操作 -
225.用队列实现栈
题目地址:https://leetcode-cn.com/problems/implement-stack-using-queues/
两个队列实现 一个队列也可以实现 -
20.有效的括号
题目地址:https://leetcode-cn.com/problems/valid-parentheses/
判断括号合法性 栈实现 -
1047.删除字符串中的所有相邻重复项
题目地址:https://leetcode-cn.com/problems/remove-all-adjacent-duplicates-in-string/
新元素与栈顶匹配,如匹配弹出栈顶元素 -
150.逆波兰表达式求值
题目地址:https://leetcode-cn.com/problems/evaluate-reverse-polish-notation/
识别到运算符就弹出两个数字进行运算,再将结果入栈。 -
239.滑动窗口最大值 困难题
题目地址:https://leetcode-cn.com/problems/sliding-window-maximum/
一个窗口大小的辅助队列,每次移动的新元素大于栈尾元素就将弹出栈尾元素直到大于新元素,然后新元素入栈。
存储新元素下标而不是值,这样当超出范围就很好判断并弹出。 -
347.前 K 个高频元素
题目地址:https://leetcode-cn.com/problems/top-k-frequent-elements/
先统计元素频率然后进行堆排序,相比快速排序只需要维持K长度的数组,节约了空间。快排要用 N(元素个数)长度数组。
二叉树
各种遍历的递归与迭代:
-
前序遍历 后序遍历 中序遍历
-
层序遍历迭代实现
-
翻转二叉树
题目地址:https://leetcode-cn.com/problems/invert-binary-tree/
递归 交换左右子节点 -
判断对称二叉树
题目地址:https://leetcode-cn.com/problems/symmetric-tree/
递归比较,注意左子树左节点对应右子树右节点 -
二叉树最大深度
题目地址:https://leetcode-cn.com/problems/maximum-depth-of-binary-tree/
递归 注意要回溯一下
或者统计层序遍历次数 -
二叉树最小深度
题目地址:https://leetcode-cn.com/problems/minimum-depth-of-binary-tree/
层序遍历统计 递归
注意根节点某个子树不存在时,最小深度是另外一个子树的深度加1,而不是直接为1, 即叶子节点左右子树都是空。 -
222.完全二叉树的节点个数 需要技巧
题目地址:https://leetcode-cn.com/problems/count-complete-tree-nodes/
可以直接统计个数 但如果要利用完全二叉树性质,展开为满二叉树再计算节点个数。 -
110.平衡二叉树
题目地址:https://leetcode-cn.com/problems/balanced-binary-tree/
递归判断 返回子树的最大高度
设定一个返回值-1表示该子树不平衡 -
257.二叉树的所有路径 技巧
题目地址: https://leetcode-cn.com/problems/binary-tree-paths/
要注意回溯
使用原始值类型不用担心引用传递被修改的问题 -
404.左叶子之和
题目地址:https://leetcode-cn.com/problems/sum-of-left-leaves/
判断叶子节点是不是左叶子需要在父节点上进行
递归 迭代 -
513.找树左下角的值
题目地址:https://leetcode-cn.com/problems/find-bottom-left-tree-value/
层序遍历: 存储最深层最左侧值,即一个变量存储每一层第一个值即可
前序遍历:记录深度,通过大于号和前序遍历是先左再右来确保变量存储的是最左侧值。 -
112.路径总和
题目地址:https://leetcode-cn.com/problems/path-sum/
是否存在根节点到叶子节点的节点值和等于target?返回布尔值
递归 前序遍历 满足条件直接返回减少计算
递归函数什么时候需要返回值?什么时候不需要返回值?这里总结如下三点:
- 如果需要搜索整颗二叉树且不用处理递归返回值,递归函数就不要返回值。(这种情况就是本文下半部分介绍的113.路径总和ii)
- 如果需要搜索整颗二叉树且需要处理递归返回值,递归函数就需要返回值。 (这种情况我们在236. 二叉树的最近公共祖先中介绍)
- 如果要搜索其中一条符合条件的路径,那么递归一定需要返回值,因为遇到符合条件的路径了就要及时返回。(本题的情况)
-
113.路径总和ii
题目地址:https://leetcode-cn.com/problems/path-sum-ii/
上一题的基础上找出所有满足的路径并返回
用字符串记录路径并且遍历到节点值时再添加值避免回溯问题。 -
106.从中序与后序遍历序列构造二叉树
题目地址:https://leetcode-cn.com/problems/construct-binary-tree-from-inorder-and-postorder-traversal/
思想是利用后序遍历末尾是根节点在中序遍历中划分出左右子树,然后不断划分。
注意根据中序遍历算出的子树长度来划分 -
105.从前序与中序遍历序列构造二叉树
题目地址: https://leetcode-cn.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/
同上,利用前序遍历第一个是根节点在中序遍历中进行划分。 -
654.最大二叉树
题目地址:https://leetcode-cn.com/problems/maximum-binary-tree/
遇上一道题类似,计算出下标然后前序遍历顺序构建二叉树 -
617.合并二叉树
题目地址:https://leetcode-cn.com/problems/merge-two-binary-trees/
直接合并,注意若某个树展开的某一子树为空,直接取另一个树的子树根节点即可,不需要对这个子树接着合并了。 -
700.二叉搜索树中的搜索某个节点值
题目地址:https://leetcode-cn.com/problems/search-in-a-binary-search-tree/
二叉搜索树左边节点值小于根节点值,直接递归搜索 -
98.验证二叉搜索树
题目地址:https://leetcode-cn.com/problems/validate-binary-search-tree/
方法1 递归传递子树的值的范围,判断节点值是否在范围
方法2 二叉搜索树中序遍历节点值构成有序递增数组,遍历存储所有节点值判断是不是有序递增即可。 -
530.二叉搜索树的最小绝对差 很巧妙
题目地址:https://leetcode-cn.com/problems/minimum-absolute-difference-in-bst/
二叉搜索树中序遍历的有序性,存储上一个节点并比较当前节点和上一个节点差值,找最小值即可。 -
501.二叉搜索树中的众数 很巧妙
题目地址:https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-tree/
方法1 map统计次数 然后再遍历map找最大值 (没有利用二叉搜索树性质)
方法2 中序遍历的有序性统计次数 一个max记录之前节点次数最大值,一个count统计当前节点次数,若大于max就更新max并清空结果数组中之前的值,将当前值加入。 -
236.二叉树的最近公共祖先 巧妙
题目地址:https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-tree/
遍历过程中查找p和q节点,回溯获取最近父节点 -
235.二叉搜索树的最近公共祖先 巧妙
题目地址:https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-search-tree/
利用二叉搜索树的性质,如果从上到下遍历到一个节点值 r 在p,q节点值构成的区间范围内,假设p是左区间,q是右区间;所以 p小于r则p在r的左子树中,q大于r则q在r的右子树中,则最近公共祖先就是 r 。
注意是从上到下第一个满足区间范围的节点,第二个就不一定是公共祖先了。 -
701.二叉搜索树中的插入操作
题目地址:https://leetcode-cn.com/problems/insert-into-a-binary-search-tree/
选择最简单的插入方式,因为无重复值,所以找到符合范围的一侧空节点进行连接即可。 -
450.删除二叉搜索树中的节点 值得再练
题目地址:https://leetcode-cn.com/problems/delete-node-in-a-bst/
主要就是删除有左右子树的节点的问题,这里通过将左子树全部移到右子树的最左侧节点的左节点来实现。
迭代和递归两种写法,与删除普通二叉树区别只在于找到目标节点的过程。
迭代写法要同时获取待删除节点的父节点以便进行删除后的连接操作,使用了虚拟头节点来处理删除根节点的情况。 -
669.修剪二叉搜索树
题目地址:https://leetcode-cn.com/problems/trim-a-binary-search-tree/
通过递归来寻找子树中第一个符合条件的节点并进行连接
很考验对递归的理解 -
108.将有序数组转换为二叉搜索树
题目地址:https://leetcode-cn.com/problems/convert-sorted-array-to-binary-search-tree/
简单的二分递归,但要注意递归函数不能使用全局变量,忘记添加变量声明导致变量变成全局变量然后找了好久才发现问题。浪费时间! -
538.把二叉搜索树转换为累加树
题目地址:https://leetcode-cn.com/problems/convert-bst-to-greater-tree/
反向的中序遍历 加1个存储前一个节点值即可实现
不要复杂化,这种树的问题还是要根据要求的操作顺序来带入熟悉的领域中。这道题实际上就是反向的中序遍历就能实现。
回溯算法
for循环横向遍历,递归纵向遍历,回溯不断调整结果集
- 77.组合 n个数中选k个数的所有组合
题目地址:https://leetcode-cn.com/problems/combinations/
回溯就类似于遍历,从左往右依次取确保不会重复。
利用如下所示这种模板,if判断终止条件,for循环在迭代过程中根据迭代值的不同进行递归。
const backcombine = function(start, n, path){
if (path.length == k) {
// 直接复制path会复制引用,后续的修改也会影响res中
// 这里相等于将数组中的元素全部浅复制,因为数组中的元素值是原始值,所以修改不会传递
result.push([...path]);
return
}
// 优化:为了满足能取到k个元素,限制一下起始范围
for (let i= start; i<=n-(k-path.length)+1; i++){
path.push(i);
// 优先深度遍历
backcombine(i+1, n, path);
// 添加完弹出保证值的正确性
path.pop();
}
}
-
216.组合总和 k个数构成n的所有组合
题目地址:https://leetcode-cn.com/problems/combination-sum-iii/
同上,使用上面的模板进行回溯,注意全局变量和引用值要回溯 -
17.电话号码的字母组合 数字对应多个字母 求一个数字串能产生的字母组合
题目地址:https://leetcode-cn.com/problems/letter-combinations-of-a-phone-number/
建立数字到字母的映射,然后一个数字对应多个字母这种情况使用回溯来处理每次取不同值的情况,记得要将传递的参数修改回之前的值 -
39.组合总和 不同点:可以重复选择数字
题目地址:https://leetcode-cn.com/problems/combination-sum/
题目要求可以重复选择,因此递归时传递的起始位置就是当前位置,不需要加1。
主要还是根据模板来分析处理,确定终止条件,确定迭代部分的处理,传给递归的参数 -
40.组合总和II 原本数组有重复数字,但解集不能有重复 好题
题目地址:https://leetcode-cn.com/problems/combination-sum-ii/
题目要求不能有重复解,但原本数组中有重复数字,所以要进行一些处理。
通过排序来将相同数字放在一起,这样在for循环迭代过程中判断这一轮之前的迭代是否已经选择过这个数字进行计算,如果选过就跳过当前的数字。
方法挺巧秒的,注意点是要保证重复数字中起码有一个会进行计算。 -
131.分割回文串
题目地址:https://leetcode-cn.com/problems/palindrome-partitioning/
分割其实跟之前的做法没有太大区别,每次从当前起始位置选择不同长度的子串,通过定义一个函数判断是否是回文串,如果是进行递归更深一层计算,不是的话就改变字串的长度。保证path数组中存储的都是回文串,这样就可以在遍历完字符串后直接添加到结果数组中。 -
93.复原IP地址
题目地址:https://leetcode-cn.com/problems/restore-ip-addresses/
跟上一道题一样,合法ip地址就是由四个合法的数字子模块构成,划分不同长度的子模块判断该子模块是否合法,合法就进行递归深层计算,不合法就跳过。当子模块长度也算不合法直接跳过。 -
78.子集 无重复数字求所有子集
题目地址:https://leetcode-cn.com/problems/subsets/
就是求能构成的所有集合,上面的题都是在叶子节点处获取结果,这个题是获取所有节点的结果,所以更改一下将path结果储存result数组这个操作的位置即可。每次修改path就直接存储在result中。
上面这些题还是都能够通过回溯算法的模板来进行处理,使用模板就可以很容易分析问题,拆分开来进行处理。
而且在比对leetcode的运行结果发现,将path数组作为回溯函数的参数传递的话比起写在父函数中执行速度要快很多,但是空间会多占一点,个人认为作为参数传递时,也是按引用值传递,所以复制的是指针,不会太影响空间大小。但是当path写在父函数中,每次调用从父函数的作用域中读取所浪费的时间似乎影响更大(leetcode上的统计)。
-
90.子集II 有重复数字求子集
题目地址: https://leetcode-cn.com/problems/subsets-ii/
还是排序,然后在迭代过程中跳过已经计算过的重复值。 -
491.递增子序列 找出无序数组中的递增子序列 需要注意
题目地址:https://leetcode-cn.com/problems/increasing-subsequences/
需要每一次迭代时用一个哈希表统计迭代使用过的数字,跳过重复的来避免选择到重复解进入path数组。
因为是子序列,所以直接添加有序的path数组进result数组。
迭代过程中某一轮的数字不符合有序,即没有加入path数组的话就不进行递归,因为path没变,只有start加1的递归和正在进行的迭代没有区别。当发生改变再进行递归。 -
46.全排列 无重复数字数组的全排列
题目地址:https://leetcode-cn.com/problems/permutations/
需要一个数组记录使用过的数字,避免使用重复数字即可。 -
47.全排列 II 有重复数字数组的全排列
题目地址:https://leetcode-cn.com/problems/permutations-ii/
遇到有重复数字还要求无重复解的时候就要想到利用排序来去重,在当前轮迭代中保证不会选择到重复值进行计算。
再用一个数组在计算前先统计数组中每个数字的次数并在迭代中进行比较,确保数字的使用次数不会超出限制。(其实不需要统计,只需要一个与原数组长度相同的数组,因为即使原数组中有重复值但它们下标不同,所以只要确保下标不重复即可)。
子集问题分析:
- 时间复杂度: O ( n ∗ 2 n ) O(n * 2^n) O(n∗2n),因为每一个元素的状态无外乎取与不取,所以时间复杂度为 O ( 2 n ) O(2^n) O(2n)(就是每个元素都有两个状态中选1个),构造每一组子集都需要填进数组,又有需要 O ( n ) O(n) O(n)(即将path数组放入result数组需要的时间),最终时间复杂度: O ( n ∗ 2 n ) O(n * 2^n) O(n∗2n)
- 空间复杂度: O ( n ) O(n) O(n),递归深度为n,所以系统栈所用空间为 O ( n ) O(n) O(n),每一层递归所用的空间都是常数级别,注意代码里的result和path都是全局变量,就算是放在参数里,传的也是引用,并不会新申请内存空间,最终空间复杂度为 O ( n ) O(n) O(n)
排列问题分析:
- 时间复杂度: O ( n ! ) O(n!) O(n!),这个可以从排列的树形图中很明显发现,每一层节点为n,第二层每一个分支都延伸了n-1个分支,再往下又是n-2个分支,所以一直到叶子节点一共就是 n ∗ n − 1 ∗ n − 2 ∗ . . . . . 1 = n ! n * n-1 * n-2 * ..... 1 = n! n∗n−1∗n−2∗.....1=n!。
- 空间复杂度: O ( n ) O(n) O(n),和子集问题同理。
组合问题分析:
- 时间复杂度: O ( n ∗ 2 n ) O(n * 2^n) O(n∗2n),组合问题其实就是一种子集的问题(每个元素都需要遍历选择试试看),所以组合问题最坏的情况,也不会超过子集问题的时间复杂度。
- 空间复杂度: O ( n ) O(n) O(n),和子集问题同理。
- 332.重新安排行程 合理的将机票顺序组合起来 选择字母排序最小的
题目地址:https://leetcode-cn.com/problems/reconstruct-itinerary/
字符串之间大小比较就是根据字母排序,所以直接比大小就能选到最小的。
同时这道题需要先进行字母排序这样机票就会处于字母排序,然后深度优先搜索中找到的第一个解就是最小的解,后续直接跳过即可。
不排序直接遍历所有的话就会超时。