>>> 经典的图论问题
>>> 经典的三种解法———【DFS】【BFS】【并差集】
>>> 关键在于【visited/draw】数组的使用 >_<
class Solution {
// DFS
public int findCircleNum(int[][] isConnected) {
int len = isConnected.length;
boolean[] visited = new boolean[len];
int cnt = 0;
for(int i = 0; i < len; i++) {
if(!visited[i]) {
cnt++;
DFS(isConnected, visited, i);
}
}
return cnt;
}
private void DFS(int[][] isConnected, boolean[] visited, int node) {
visited[node] = true;
for(int i = 0; i < isConnected.length; i++) {
if(isConnected[node][i] == 1 && !visited[i]) {
DFS(isConnected, visited, i);
}
}
}
}
class Solution {
public int findCircleNum(int[][] isConnected) {
// BFS
int len = isConnected.length;
boolean[] visted = new boolean[len];
int cnt = 0;
Queue<Integer> queue = new LinkedList<>();
for(int i = 0; i < len; i++) {
if(!visted[i]) {
cnt++;
queue.offer(i);
visted[i] = true;
while (!queue.isEmpty()) {
int node = queue.poll();
for(int k = 0; k < len; k++) {
if(isConnected[node][k] == 1 && !visted[k]) {
queue.offer(k);
visted[k] = true;
}
}
}
}
}
return cnt;
}
}
class Solution {
public int findCircleNum(int[][] isConnected) {
// 并差集
int len = isConnected.length;
UnionSet unionSet = new UnionSet(len);
for(int i = 0; i < len; i++) {
for(int j = i + 1; j < len; j++) {
if(isConnected[i][j] == 1) {
unionSet.union(i, j);
}
}
}
return unionSet.size;
}
}
/*
*【并差集模板(变式)】
* 1.使用了一个size变量,默认值为roots数组的长度
* 2.在合并(union)时,判断两个将要合并的节点是否已经一组,如果不是,则本次的合并导致size--
*/
class UnionSet {
int size;
int[] roots;
public UnionSet(int len) {
size = len;
roots = new int[len];
for(int i = 0; i < len; i++) {
roots[i] = i;
}
}
public int findRoot(int node) {
if(node == roots[node]) {
return node;
}
roots[node] = findRoot(roots[node]);
return roots[node];
}
public void union(int node1, int node2) {
if(!isUnion(node1, node2)) {
roots[findRoot(node1)] = findRoot(node2);
size--;
}
}
public boolean isUnion(int node1, int node2) {
return findRoot(node1) == findRoot(node2);
}
}
>>> 一个重要的优化操作是:每次n都模5,将n限制在0,1,2,3,4,这样既能判断是否能被5整除又避免了n过大
>>> 两个小细节是:乘2的操作使用位运算;"<<"的优先级很低,甚至低于"+","n << 1 + A[i]"应该写成"(n << 1) + A[i]"
class Solution {
public List<Boolean> prefixesDivBy5(int[] A) {
int len = A.length;
List<Boolean> res = new ArrayList<>();
int n = 0;
for(int i = 0; i < len; i++) {
n = ((n << 1) + A[i]) % 5;
res.add(n == 0);
}
return res;
}
}
>>> 本质还是数组按位相加———这类题使用三元表达式极其优雅
class Solution_989 {
public List<Integer> addToArrayForm(int[] A, int K) {
List<Integer> res = new LinkedList<>();
int len = A.length;
int i = len - 1; // 数组下标
int carry = 0; // 进位
while (i >= 0 || K > 0) {
int num1 = (i >= 0 ? A[i] : 0);
int num2 = (K > 0 ? K % 10 : 0);
int num3 = num1 + num2 + carry;
res.add(0, num3 % 10);
carry = num3 / 10;
i--; // 数组移动下标
K /= 10; // 数字移动下标
}
if(carry > 0) {
res.add(0, carry); // 最后的一个进位不要忘记 >_<
}
return res;
}
}
END END E N D