以往刷题的很多题并没有做系统性记录,逻辑不够清晰。本页将已经刷过的每题进行二次总结,强化算法实现、逻辑思维能力。同时每天也会坚持刷题,坚持总结一篇以往AC的题目。
13. 罗马数字转整数
罗马数字包含以下七种字符:
I
,V
,X
,L
,C
,D
和M
。
1
2
3
4
5
6
7
8 字符 数值
I 1
V 5
X 10
L 50
C 100
D 500
M 1000例如, 罗马数字 2 写做
II
,即为两个并列的 1。12 写做XII
,即为X
+II
。 27 写做XXVII
, 即为XX
+V
+II
。通常情况下,罗马数字中小的数字在大的数字的右边。但也存在特例,例如 4 不写做
IIII
,而是IV
。数字 1 在数字 5 的左边,所表示的数等于大数 5 减小数 1 得到的数值 4 。同样地,数字 9 表示为IX
。这个特殊的规则只适用于以下六种情况:
I
可以放在V
(5) 和X
(10) 的左边,来表示 4 和 9。X
可以放在L
(50) 和C
(100) 的左边,来表示 40 和 90。C
可以放在D
(500) 和M
(1000) 的左边,来表示 400 和 900。给定一个罗马数字,将其转换成整数。输入确保在 1 到 3999 的范围内。
1 | import java.util.HashMap; |
解:
我们定义一个Map存储每个字符对应的数值。
根据如下信息,可以得到,在遇上I、V、X 或者 X、L、C 或者C、D、M同时出现时需要特殊处理。
I
可以放在V
(5) 和X
(10) 的左边,来表示 4 和 9。X
可以放在L
(50) 和C
(100) 的左边,来表示 40 和 90。C
可以放在D
(500) 和M
(1000) 的左边,来表示 400 和 900。
因此,我们在遇到每个字符的时候在总数上加上一个该字符对应的数值,通过判断当前字符是否是”V、X 或者L、C 或者D、M”来进行处理特殊情况。在上述条件下,且上一个字符是对应的I、X、C,则需要减去2 * I、2 * X、2 * C,来纠正最新的数据。
最终数据的总和就是我们要的答案。
14. 最长公共前缀
编写一个函数来查找字符串数组中的最长公共前缀。
如果不存在公共前缀,返回空字符串
""
。
1
2 输入: ["flower","flow","flight"]
输出: "fl"
1
2
3 输入: ["dog","racecar","car"]
输出: ""
解释: 输入不存在公共前缀。
1 | package leetcode_14_longestCommonPrefix; |
解:
时间复杂度:log(n*m),n代表数组长度,m代表共同前缀个数
思路:
获取第一个字符串的长度n,从0-n进行遍历,在某个下标位置记录当前字符,比较数组中剩下元素的当前位置字符是否相同。如果相同,则输出字符串附加一个字符。
16. 最接近的三数之和
给定一个包括 n 个整数的数组
nums
和 一个目标值target
。找出nums
中的三个整数,使得它们的和与target
最接近。返回这三个数的和。假定每组输入只存在唯一答案。
1 | import java.util.*; |
解:
我们可以通过双指针的方式降低时间复杂度(前提是该数组是有序的)。
i指向当前下标,start = i+1,end = length - 1;
每次遍历,将三个下标对应的数组相加求和(temp)。记录和target距离最小的temp。
如果target < temp,end小标减一。如果target > temp,start下标加一,该过程用于找到每个i小标对应的与target相差最小的那三组数之和。
遍历完i,就得到了最接近target的三数之和。
18.最接近的四数之和
给定一个包含 n 个整数的数组
nums
和一个目标值target
,判断nums
中是否存在四个元素 a,**b,c 和 d ,使得 a + b + c + d 的值与target
相等?找出所有满足条件且不重复的四元组。注意:
答案中不可以包含重复的四元组。
示例:
1
2
3
4
5
6
7
8 给定数组 nums = [1, 0, -1, 0, -2, 2],和 target = 0。
满足要求的四元组集合为:
[
[-1, 0, 0, 1],
[-2, -1, 1, 2],
[-2, 0, 0, 2]
]
1 | package leetcode_18_fourSum; |
解:
该题“最接近的四数之和”,可以通过三数之和一样的思路进行解题。三数之和把0(n^3) => 0(n^2),在这里可以为四数之和降到 0(n^3)的时间复杂度。
- 一开始的两循环遍历四个数的前两个数。
- 后面两个数通过双指针的方式降低时间复杂度
- 当然,在经过测试之后会发现有重复的元组,需要进行去重。上述代码中,通过依次比较ans列表中是否有和新增加的列表相同的列表,有则不执行add操作。
20. 有效括号
给定一个只包括
'('
,')'
,'{'
,'}'
,'['
,']'
的字符串,判断字符串是否有效。有效字符串需满足:
- 左括号必须用相同类型的右括号闭合。
- 左括号必须以正确的顺序闭合。
注意空字符串可被认为是有效字符串。
示例 1:
1
2 输入: "()"
输出: true示例 2:
1
2 输入: "()[]{}"
输出: true示例 3:
1
2 输入: "(]"
输出: false示例 4:
1
2 输入: "([)]"
输出: false示例 5:
1
2 输入: "{[]}"
输出: true
1 | package leetcode_20_isValid; |
解:
通过栈的方式完成任务,把任何左括号的类型一律入栈。
如果遇到了右括号则去栈中弹出一个字符,如果这两个括号是对应的一组则继续遍历,如果不是则返回false
如果字符串遍历完毕之后栈中还有数据,那么括号不对称返回false。
如果字符串遍历完,且栈为空,那么返回true。
148 排序链表
在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序。
示例 1:
1
2 输入: 4->2->1->3
输出: 1->2->3->4示例 2:
1
2 输入: -1->5->3->4->0
输出: -1->0->3->4->5
1 | package leetcode148; |
解:
归并递归描述
依据归并排序,我们需要对数据进行不断地二分。划分到只有一个节点之后,返回阶段,在返回是对两组数据进行归并,然后进一步递归返回,再将两个数组进行按序归并,如此到递归退出就能获得有序地数据。
上面代码比较长,这里做一个解释。
由于是链表,不能直接类似于数组进行索引访问,因此需要通过快慢指针获得中间元素。
findMidNode()
同时找到指针之后,在进一步递归地时候,为了使得数据能够前后分割,不会被链表的链接影响,我们需要对mid结点进行切断操作。
midNode.next = null;
当数据被划分成两个链表之后就可以进一步递归-二分。
当递归数据返回的时候,我们需要对返回的有序链表进行归并。通过辅助指针的方式,不断地往头节点按递增次序拼接结点,不断返回、拼接数据就能获得会后有序地链表了。
101. 对称二叉树
给定一个二叉树,检查它是否是镜像对称的。
例如,二叉树
[1,2,2,3,4,4,3]
是对称的。
1
2
3
4
5 1
/ \
2 2
/ \ / \
3 4 4 3
1 | /** |
解:
递归
如果镜像对称,那么左子树和右子树也一定是镜像对称。
通过递归,将左子树的左节点和右子树的右节点递归传入进行比较。
① 递归结束条件:如果左节点 == 右节点 == null, 返回 ture;如果左节点 != 右节点,返回false;
②如果两个节点都不为空,且值相等,则需要进一步递归。
层序遍历
通过迭代,定义一个栈,将左节点的左子节点和右节点的右子节点,左节点的右子节点和右节点的左子节点并列放入栈中。
每次弹出两个数据:
如果两个数据相等则进一步迭代,直至栈为空返回ture。
如果两个数据不相等直接返回false
153. 寻找旋转排序数组中的最小值
假设按照升序排序的数组在预先未知的某个点上进行了旋转。
( 例如,数组
[0,1,2,4,5,6,7]
可能变为[4,5,6,7,0,1,2]
)。请找出其中最小的元素。
你可以假设数组中不存在重复元素。
示例 1:
1
2 输入: [3,4,5,1,2]
输出: 1示例 2:
1
2 输入: [4,5,6,7,0,1,2]
输出: 0
题解:
1 | /** |
解:
本题是二分搜索的变题
对于传入数组,指定两个指针,一个left指针,一个right指针。
如果nums[left] >= nums[right
]则说明,left指针在旋转点的左侧,right指针在旋转点的右侧。则不断进行循环,执行二分的过程。
二分的结束条件为, nums[left] > nums[right]
&& right-left == 1
如果nums[mid] < nums[left]
说明,mid在旋转点的右侧,应该将right = mid
;
如果nums[mid] > nums[right]
说明,mid在旋转点的左侧,应该将left = mid
;
上述代码中间还处理一个特殊情况,
nums[left] == nums[mid] == nums[right]
。这种情况没法区分下一个mid应该在哪半边区域,因此用遍历的方式进行寻找
nums[left] > nums[right]
的位置
515. 在每个树行中找最大值
您需要在二叉树的每一行中找到最大的值。
示例:
输入:
1
/ \
3 2
/ \ \
5 3 9
输出: [1, 3, 9]
1 | class Solution { |
解:
通过递归实现。
① 递归结束条件:碰到叶节点。
② 在进行DFS遍历二叉树的同时,传入参数level给定每次递归的层级,通过层级来进行对当前level的最大值进行比较和替换。
(如果list中没有当前层级的数据,则初始化Integer.MIN_VALUE,有则取出来和当前数值进行比较,若当前值更大则将当前值替换成list中的数值。)
时间复杂度:遍历每个节点,0(N)
空间复杂度:递归需要每向下一层就需要入栈,O(h + h + h) 。三个h分别存储root节点、每层max的值,该层对应的level。