面试专写-八皇后问题C++完整版本

380 阅读2分钟

1 问题

要在8*8的国际象棋棋盘中放8个皇后,使任意两个皇后都不能互相吃掉。规则是皇后能吃掉同一行、同一列、同一对角线的棋子。有多少种摆放方法?
棋盘

2 缘由

八皇后问题有很多解题思路,包括递归式和迭代式。
无论是网上资源,还是面试书籍,往往只给出关键的算法步骤。
面试时,面试官往往希望能手写一个完整的版本。
例如, 选择迭代方法,必须先考虑是否使用辅助栈?该辅助栈是使用STL版本stack, 还是你自己手写一个栈?
难点是,现场面对面试官,我们能否真的很好地设计一个完备的辅助栈? 在面试时,我比较推荐使用STL的容器,简单方便。
如果这些容器不符合你的需求,可以在STL容器基础上扩建。
例如,解决八皇后问题使用的辅助栈,它实际既需要可以遍历,也需要pop()时候返回栈顶元素。
STL的stack无法满足,需要在其基础上扩展。

3 扩展功能的stack

#pragma once
#include <vector>
using namespace std;

template <typename T, class Container = std::vector<T> > 
class Stack {
private:
    Container _data;
public:
    void push(T const& e) {
        _data.insert(_data.end(), e);
    }

    T pop() {
        T tmp = _data[_data.size() - 1];
        _data.pop_back();
        return tmp;
    }

    //e首次出现的索引
    int find(T e);

    void traverse(void (*)(T&));

    int size() { return _data.size(); }
};

template <typename T, class Container>
int Stack<T, Container>::find(T e) {
    int index = -1;
    typename std::vector<T>::iterator it = std::find(_data.begin(), _data.end(), e);
    if (it != _data.end()) {
        index = it - _data.begin();
    }
    return index;
}

template <typename T, class Container>
void Stack<T, Container>::traverse(void (*visit)(T&)) {
    for (auto i : _data) {
        visit(i);
    }
}

4 支持判等的皇后类

#pragma once
/*
--------------->y
│
│
│
│
↓
x
*/
//皇后类
class Queen {
public:
    int x, y;     //皇后在棋盘上的位置坐标
public:
    Queen(int tmpx = 0, int tmpy = 0) : x(tmpx), y(tmpy) {
    }
    Queen(Queen const& tmpQ) : x(tmpQ.x), y(tmpQ.y) {
    }
    bool operator== (Queen const& q) const {
        if ( (x == q.x)  //列冲突
        || (y == q.y)    //行冲突
        || (x + y == q.x + q.y) //对角线
        || (x - y == q.x - q.y) ) {
            return true;
        }
        return false;
    }

    bool operator!= (Queen const& q) const {
        return !(*this == q);
    }
};

5 关键步骤

//N皇后问题
void n_queen_question(int N) {
    Stack<Queen> solu;
    Queen q(0, 0);                      // 从原点出发试探
    while( !(q.x == 0 && q.y >= N) ) {  // 试探结束条件 q.x == 0 && q.y >= N
        //判断是否已出边界
        if ( N <= solu.size() || q.y >= N) {
            q = solu.pop();
            q.y++;       // 此时可能会出现q.x = 0 && q.y >= N 情况,故需要再次判断是否满足第一个while条件
        } else {
            while( (q.y < N) && (0 <= solu.find(q)) ) {
                q.y++;
                nCheck++;
            }
            if (q.y < N) {
                solu.push(q);
                if (N <= solu.size()) {
                    nSolu++;
                    //打印该次输出结果
                    displayProgress ( solu, N );
                }

                q.x++;
                q.y = 0;
            }
        }

        if ( Step == runMode ) {
            displayProgress ( solu, N );
        }
    }
}

上面完整代码已经上传到github

参考文献