16、栈案例4:八皇后问题(非递归实现)

题源及算法思路来源于“网易云课堂:数据结构实战完全手册(夏曹俊·丁宋涛)”

八皇后问题(非递归实现)的需求

  八皇后问题,是一个古老而著名的问题,是回溯算法的典型案例。该问题是国际西洋棋棋手马克斯·贝瑟尔于1848年提出:在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。

八皇后问题(非递归实现)的数据和通用函数

i表示第i行,j表示第j列,“i+j”表示第i行第j列格的副对角线,“i-j+9”表示第i行第j列格的主对角线

#define TRUE 1
#define FALSE -1 

// 处理第i行上第j个元素的冲突问题的数组
int a[9];// 当前行第j列
int b[17];// 当前行第j列的副对角线
int c[17];// 当前行第j列的主对角线

// 存储每一行第几列放置皇后的数组
int s[9];

// 出现冲突或得出一个解的时候,进行回溯的番薯
void MoveQueen(int i, int j)
{
    a[j] = TRUE;
	b[i + j] = TRUE;
	c[i - j + 9] = TRUE;
}

// 得出一个解的时候,进行打印结果的函数
void PrintResult()
{
    for(int i = 1; i < 9; i++)
    {
        printf("%d, %d\n", i, s[i]);
    }
    printf("\n");
}

八皇后问题(非递归实现)的简单实现

采用栈(后进先出)和回溯算法的思想,通过不断的尝试搜索寻找问题的解

void EightQueen()
{
    int i, j;

    // 因为对角线有15条,且i+j从2开始
    // 初始化全部的皇后冲突,假设此时皇后可随意放置
    for(i = 2; i <= 16; i++)
    {
        if(i >= 2 && i <= 9)
        {
            a[i - 1] = TRUE;
        }
        b[i] = TRUE;
        c[i] = TRUE;
    }

    i = 1;
    j = 1;
    // 开始使用回溯法进行处理
    while(i <= 8)
    {
        while(j <= 8)
        {
            /* 在第i行,寻找潜在的第j列,可选原则如下:
               a[j]==TRUE,表示当前行的第j列格可放置
               b[i+j]==TRUE、c[i-j+9]==TRUE,表示当前行的第j列格的主副对角线皆不冲突
            */
            if(a[j] == TRUE && b[i + j] == TRUE && c[i - j + 9] == TRUE)
            {
                break;
            }
            j++;// 未找到则继续遍历
        }
        // 找到当前行潜在的第j列可放置,
        if(j <= 8)
        {
            // 放置棋子
            a[j] = FALSE;// 确定该位置棋子可留,该位置便为FALSE
            b[i + j] = FALSE;// 将该位置的副对角线的棋子全部清空,并将其标记为不可防止
            c[i - j + 9] = FALSE;// 将该位置的主对角线的棋子全部清空,并将其标记为不可防止
            // 将第i行第j列放置的位置存储
            s[i] = j;
            
            i++;// 进行下一行的遍历
            j = 1;// j重置
        }
        else
        {
            // 当前的第i行已经不能再放入元素了,那么回溯到上一行再次寻找
            i--;
            j = s[i];
            MoveQueen(i, j);
            j++;
        }
    }
}

输出结果:

1, 1
2, 5
3, 8
4, 6
5, 3
6, 7
7, 2
8, 4

八皇后问题(非递归实现)的求全解实现

采用栈(后进先出)和回溯算法的思想,通过不断的尝试搜索寻找问题的解,对简单实现进行了一些改进,当求得一种解法后,直接输出结果,然后清空当前结果,回溯到上一行,寻找第二种可行的摆法,直到回溯到第一行,且不能再回溯为止,最终可输出92种摆法

void EightQueenEx()
{
    int i, j;

    // 因为对角线有15条,且i+j从2开始
    // 初始化全部的皇后冲突,假设此时皇后可随意放置
    for(i = 2; i <= 16; i++)
    {
        if(i >= 2 && i <= 9)
        {
            a[i - 1] = TRUE;
        }
        b[i] = TRUE;
        c[i] = TRUE;
    }

    i = 1;
    j = 1;
    // 开始使用回溯法进行处理
    while(i <= 8)
    {
        while(j <= 8)
        {
            // 在第i行,寻找潜在的第j列
            if(a[j] == TRUE && b[i + j] == TRUE && c[i - j + 9] == TRUE)
            {
                break;
            }
            j++;// 未找到则继续遍历
        }
        // 找到当前行潜在的第j列可放置,设置其对角线不可放置
        if(j <= 8)
        {
            // 放置棋子
            a[j] = FALSE;// 确定该位置棋子可留,该位置便为FALSE
            b[i + j] = FALSE;// 将该位置的副对角线的棋子全部清空,并将其标记为不可防止
            c[i - j + 9] = FALSE;// 将该位置的主对角线的棋子全部清空,并将其标记为不可防止
            
            s[i] = j;
            
            if(i == 8)
            {
                // i==8表明找到了一个解,就进行打印
                PrintResult();
                // 移除i,j位置上的皇后
                MoveQueen(i, j);
                // 回溯到上一行, 寻找下一个解法
                i = i - 1;
                j = s[i];
                MoveQueen(i, j);
                j++;// 从上一行的第j + 1列开始寻找
            }
            else// 当前解法未计算完
            {
                i++;// 进行下一行的遍历
                j = 1;// j重置
            }
        }
        else
        {
            // 当前的第i行已经不能再放入元素了,那么回溯到上一行再次寻找
            i--;
            if(i >= 1)
            {
                j = s[i];
                // 如果未找到所有解法,移除当前行的皇后
                MoveQueen(i, j);
                j++;
            }
        }
    }
}

你可能感兴趣的:(数据结构,学习笔记)