演算法練習帖--56--由斜槓劃分區域(Java)
阿新 • • 發佈:2021-01-27
由斜槓劃分區域(並查集、DFS、BFS)
一、題目簡介
在由 1 x 1 方格組成的 N x N 網格 grid 中,每個 1 x 1 方塊由 /、\ 或空格構成。這些字元會將方塊劃分為一些共邊的區域。
(請注意,反斜槓字元是轉義的,因此 \ 用 “\” 表示。)。
返回區域的數目。
(題目來源:力扣(LeetCode))
示例 1:
輸入:
[
" /",
"/ "
]
輸出:2
解釋:2x2 網格如下:
示例 2: 輸入: [ " /", " " ] 輸出:1 解釋:2x2 網格如下:
示例 3:
輸入:
[
"\\/",
"/\\"
]
輸出:4
解釋:(回想一下,因為 \ 字元是轉義的,所以 "\\/" 表示 \/,而 "/\\" 表示 /\。)
2x2 網格如下:
示例 4:
輸入:
[
"/\\",
"\\/"
]
輸出:5
解釋:(回想一下,因為 \ 字元是轉義的,所以 "/\\" 表示 /\,而 "\\/" 表示 \/。)
2x2 網格如下:
示例 5: 輸入: [ "//", "/ " ] 輸出:3 解釋:2x2 網格如下:
提示:
1 <= grid.length == grid[0].length <= 30
grid[i][j] 是 '/'、'\'、或 ' '。
二、解決方法
1. 並查集(官方題解)
package com.lxf.uf;
public class RegionsBySlashes {
public int regionsBySlashes(String[] grid) {
//獲取網格長
int length=grid.length;
//因為是正方形所以寬也是length,而且由4*length*length小正方形組成
int size=4*length*length;
//初始化並查集物件
UnionFind uf = new UnionFind(size);
for (int i = 0; i < length; i++) {
//轉字串為字元陣列
char[] chars = grid[i].toCharArray();
for (int j = 0; j < chars.length; j++) {
//將二維網格轉換為一維網格
int index=4*(i*length+j);
//獲取當前字元
char c=chars[j];
//單元格內合併
if(c=='/'){
//合併0,3
uf.union(index,index+3);
//合併1,2
uf.union(index+1,index+2);
}else if(c=='\\'){
//合併0,1
//合併2,3
uf.union(index,index+1);
uf.union(index+2,index+3);
}else{
//合併0,1,2,3
uf.union(index,index+1);
uf.union(index+1,index+2);
uf.union(index+2,index+3);
}
//單元格間合併
if(j+1<length){
//合併當前正方形1和下一個正方形的3
uf.union(index+1,index+7);
}
if(i+1<length){
//合併當前正方形2和下一個正方形的0
uf.union(index+2,index+4*length);
}
}
}
return uf.getCount();
}
class UnionFind{
int count;
int[] parent;
int[] rank;
public int getCount() {
return count;
}
/**
* 初始化方法
* @param length
*/
public UnionFind(int length) {
parent=new int[length];
rank=new int[length];
count=length;
for (int i = 0; i < length; i++) {
parent[i]=i;
}
}
/**
* 查詢父親結點方法
* @param index
* @return
*/
public int find(int index){
while (index!=parent[index]){
parent[index]=parent[parent[index]];
index=parent[index];
}
return index;
}
/**
* 合併方法
* @param index1 要合併的第一個集合下標
* @param index2 要合併的第二個集合下標
*/
public void union(int index1,int index2){
//找到對應的父結點
int root1=find(index1);
int root2=find(index2);
if(root1==root2) return;
//按秩合併
if(rank[root1]>rank[root2]){
parent[root2]=root1;
}else if(rank[root1]<rank[root2]){
parent[root1]=root2;
}else{
parent[root2]=root1;
rank[root1]++;
}
count--;
}
}
}
在寫BFS和DFS的之前看一段大佬的解析就豁然開朗了:每個小格分解為 3 * 3 方格,BFS和DFS求連通分量思路。
2. BFS
package com.lxf.test;
public class RegionsBySlashes {
public static void main(String[] args) {
}
private static int tupleL;
public static int regionsBySlashes(String[] grid) {
//獲取網格長
int l=grid.length;
tupleL=3*l;
//新建3*3*length*length小正方形陣列
int[][] tuple = new int[tupleL][tupleL];
for (int i = 0; i < l; i++) {
//轉字串為字元陣列
char[] chars = grid[i].toCharArray();
for (int j = 0; j < chars.length; j++) {
//獲取當前字元
char c=chars[j];
//根據字元給陣列附上初始值:0是空出來的小正方型,是可以連通的;1是斜線,阻隔小正方型的。
//或者比作第200道題目:0是小島,1是水域(我這反過來是因為陣列初始值就是0)
if(c=='/'){
//將9個小正方形按/劃開
tuple[i*3][j*3+2]=1;
tuple[i*3+1][j*3+1]=1;
tuple[i*3+2][j*3]=1;
}else if(c=='\\'){
//將9個小正方形按\劃開
tuple[i*3][j*3]=1;
tuple[i*3+1][j*3+1]=1;
tuple[i*3+2][j*3+2]=1;
}
}
}
//DFS求連通量
int count=0;
for (int i = 0; i < l*3; i++) {
for (int j = 0; j < l*3; j++) {
if(tuple[i][j]==0){
count++;
dfs(tuple,i,j);
}
}
}
return count;
}
/**
* 深度搜索演算法
* @param tuple
* @param i
* @param j
*/
private static void dfs(int[][] tuple, int i, int j) {
//結束條件
if(i<0||j<0||i>=tupleL||j>=tupleL||tuple[i][j]==1){
return;
}
tuple[i][j]=1;
//上下左右搜尋
dfs(tuple,i-1,j);
dfs(tuple,i+1,j);
dfs(tuple,i,j-1);
dfs(tuple,i,j+1);
}
}
3.BFS
package com.lxf.bfs;
import java.util.LinkedList;
import java.util.Queue;
public class RegionsBySlashes {
//(9*grid.length*grid.length)個小正方型組成的大正方型的邊長
private static int tupleL;
public static int regionsBySlashes(String[] grid) {
//獲取網格長
int l=grid.length;
tupleL=3*l;
//新建3*3*length*length小正方形陣列
int[][] tuple = new int[tupleL][tupleL];
for (int i = 0; i < l; i++) {
//轉字串為字元陣列
char[] chars = grid[i].toCharArray();
for (int j = 0; j < chars.length; j++) {
//獲取當前字元
char c=chars[j];
//根據字元給陣列附上初始值:0是空出來的小正方型,是可以連通的;1是斜線,阻隔小正方型的。
//或者比作第200道題目:0是小島,1是水域(我這反過來是因為陣列初始值就是0)
if(c=='/'){
//將9個小正方形按/劃開
tuple[i*3][j*3+2]=1;
tuple[i*3+1][j*3+1]=1;
tuple[i*3+2][j*3]=1;
}else if(c=='\\'){
//將9個小正方形按\劃開
tuple[i*3][j*3]=1;
tuple[i*3+1][j*3+1]=1;
tuple[i*3+2][j*3+2]=1;
}
}
}
//BFS求連通量
int count=0;
for (int i = 0; i < tupleL; i++) {
for (int j = 0; j < tupleL; j++) {
if(tuple[i][j]==0){
//輔助佇列
Queue<Integer> queue = new LinkedList<>();
//將二維座標轉換成一維
queue.add(i*tupleL+j);
tuple[i][j]=1;
count++;
while (!queue.isEmpty()){
//獲取二維座標
int index=queue.remove();
int row=index/tupleL;
int col=index%tupleL;
//上下左右廣度搜索
if(row-1>=0&&tuple[row-1][col]==0){
queue.add((row-1)*tupleL+col);
tuple[row-1][col]=1;
}
if(row+1<tupleL&&tuple[row+1][col]==0){
queue.add((row+1)*tupleL+col);
tuple[row+1][col]=1;
}
if(col-1>=0&&tuple[row][col-1]==0){
queue.add(row*tupleL+col-1);
tuple[row][col-1]=1;
}
if(col+1<tupleL&&tuple[row][col+1]==0){
queue.add(row*tupleL+col+1);
tuple[row][col+1]=1;
}
}
}
}
}
return count;
}
}