目录
- 1789. 员工的直属部门
- 题目链接
- 表
- 要求
- 知识点
- 思路
- 代码
- 6. Z 字形变换
- 题目链接
- 标签
- 思路
- 代码
- 138. 随机链表的复制
- 题目链接
- 标签
- 思路
- 代码
1789. 员工的直属部门
题目链接
1789. 员工的直属部门
表
- 表
Employee
的字段为employee_id
,department_id
和primary_flag
。
要求
- 请编写解决方案,查出员工所属的直属部门。
- 返回结果 没有顺序要求 。
知识点
count()
:统计个数的函数。group by
:根据字段分组。
思路
直属部门的定义是 primary_flag
为 Y
或者 他只属于一个部门,知道这个定义后,思路就很明显了:先获取只属于一个部门的员工的id employee_id
,然后将这些数据作为子表,放到限制条件中,限制条件还有一个 primary_flag = ‘Y’
,它们的关系是 或
。
代码
select
employee_id,
department_id
from
Employee
where
primary_flag = 'Y'
or
employee_id
in
(
select
employee_id
from
Employee
group by
employee_id
having
count(*) = 1
)
6. Z 字形变换
题目链接
6. Z 字形变换
标签
字符串
思路
做这道题时是我想起了高中物理中的简谐运动,可以将其简单理解为正弦函数
y
=
s
i
n
x
y=sinx
y=sinx,横轴为时间轴,竖轴为位移轴,只看竖轴的话,那么就是一个质点随着时间流逝在做上下往复的运动,当它到极限(波峰或波谷)时,它就得掉头(调转向上或向下的方向),如下图所示:
讲完这个东西后,理解本题的解法就更容易了。本题可以将原字符串当作这个做简谐运动的质点,它的运动轨迹(即题目中描述的 Z
字形,但我觉得它更像 N
字形一点)就像上图描述的一样,不过这时竖轴就是这个字符在Z
字形中的行数,横轴没有实际意义。
所以模拟这个往复运动的过程就能将字符按Z
字形分到不同的行上,然后将每行组成的小字符串按照行数的顺序拼接在一起,组成最终的大字符串。
遍历原字符串,字符的行数是从小到大、然后再从大到小的,调转的时机就是 行数为0
(波峰) 和 行数为numRows - 1
(波谷)。
将字符分到不同行时可以使用 S t r i n g B u i l d e r StringBuilder StringBuilder,本题解使用了一个 S t r i n g B u i l d e r StringBuilder StringBuilder数组,每个 S t r i n g B u i l d e r StringBuilder StringBuilder都记录了一行字符,在将所有字符都分到对应的行之后,按顺序将所有行合并到一起。
代码
class Solution {
public String convert(String s, int numRows) {
// 如果numRows比2小,则不需要改变,直接返回原字符串即可
if (numRows < 2) {
return s;
}
// 先初始化numRows个StringBuilder,每个StringBuilder都代表一行
StringBuilder[] builders = new StringBuilder[numRows];
for (int i = 0; i < numRows; i++) {
builders[i] = new StringBuilder();
}
int i = 0; // i是填充字符的行数
int flag = -1; // flag是方向,要么为上: -1,要么为下: 1
for (char c : s.toCharArray()) {
builders[i].append(c);
if (i == 0 || i == numRows - 1) { // 若行数为 第一行 或 最后一行
flag = -flag; // 则调转填充字符的方向
}
i += flag; // 调整行数
}
// 按顺序合并StringBuilder的字符串
StringBuilder res = new StringBuilder();
for (StringBuilder builder : builders) {
res.append(builder);
}
return res.toString();
}
}
138. 随机链表的复制
题目链接
138. 随机链表的复制
标签
哈希表 链表
思路
先讲一讲什么叫做浅拷贝和深拷贝,实际上这两个概念很简单,浅拷贝就是拷贝后的对象和原来的对象指向同一块内存,而深拷贝是拷贝后的对象和原来的对象指向不同的内存。
本题可以使用旧节点和新节点的映射,key
为旧节点,value
为新节点。遍历两遍链表,第一遍先创建新节点,并建立映射;第二遍给新节点的next, random
赋值,这时就可以从映射中获取旧节点对应的新节点,从而实现深拷贝。
代码
class Solution {
// 旧节点与新节点的映射,key为旧节点,value为新节点
private final Map<Node, Node> mapper = new HashMap<>();
public Node copyRandomList(Node head) {
if (head == null) {
return null;
}
// 创建新节点,建立映射
for (Node curr = head; curr != null; curr = curr.next) {
mapper.put(curr, new Node(curr.val));
}
// 为新节点的next和random赋值
for (Node curr = head; curr != null; curr = curr.next) {
Node newNode = mapper.get(curr);
newNode.next = mapper.get(curr.next);
newNode.random = mapper.get(curr.random);
}
// 返回第一个新节点
return mapper.get(head);
}
}