针对在校大学生的C语言入门学习——扫雷算法讨论

针对在校大学生的C语言入门学习——扫雷算法讨论

  • 在上一篇文章《针对在校大学生的C语言入门学习——扫雷》我使用分治算法作为扫雷扩散的解决方案。分治算法一般都使用递归的写法。如果有些同学感觉递归实在是抽象不好理解,那么今天我使用广度遍历的算法作为解决方案给大家再做演示。
  • 这次我只写算法,也就是上一篇文章中的open函数。其他部分代码不做任何修改。

队列

  • 既然使用广度遍历,那我就不能免俗的使用队列。学习过计算机的同学应该都知道,队列是一种先进先出的数据结构。说的国际化一些就叫FIFO。这次我使用一个一维数组作为队列的内存。使用两个变量分别表示队列的头和队列的尾。头是用来出队的,尾是用来入队的。队列的每个元素是一对坐标。
typedef struct Coord
{
    
    
    int x;
    int y;
}Coord;//表示坐标的结构体

void open(int x, int y)
{
    
    
    Coord queue[WIDTH*HEIGHT];
    int begin = 0, end = 0;
}
  • queue是我定义的数组,用来做队列的内存。begin和end分别表示队列的头和尾,分别用来做出队和入队的逻辑。我这个队列写的比较粗糙。数组的大小我取的是极限状态下的值,也就是屏幕的所有点数。

算法分析

  • 所谓的广度遍历,就是以open函数的参数xy坐标位置为中心,一圈一圈的向外扩散。如何保证这一圈一圈的逐渐扩大呢?大家可以找一张纸画一画,我们是不是可以把第N圈的所有点入队,然后再逐个出队。根据FIFO的原则,先出的肯定就是第N圈中的点。如果出队的点周围雷数是0,就把它周围还没有被点开过的点入队。入队的点一定是第N+1圈中的点。这样当第N圈的点全部出队之后,就该出队第N+1圈的点。
  • 可能还是有点懵懵的,结合我下面的代码就容易理解了。
void open(int x, int y)
{
    
    
    Coord queue[WIDTH*HEIGHT];
    int begin = 0, end = 0;
    Coord c;
    c.x = x;
    c.y = y;
    queue[end++] = c;//以xy为中心点,将中心点首先入队
    do{
    
    
        Coord pc = queue[begin++];//出队一个点
        int count = countMine(pc.x, pc.y);//查找周围的雷数
        map[pc.y][pc.x].show = count+'0';//数字整数转换为数字字符
        if(count == 0)
        {
    
    
            /*
            * 周围点入队
            */
        }
    }while(begin != end);
}
  • do-while中的代码应该很熟悉,我们上一次使用分治算法的时候也写过同样的逻辑。只不过这次判断的点是从队列中获取的。既然使用广度遍历,那我们就要把所有需要遍历的点都放在队列中,然后一个一个出队做判断。begin和end相等说明队列已经空了,没有需要遍历的点,循环结束。countMine函数在上一篇文章已经封装完毕,这里没有任何修改。二维数组map也没做任何修改。
  • 接下来就是周围的点入队的逻辑,在countMine函数中我们已经写好了考虑边界条件的遍历方式,这里可以直接复制。
void pushRound(Coord* queue, int* end, int x, int y)
{
    
    
    int i,j;
    for(i = (y==0?0:y-1);i <= (y==HEIGHT-1?HEIGHT-1:y+1);i++)
    {
    
    
        for(j = (x==0?0:x-1);j <= (x==WIDTH-1?WIDTH-1:x+1);j++)
        {
    
    
            if(map[i][j].show == '*')
            {
    
    
                Coord n;
                n.x = j;
                n.y = i;
                queue[(*end)++] = n;
            }
        }
    }
}
  • 参数end必须定义成指针,因为在pushRound函数中要对open函数中的end变量进行修改。
  • 这里有没有觉得这个双重for循环已经写过很多遍了,很烦?发现只是循环逻辑不一样而已,提醒大家可以使用函数指针传递逻辑,这里我就不做修改了。
  • 接下来open函数中调用即可。
void open(int x, int y)
{
    
    
    Coord queue[WIDTH*HEIGHT];
    int begin = 0, end = 0;
    Coord c;
    c.x = x;
    c.y = y;
    queue[end++] = c;//以xy为中心点,将中心点首先入队
    do{
    
    
        Coord pc = queue[begin++];//出队一个点
        int count = countMine(pc.x, pc.y);//查找周围的雷数
        map[pc.y][pc.x].show = count+'0';//数字整数转换为数字字符
        if(count == 0)
        {
    
    
            pushRound(queue, &end, pc.x, pc.y);
        }
    }while(begin != end);
}
  • 扫雷就到这里吧,因为代码很少,就不给源码下载了。下一次是学生管理系统,代码量将远超扫雷。

猜你喜欢

转载自blog.csdn.net/seamancsdn/article/details/112834738
今日推荐