735. 行星碰撞【中等题】【每日一题】
思路:
从左到右遍历行星数组,当行星未爆炸时,加入行星队列,当行星产生爆炸时,对行星队列进行处理。具体操作过程见代码。
另外这题之前写过,与上次的解法思路略有不同。
剑指 Offer II 037. 小行星碰撞
代码:
class Solution {
public int[] asteroidCollision(int[] asteroids) {
Deque<Integer> deque = new ArrayDeque<>();
deque.offerLast(asteroids[0]);
int index = 1,n = asteroids.length;
while (!deque.isEmpty() && index < n){
int cur = asteroids[index];
int last = deque.peekLast();
//dir表示行星行星移动方向,ture为向左,false为向右
boolean dir_cur = cur < 0;
boolean dir_last = last < 0;
//同向或者反向则不碰撞,将cur加入行星队列,左为last,右为cur
//左左右左 左右右右 左左右右
// (dir_last && dir_cur) || (!dir_last && !dir_cur) || (dir_last && !dir_cur)
//上述表达式可简化为 左左 或 右右,即
if (dir_last || !dir_cur){
deque.offerLast(cur);
}else {//相向则发生碰撞,左右右左
int a = Math.abs(cur),b = Math.abs(last);
//质量相等则同归于尽
if (a == b){
deque.pollLast();
if (index == n-1){
break;
}
if (deque.isEmpty()){
deque.offerLast(asteroids[++index]);
}
}else if (a > b){
//当前移动的行星质量比队列尾部的行星质量大,则队列尾部行星爆炸
deque.pollLast();
//如果行星位列就此为空,则需要将当前行星加入行星队列
if (deque.isEmpty()){
deque.offerLast(cur);
}else {
//如果行星队列不为空,则当前行星继续尝试与新的行星队列尾部行星进行碰撞
continue;
}
}
//当前移动的行星质量比队列尾部的行星质量小,则当前行星爆炸,不加入队列,也不对队列进行处理
}
index++;
}
int[] ans = new int[deque.size()];
int i = 0;
while (!deque.isEmpty()){
ans[i++] = deque.pollFirst();
}
return ans;
}
}
剑指 Offer II 115. 重建序列【中等题】
思路:【拓扑排序+BFS】
本题是昨天的剑指 Offer II 113. 课程顺序的升级版。
主要结构代码参考自 剑指 Offer II 113. 课程顺序 官解。
代码实现参考自氧气柠檬 大佬题解
具体实现见代码注释。
代码:
class Solution {
public boolean sequenceReconstruction(int[] nums, int[][] sequences) {
int n = nums.length;
// nums 只有一个元素且不是 1
if (n == 1 && nums[0] != 1){
return false;
}
//用于判断nums中是否为[1,n]中所有整数,不满足则返回false
Set<Integer> set = new HashSet<>();
for (int[] sequence : sequences) {
for (int num : sequence) {
set.add(num);
}
}
if (set.size() != n){
return false;
}
//记录每个数字的入度
int[] indegree = new int[n+1];
//存储有向图
List<List<Integer>> edges = new ArrayList<>();
for (int i = 0; i <= n; i++) {
edges.add(new ArrayList<>());
}
//遍历每个子序列
for (int[] sequence : sequences) {
//根据当前子序列,记录每个节点数字(from)与后边数字(to)的对应关系,并记录每个数字的入度,即indegree[to]++
for (int i = 0; i < sequence.length - 1; i++) {
int from = sequence[i],to = sequence[i+1];
//去重,由于nums中每个数字唯一,因此每个节点的有向图中节点也不能重复。
//因此 from后面的数字不能重复
if (!edges.get(from).contains(to)){
//在from数字对应的列表追加 数字to
edges.get(from).add(to);
//to的入度+1,代表to前边多了一个数字
indegree[to]++;
}
}
}
//建立BFS搜索队列
Queue<Integer> queue = new LinkedList<>();
//将所有入度为0的节点放入队列
for (int i = 1; i <= n; i++) {
if (indegree[i] == 0){
queue.offer(i);
}
}
//记录拓扑排序结果
int[] result = new int[n];
int index = 0;
//开启BFS搜索,队列表示的是满足从当前节点开始往后的子序列均在sequences中出现
while (!queue.isEmpty()){
//因为题目要保证最短超序列唯一,因此需要保证拓扑排序的结果唯一
//因此每次搜索,只能有一个入度为0的节点,即 queue的大小必须为1
if (queue.size() > 1){
return false;
}
//弹出当前节点
int cur = queue.poll();
//弹出节点的顺序即为拓扑排序结果
result[index++] = cur;
//遍历当前节点cur的所有后续节点 next
for (Integer next : edges.get(cur)) {
//next节点入度减1,表示next前方少了一个节点
indegree[next]--;
//如果next节点的入度为0,说明next前方已没有节点,此时需要将其加入待排序队列
if (indegree[next] == 0){
queue.offer(next);
}
}
}
//此时的result顺序就是我们搜索到的唯一最短超序列,比较其是否与给定的nums相同即可。
for (int i = 0; i < n; i++) {
if (nums[i] != result[i]){
return false;
}
}
return true;
}
}