预备知识
首先需要了解“各组得分最高的员工”要如何求解,参考这篇文章:
如何查找各组得分最高的员工?以“部门工资最高的员工”为例。
类比”184.部门工资最高的员工“,可以考虑找出每个分组的工资前三高的值,然后通过子查询或者是连接这个临时表找出各组工资前三高的员工。
但实际上,各组前三高的工资并不能像各组最高的工资一样直接group by就能求出,所以这个方法并没有想象中那么简单。
首先需要了解“各组得分最高的员工”要如何求解,参考这篇文章:
如何查找各组得分最高的员工?以“部门工资最高的员工”为例。
类比”184.部门工资最高的员工“,可以考虑找出每个分组的工资前三高的值,然后通过子查询或者是连接这个临时表找出各组工资前三高的员工。
但实际上,各组前三高的工资并不能像各组最高的工资一样直接group by就能求出,所以这个方法并没有想象中那么简单。
类似于求各种组合,选择->递归选择之后的元素->回溯。
为了避免重复计算,在每次递归时将数组加入答案中即可,不需要每个数组都是从头开始计算。
时间O(n*2^n)
,空间O(n)
递归栈。
1 | class Solution { |
首先记录空集,然后在此基础上每次向后追加新元素。
如:[] -> [], [1] -> [], [1], [2], [1,2] -> [], [1], [2], [1,2], [3], [1,3], [2,3], [1,2,3]
名次实际上就是计算比当前元素大的值的个数,要求名次之间没有间隔,加个distinct即可。
灵活使用as重命名。
1 | select Score, (select count(distinct Score) from Scores where Score >= s.Score) as `Rank` |
1 | select max(Salary) as SecondHighestSalary |
这种方法的优点是如果存在第二大的,则一定能返回;如果不存在,则会返回null。
缺点是难以处理第3, 4, 5…大的数据。
limit
与offset
limit
可以限制结果的条数,offset
是跟limit
连用的,可以限制limit
开始的位置。
如数据为1,2,3,4,5,limit 2
获得的是1,2,而如果limit 2 offset 1
返回的就是2,3,开始limit
的位置向后偏移了1位。
另外,limit m offset n
可以写成limit n, m
如果offset超出了数据范围,则不会报错,结果会得到空集,代表0条记录,而不是结果为null。
递归求解左叶子的和,直接在最小的树(2层)中定位到左叶子,而不是递归到只剩一个节点,因为这样无法判断左右。
时间O(n)
,空间O(n)
.
1 | class Solution { |
使用层次遍历,逐个判断是否为左叶子。
时间O(n)
,空间O(n)
。
类似的题目是46.全排列,数组中不包含可重复数字,所以直接对于每一位遍历每一个,选择然后回溯不选择。
而这道题目的数组中包含重复数字,因此如果按照上述方法,会出现重复排列。
如:
1 | / | \ |
出现重复排列的原因是,对于当前位置,已经选择过的数字在之后又被重新选择了(但选择的不是同一个元素,只是它们的值相等)
因此避免重复的方法就是,在当前位置,避免选择之前已经选择过的数字。
想象在当前位置,第一次选择某个数字,则这个数字一定是所有相等的数字中,第一个在数组中未被选择的,选择它并置vis为true。
在获得当前位置为这个数字得到的所有排列之后,当前位置的数字被重新选择,并将之前元素的vis置为false。
如果选择到的元素还是相等的数字,则之后的排列一定是已经获得过的,并且可以发现它不是数组的相等数字中第一个未被选择的。
因此算法是,先将数组排序,将相等的元素放在一起,然后dfs,选择元素时只选择前一个相等元素被选择过的。
时间O(n*n!)
,空间O(n)
。
先交换左右子节点,然后再递归反转左右子树。
时间O(n)
,空间O(n)
。
1 | /** |
左右根
时间O(n)
,空间O(n)
。
1 | class Solution { |