Leetcode刷题 2021.01.13
Leetcode684 冗余连接
在本问题中, 树指的是一个连通且无环的无向图。
输入一个图,该图由一个有着N个节点 (节点值不重复1, 2, …, N) 的树及一条附加的边构成。附加的边的两个顶点包含在1到N中间,这条附加的边不属于树中已存在的边。
结果图是一个以边组成的二维数组。每一个边的元素是一对[u, v] ,满足 u < v,表示连接顶点u 和v的无向图的边。
返回一条可以删去的边,使得结果图是一个有着N个节点的树。如果有多个答案,则返回二维数组中最后出现的边。答案边 [u, v] 应满足相同的格式 u < v。
图论月,彳亍。又是并查集,如果上周做的话可能都无从下手,现在做就是并查集模板了。
class Solution {
public int[] findRedundantConnection(int[][] edges) {
if (edges == null || edges.length == 0) return new int[]{0, 0};
//没什么好注释的,并查集模板题。。
int n = edges.length + 1;
UnionFind uf = new UnionFind(n);
for(int i = 0; i < edges.length; i++){
int x = edges[i][0], y = edges[i][1];
if (!uf.union(x, y)){
return new int[]{x, y};
}
}
return new int[]{0, 0};
}
class UnionFind{
int[] parent;
public UnionFind(int n){
parent = new int[n];
for(int i = 0; i < n; i++){
parent[i] = i;
}
}
private int find(int i){
if(parent[i] == i){
return parent[i];
}
return parent[i] = find(parent[i]);
}
private boolean union(int i, int j){
int root1 = find(i);
int root2 = find(j);
if (root1 == root2) return false;
parent[root1] = parent[root2];
return true;
}
}
}
Leetcode210 元素和小于等于阈值的正方形的最大边长
现在你总共有 n 门课需要选,记为 0 到 n-1。
在选修某些课程之前需要一些先修课程。 例如,想要学习课程 0 ,你需要先完成课程 1 ,我们用一个匹配来表示他们: [0,1]
给定课程总量以及它们的先决条件,返回你为了学完所有课程所安排的学习顺序。
可能会有多个正确的顺序,你只要返回一种就可以了。如果不可能完成所有课程,返回一个空数组。
昨天的每日一题不会做,但是拓扑排序还是学了下。看了Liweiwei的题解,主要还是使用广度优先遍历,将入度为0的点一一入队即可。
class Solution {
public int[] findOrder(int numCourses, int[][] prerequisites) {
if (numCourses <= 0) return new int[0];
//声明邻接表,用list数组表示
List<Integer>[] adj = new ArrayList[numCourses];
//初始化一下
for(int i = 0; i < numCourses; i++){
adj[i] = new ArrayList<>();
}
//每个结点的入度表
int[] indegree = new int[numCourses];
//如果有p[1]指向p[0]的边,就添加到邻接表,并且p[0]的入度加1.
for(int[] p : prerequisites){
adj[p[1]].add(p[0]);
indegree[p[0]]++;
}
//结果数组
int[] res = new int[numCourses];
//广度优先使用队列
Queue<Integer> queue = new LinkedList<>();
//入度为0的点入队
for(int i = 0; i < numCourses; i++){
if (indegree[i] == 0){
queue.offer(i);
}
}
//结果个数
int count = 0;
while (!queue.isEmpty()){
//出队
Integer temp = queue.poll();
//放到结果集
res[count++] = temp;
//遍历出队元素的邻接表,把这些点的入度都减一
for(int ele : adj[temp]){
indegree[ele]--;
//如果入度为0,入队
if (indegree[ele] == 0){
queue.offer(ele);
}
}
}
//count == numCourses说明有结果
if (count == numCourses){
return res;
}
//没有结果返回空数组
return new int[0];
}
}
Leetcode1669 合并两个链表
给你两个链表 list1 和 list2 ,它们包含的元素分别为 n 个和 m 个。
请你将 list1 中第 a 个节点到第 b 个节点删除,并将list2 接在被删除节点的位置。
链表题还是比较简单的吧,无非考虑边界,还有index位置。这种交换顺序的题目,不行就多用几个辅助结点。这题好像有一定的特殊性,边界问题不需要考虑很多。
class Solution {
public ListNode mergeInBetween(ListNode list1, int a, int b, ListNode list2) {
//伪节点,链表题的常见套路了
ListNode dummyHead = new ListNode(0);
dummyHead.next = list1;
ListNode cur = dummyHead;
ListNode prevHead = null;
ListNode prevTail = null;
//先找a的位置的前一个结点,和b结点的后一个位置
for(int i = 0; i < b + 2; i++){
if (i == a) prevHead = cur;
cur = cur.next;
}
prevTail = cur;
//再找list的尾结点
ListNode cur2 = list2, prev = null;
while (cur2 != null){
prev = cur2;
cur2 = cur2.next;
}
//两个链上就行
prevHead.next = list2;
prev.next = prevTail;
return dummyHead.next;
}
}