一、环境说明
- 本文是 LeetCode 934. 最短的桥,使用c语言实现。
- 模拟深度优先遍历+广度优先遍历。
- 测试环境:Visual Studio 2019。
二、代码展示
int dx[4]={-1,1,0,0};
int dy[4]={0,0,1,-1};
void dfs(int **grid,int n ,int x,int y, int *queue,int *tail){//传入矩阵,行数n,坐标x,y,队列queue,队尾大小tail
//遍历过的点置-1,入队
if(x<0||y<0||x>=n||y>=n||grid[x][y]!=1){
return;//越界,或者不是陆地。
}
queue[(*tail)++] = x*n + y;//特殊编码,存储x/y
grid[x][y] = -1;
for(int i =0;i<4;i++){
dfs(grid,n,x+dx[i],y+dy[i],queue,tail);//向四个方向遍历
}
}
int shortestBridge(int** grid, int gridSize, int* gridColSize){
//n=gridSize=*gridColSize;
int n= gridSize;//行数
int i = 0,j = 0;
for(i = 0;i<gridSize;i++){//找到第一块陆地
for(j = 0;j<*gridColSize;j++){
if(1==grid[i][j]){//遇到陆地
goto haha;//当然能用goto都可以不用goto//删了goto的判断,把下面8行解除注释,效果一样。
}
// if(1==grid[i][j]){//遇到陆地
// break;
// }
}
// if(j!=n)
// if(1==grid[i][j]){//遇到陆地
// break;
// }
}
haha:;//程序员前辈在上,在下用goto了,为了节约时间,陷入魔道。
int *queue = (int*)calloc(sizeof(int),n*n);//最多全部元素入栈
int head = 0,tail = 0;//设置队列和队头队尾指针。
dfs(grid,gridSize,i,j,queue,&tail);//递归遍历i、j,整座岛屿入队。
int step = 0;//宽搜层数
while(tail>head){
int sz = tail - head;//队列元素个数
for(int s = 0;s<sz;s++){//所有陆地和当前遍历到的水面
int x = queue[head]/n;//行号
int y = queue[head++]%n;//列号//head指向下一个位置
for(int t = 0 ;t<4;t++){//整体向外扩展一步
int new_x = x+dx[t];
int new_y = y+dy[t];
if(new_x>=0&&new_y>=0&&new_x<n&&new_y<n){//边界判断
if(0==grid[new_x][new_y]){//遇到水面
queue[tail++] = new_x *n +new_y;//水面入队
grid[new_x][new_y] = -1;//说明遍历过
}else if(1==grid[new_x][new_y]){//遍历到新大陆
free(queue);//释放队列
return step;//返回深搜层数
}
}
}
}
step++;//整座岛+遍历过的水域,向外延伸了一层
}
return 0;
}
三、思路分析
- 题目给定 n × n n\times n n×n矩阵,保证有两座岛,求最短的桥。
- 考虑遍历整个矩阵。找到陆地,则对陆地 D F S DFS DFS,得到整个岛的所有陆地。遍历到的陆地,我们将它置为 − 1 -1 −1,这样就不会再次遍历。为了保存深搜结果,我们建立 q u e u e queue queue队列,将陆地入队。
- q u e u e queue queue存储了一座岛的所有陆地。我们将这座岛,整体扩张,每次只向上下左右扩张一格。每次扩张后,可以想象,可能遇到:
- 自己的陆地 2. 水面 3. 新大陆
- 自己的陆地,已经置 − 1 -1 −1,跳过对它的遍历。
- 水面,说明我们还没遇到新大陆。下次需要从水面继续扩张,所以水面入队 q u e u e queue queue,同时将它置 − 1 -1 −1,不会再次遍历。
- 新大陆,我们搜索的结果。到这里停止遍历,返回目前搜索的层数即可。
得到整座岛的陆地的操作,就是DFS深度优先遍历。
对岛屿扩张的操作,就是BFS宽度优先遍历。
四、博主致语
理解思路很重要!
欢迎读者在评论区留言,作为日更博主,看到就会回复的。
五、AC
六、复杂度分析
- 时间复杂度: O ( n 2 ) O(n^2) O(n2) , n n n是 g r i d S i z e gridSize gridSize行数,一次遍历 g r i d grid grid矩阵的时间复杂度是 O ( n 2 ) O(n^2) O(n2)。二重循环只负责找到第一个陆地,找到就跳出循环了。总的来说,遍历找第一个陆地+第一个陆地DFS找整个岛+从第一个岛BFS得到最短的桥,总时间复杂度不会超过 O ( n 2 ) O(n^2) O(n2)。
- 空间复杂度: O ( n 2 ) O(n^2) O(n2),当前需要遍历的 q u e u e queue queue队列的空间复杂度 O ( n 2 ) O(n^2) O(n2),最坏情况下, q u e u e queue queue内元素个数可能接近 n 2 n^2 n2。