Flood Fill—DFS和BFS算法的实例讲解(C)(岛屿数量I)

一、前言:

初学编程的小白学习完基础算法总之不知道有什么用。所以本篇从一个有趣的leetcode题目讲解Flood fill算法,即所熟知的DFS广度优先和BFS深度优先算法。

二、题目:

Flood Fill—DFS和BFS算法的实例讲解(C)(岛屿数量I)_第1张图片

三、简介:

维基百科:Flood fill 算法是从一个区域中提取若干个连通的点与其他相邻区域区分开(或分别染成不同颜色)的经典 算法。因为其思路类似洪水从一个区域扩散到所有能到达的区域而得名。在 GNU Go 和 扫雷 中,Flood Fill算法被用来计算需要被清除的区域。
       Flood fill算法其实就是:从一个区域中提取若干个连通的点与其他相邻区域区分开。从一个点扩散开,通过 “深度优先遍历” 或者 “广度优先遍历” 找到与其连通的点,从而发现一片连着的区域。

解题:

https://leetcode-cn.com/problems/number-of-islands/solution/dfs-bfs-bing-cha-ji-python-dai-ma-java-dai-ma-by-l/

本题解题思路就是:
       1.从第一个点开始检索,找到一个为1的数后,“岛数”count+1,然从这个点开始对整个岛进行“深度搜索”或者“广度搜索”。
       2.如果使用的深度搜索,那么在递归搜索时将点设置为0或者其他不为1的数(防止重复计算)。
          如果使用的广度搜索,在入队时就将该点设置为0或者其他不为1的数(防止重复计算)。
       3.然后一个岛搜索完后继续进行搜索,找到下一个为1的数后再重复上面步骤,直到遍历完整个二维数组,再无为1的点。最后的count就是“岛数”。

四、算法代码:

首先先看DFS和BFS实现的相关代码。这里总共有两种方式存储图的信息,一种是邻接表,一种是邻接矩阵。
       当图为稀疏结构时,也就是边数远小于节点数平方时,用邻接表更加节省空间,邻接表如:

Flood Fill—DFS和BFS算法的实例讲解(C)(岛屿数量I)_第2张图片

当图为稠密图,也就是节点间连线很多时,用邻接矩阵更加方便,如更加方便判断两点间有无边:

Flood Fill—DFS和BFS算法的实例讲解(C)(岛屿数量I)_第3张图片

DFS、BFS(邻接表)

#include
#include
#include

#pragma warning(disable:4996)

#define MAX 10
#define INIFINITY 65535
#define TRUE 1
#define FALSE 0

typedef int Boole;  //布尔类型 存储TRUE FALSE

Boole visited[MAX];    //访问标志数组 
					   //邻接表结点定义

typedef char VertexType;  //顶点数据类型	 
typedef int EdgeType;    //边上的权值类型 

typedef struct EdgeNode  //边表结点   存储边表信息 
{
	int adjvex;		    //邻接点域,存储该顶点对应的下标 
	EdgeType weight;	//权值 
	struct EdgeNode *next;	//链域,指向下一个邻接点 
}EdgeNode;

typedef struct VertexNode   //顶点表结点
{
	VertexType data;      //顶点域,存储顶点信息 
	EdgeNode *firstedge;	//边表头指针,指向此顶点的第一个邻接点 
}VertexNode, AdjList[MAX];

typedef struct
{
	AdjList adjList;
	int numVertexes, numEdges;   //图中当前顶点数和边数 
}GraphAdjList, *GraphAdj;

typedef struct LoopQueue { //定义循环队列结构体 
	int data[MAX];
	int front;
	int rear;   //注意每次队尾标记指向最后一个元素的下一个位置 
}Queue, *LQueue;

void InitQueue(LQueue Q) {  //初始化队列 
	Q->front = Q->rear = 0;
}

bool QueueisFull(LQueue Q) { //判断队列是否满了
	if ((Q->rear + 1) % MAX == Q->front) {
		return true;  //已满
	}
	else {
		return false;
	}
}

bool QueueisEmpty(LQueue Q) {//判断队列是否为空 
	if (Q->front == Q->rear) {
		return true;
	}
	return false;
}

void EnQueue(LQueue Q, int i) { //入队列 
	if (!QueueisFull(Q)) {
		Q->data[Q->rear] = i;
		Q->rear = (Q->rear + 1) % MAX;  //队尾指针后移 
	}
}

