602. 好友申请 II :谁有最多的好友
题目链接
表
- 表
RequestAccepted
的字段为requester_id
、accepter_id
和accept_date
。
要求
编写解决方案,找出拥有最多的好友的人和他拥有的好友数目。
生成的测试用例保证拥有最多好友数目的只有 1 个人。
知识点
count()
:统计个数的函数。union all
:将查询到的数据合并起来。order by
:根据某些字段排序。group by
:根据某些字段分组。limit
:限制返回数据的条数。
思路
要想找出拥有好友最多的人,得先找出他拥有的好友数目,观察表可得出一个结论:一个人拥有的好友数与他的id
在这张表的出现的次数相同。
requester_id | accepter_id | accept_date |
---|---|---|
1 | 2 | 2016/06/03 |
1 | 3 | 2016/06/08 |
2 | 3 | 2016/06/08 |
3 | 4 | 2016/06/09 |
例如对于上面这张表,id
为1的人的好友数为2,id
为2的人的好友数为2,id
为3的人的好友数为3,id
为4的人的好友数为1。
所以本题的核心是将requester_id
和accepter_id
合并起来union all
,然后统计每个人的记录数cnt
,最后按记录数倒序排序order by cnt desc
,返回第一条数据limit 1
即可。
代码
select
id,
cnt num
from
(
select
id,
count(*) cnt
from
(
(
select
accepter_id id
from
RequestAccepted
)
union all
(
select
requester_id id
from
RequestAccepted
)
) tb_id
group by
id
) tb_cnt
order by
cnt desc
limit 1
3. 无重复字符的最长子串
题目链接
标签
哈希表 字符串 滑动窗口
思路
本题可以将子串
想象成一个窗口
,对子串的增加或减少就像窗口的扩大或缩小,子串的移动就像窗口的滑动。
让窗口在整个字符串中滑动,窗口的第一个字符为从字符串的第一个字符到最后一个字符的遍历,选取完窗口的第一个字符后,不断向右扩张窗口,直到新字符与窗口中的某个字符相同。接着移除窗口的第一个字符(向右滑动窗口),进行下一轮扩张(注意此时窗口的右端点不需要从头再来,因为当前的窗口内没有重复字符)。
每次扩张完窗口都会获取一个尽可能长的子串,这个子串的第一个字符就是从原字符串的第一个字符到最后一个字符的遍历,所以在窗口滑动完这个字符串后能保证不遗漏任意一个尽可能长的子串,从而在扩张完窗口后记录最大子串长度就能得到结果。
如何判断一个新字符是否与窗口内的字符重复?可以使用一个boolean[]
来记录窗口中每个字符是否存在,在判断新字符是否存在时直接查看这个数组,如果存在,则为true
,否则为false
。将新字符加入窗口时记录这个字符已存在,移除窗口中的字符时记录这个字符不存在。
代码
class Solution {
public int lengthOfLongestSubstring(String s) {
char[] str = s.toCharArray();
int n = str.length;
int left = 0, right = 0, res = 0; // left、right 分别表示窗口 左、右 端点的索引
boolean[] check = new boolean[128]; // 检查某个字符是否存在于窗口中 的数组
while (left < n) {
// 扩张窗口
while (right < n && !check[str[right]]) { // 若窗口内不包含当前字符
check[str[right]] = true; // 将当前字符加入窗口
right++; // 更新右端点的索引,扩张窗口
}
res = Math.max(res, right - left); // 更新结果值
check[str[left]] = false; // 移除窗口左端的字符
left++; // 向右滑动窗口
}
return res;
}
}
108. 将有序数组转换为二叉搜索树
题目链接
标签
树 二叉搜索树 数组 分治 二叉树
思路
二叉搜索树也是二叉树,不过它有一个独特的性质:左节点小于父节点,右节点大于父节点(也可以是左节点大于父节点,右节点小于父节点)。拥有这样的性质,会使在二叉搜索树中搜索一个数就像二分查找一样快,这就是这棵树叫二叉搜索树的原因。
回到题目中,既然数组是有序的,那么就可以使用分治的思想构建树:以区间的中点为父节点,然后在左子区间建立左子树,在右子区间建立右子树,接着将左子树和右子树连接到父节点上,最后返回父节点给更上层的调用。
代码
class Solution {
public TreeNode sortedArrayToBST(int[] nums) {
return build(nums, 0, nums.length - 1);
}
// 从数组nums的区间[left, right]内构建二叉树
private TreeNode build(int[] nums, int left, int right) {
if (left > right) { // 如果区间长度为负数
return null; // 则返回null作为父节点的子节点
}
int mid = left + right >> 1;
TreeNode parent = new TreeNode(nums[mid]); // 选取[left, right]的中间元素作为父节点
parent.left = build(nums, left, mid - 1); // 构建左子树
parent.right = build(nums, mid + 1, right); // 构建右子树
return parent; // 返回父节点
}
}