void DeQueue(LQueue Q, int *k) { //出队列 
	if (!QueueisEmpty(Q)) {
		*k = Q->data[Q->front];
		Q->front = (Q->front + 1) % MAX;
	}
}

/*邻接表创建*/
void create(GraphAdj G)
{
	int i, j, k;
	EdgeNode *e;
	printf("输入顶点数和边数:");
	scanf("%d%d", &G->numVertexes, &G->numEdges);
	getchar();  						//注意要清除缓冲 
	for (i = 0; inumVertexes; i++)          //建立顶点表 
	{
		scanf("%c", &G->adjList[i].data);    //输入顶点的符号 
		G->adjList[i].firstedge = NULL; 		//将边表置空 
		getchar();
	}
	for (k = 0; knumEdges; k++)             //建立边表 
	{
		printf("输入边(Vi,Vj)上的顶点序号:");
		scanf("%d%d", &i, &j);
		/*使用头插法加入边表结点*/
		e = (EdgeNode *)malloc(sizeof(EdgeNode));   //生成结点 
		e->adjvex = j;
		e->next = G->adjList[i].firstedge;
		G->adjList[i].firstedge = e;

		e = (EdgeNode *)malloc(sizeof(EdgeNode));   //生成结点 
		e->adjvex = i;
		e->next = G->adjList[j].firstedge;
		G->adjList[j].firstedge = e;
	}
	printf("\n");
}

/*邻接表的深度优先递归*/
void DFS(GraphAdj G, int i)
{
	EdgeNode *p;
	visited[i] = TRUE;         		//访问过了该顶点,标记为TRUE 
	printf("\t%c", G->adjList[i].data);
	p = G->adjList[i].firstedge;     //让p指向边表第一个结点 

	while (p)                      //在边表内遍历 
	{
		if (!visited[p->adjvex])    //对未访问的邻接顶点递归调用 
			DFS(G, p->adjvex);
		p = p->next;
	}
}

//邻接表的深度遍历操作

void DFSTraverse(GraphAdj G)
{
	int i;
	for (i = 0; inumVertexes; i++)
		visited[i] = FALSE;       //初始设置为未访问 
	for (i = 0; inumVertexes; i++)
		if (!visited[i])       //对未访问的顶点调用DFS,若是连通图只会执行一次 
			DFS(G, i);
}

/*广度优先遍历*/
void BFS(GraphAdj G) {
	Queue *Q = (LQueue)malloc(sizeof(Queue));
	for (int i = 0; i < G->numVertexes; i++) {
		visited[i] = FALSE;
	}
	InitQueue(Q);  //初始化队列 
	for (int i = 0; i < G->numVertexes; i++) {
		visited[i] = TRUE;
		printf("\t%c", G->adjList[i].data);
		EnQueue(Q, i);

		while (!QueueisEmpty(Q)) {
			DeQueue(Q, &i);  //这里不断的修改i的值!! 
			EdgeNode *e = G->adjList[i].firstedge;  //i顶点的邻接链表的第一个结点
			while (e) {//e存在时,将e的所有邻接点加入队列,也就是遍历i的所有邻接点 
				if (!visited[e->adjvex]) { // adjvex是e所表示的结点下标 
					visited[e->adjvex] = TRUE;
					printf("\t%c", G->adjList[e->adjvex].data);
					EnQueue(Q, e->adjvex); //将该结点入队 
				}
				e = e->next; //遍历i的下一个邻接点 
			}
		}
	}
}

int main()
{
	GraphAdjList G;
	create(&G);
	printf("深度优先遍历为:");
	DFSTraverse(&G);
	printf("\n");
	printf("广度优先遍历为:");
	BFS(&G);
	printf("\n图遍历完毕");

	return 0;
}

 

代码中有个特别好的就是用getchar()来清空缓冲区,防止干扰,个人非常喜欢这种处理方式,以前使用scanf前并没有注意到这点。

DFS、BFS(邻接矩阵)

#include 

#define MaxVex        100            //最大顶点数
#define INFINITY    65535        //表示∞
#define TRUE        1
#define    FALSE        0

typedef char        VertexType;    //顶点类型
typedef    int            EdgeType;    //权值类型

typedef int            Bool;
Bool    visited[MaxVex];
 
typedef struct {
    VertexType    vexs[MaxVex];            //顶点数组
    EdgeType    arc[MaxVex][MaxVex];    //邻接矩阵
    int    numVertexes, numEdges;            //当前图中的结点数以及边数
}MGraph;
 
//广度优先遍历需要的循环队列
typedef struct {
    int    data[MaxVex];
    int    front, rear;
}Queue;
 
/****************************************/
//队列的相关操作
//初始化
void InitQueue(Queue *Q)
{
    Q->front = Q->rear = 0;
}
 
//入队
void EnQueue(Queue *Q, int e)
{
    if ((Q->rear+1)%MaxVex == Q->front)
        return ;
 
    Q->data[Q->rear] = e;
    Q->rear = (Q->rear+1)%MaxVex;
}
 
//判空
Bool QueueEmpty(Queue *Q)
{
    if (Q->front == Q->rear)
        return TRUE;
    else
        return FALSE;
}
 
//出队
void DeQueue(Queue *Q, int *e)
{
    if (Q->front == Q->rear)
        return ;
 
    *e = Q->data[Q->front];
    Q->front = (Q->front+1)%MaxVex;
}

/****************************************/
//建立图的邻接矩阵
void CreateMGraph(MGraph *G)
{
    int i, j, k, w;
 
    printf("输入顶点数和边数: ");
    scanf("%d%d", &G->numVertexes,&G->numEdges);
    fflush(stdin);
 
    printf("==============================\n");
    printf("输入各个顶点:\n");
    for (i=0; inumVertexes; ++i)
    {
        printf("顶点%d: ",i+1);
        scanf("%c", &G->vexs[i]);
        fflush(stdin);
    }
 
    for (i=0; inumVertexes; ++i)
    {
        for (j=0; jnumVertexes; ++j)
            G->arc[i][j] = INFINITY;
    }
 
    printf("==============================\n");
    for (k=0; knumEdges; ++k)
    {
        printf("输入边(vi, vj)中的下标i和j和权W: ");
        scanf("%d%d%d", &i,&j,&w);
        G->arc[i][j] = w;
        G->arc[j][i] = G->arc[i][j];
    }
}
 
//输出
void DisMGraph(MGraph *G)
{
    int i, j, k;
    k = G->numVertexes;
    for (i=0; iarc[i][j]);
        }
        putchar('\n');
    }
}
 
/****************************************/
//图的深度优先遍历
void DFS(MGraph G, int i)
{
    int j;
    visited[i] = TRUE;
    printf("%c ",    G.vexs[i]);
 
    for (j=0; jnumVertexes; ++i)
        visited[i] = FALSE;
 
    InitQueue(&Q);
 
    for (i=0; inumVertexes; ++i)
    {
        if (!visited[i])
        {
            visited[i] = TRUE;
            printf("%c ", G->vexs[i]);
            EnQueue(&Q, i);
 
            while (!QueueEmpty(&Q))
            {
                DeQueue(&Q, &i);
                for (j=0; jnumVertexes; ++j)
                {
                    if (!visited[j] && G->arc[i][j]!=INFINITY)
                    {
                        visited[j] = TRUE;
                        printf("%c ", G->vexs[j]);
                        EnQueue(&Q, j);
                    }
                }
            }
        }
    }
}

/****************************************/
//程序入口
int main(){
    MGraph G;
 
    CreateMGraph(&G);
 
    printf("\n图的深度优先遍历为: ");
    DFSTraverse(G);
 
    printf("\n图的广度优先遍历为: ");
    BFSTraverse(&G);
 
    printf("\n");
 
    return 0;
}
 

此题解答代码(深度搜索):

void _numIslands(char** grid, int rowSize, int colSize, int i, int j){
    if(i < 0 || i >= rowSize || j < 0 || j >= colSize){
        return ;
    }
    
    if(grid[i][j] == '1'){
        grid[i][j] = '0';
        _numIslands(grid, rowSize, colSize, i-1, j);
        _numIslands(grid, rowSize, colSize, i+1, j);
        _numIslands(grid, rowSize, colSize, i, j-1);
        _numIslands(grid, rowSize, colSize, i, j+1);
    }
}

int numIslands(char** grid, int gridSize, int* gridColSize){
    int cnt = 0;
    for(int i = 0; i < gridSize; i++){
        for(int j = 0; j < gridColSize[0]; j++){
            if(grid[i][j] == '1'){
                cnt++;
                _numIslands(grid, gridSize, gridColSize[0], i, j);
            }
        }
    }
    
    return cnt;
}

 

你可能感兴趣的:(基础算法)