試探回溯法解決八皇后的問題

算法描述

  • 試探回溯算法
    • 試探
      • 從零開始,嘗試逐步增加候選解的長度(本質上是成批的考察具有特定前綴的所有候選解),這種從長度上逐漸向目標解靠近的嘗試叫做試探
    • 回溯
      • 一般問題候選解都是呈樹狀分佈的,某個節點不合法,則捨棄這個分支,然後回溯到上上個節點,探索其他的可能

八皇后的問題

算法實現

  • 依賴棧實現
// 定義女皇結構
struct Queen {
    int x, y; // x,y座標
    Queen(int xx=0, int yy=0) : x(xx), y(yy) {};

    // 重載==運算符
    bool operator==(Queen const &q) {
        return (x == q.x) // 行衝突
               || (y == q.y) //列衝突
               || (x + y == q.x + q.y) //沿對角線衝突
               || (x - y == q.x - q.y);

    }

    // 重載 !=運算符
    bool operator!=(Queen const &q) {
        return !(*this == q);
    }
};


// 試探回溯法獲取N皇后算法
// 國際象棋要求每行只可能出現一個皇后,所以每行的皇后都在(N,0)爲遠點,不斷的移動列,如果當前行越界,則回溯到上一行
 void placeQueens(int N) {
    // N必需大於1
    if (N < 2) {
        throw "N必需大於1";
    }

    // 棧存放部分皇后信息
    Stack<Queen> solu;

    // 原點皇后
    Queen queen(0, 0);
    do {
        // 如果越界, 則回溯到上一行,並試探下一列
        if (queen.y >= N) {
            queen = solu.pop();
            queen.y++;
        } else {
            // 如果棧中皇后和當前queen衝突 則繼續移動queen列
            while (solu.find(queen) && queen.y < N) {
                queen.y++;
            }

            // 如果當前queen沒有越界 則入棧
            if (queen.y <N) {
                solu.push(queen);

                // 如果找到了全部解 則中斷循環
                if (solu.size() >= N) {
                    break;
                }

                // 否則繼續下一行的第一列
                queen.x++;
                queen.y = 0;
            }
        }

        // 回溯到(0,N-1)之前都是合法的
    } while (queen.x > 0 || queen.y < N);

    // 打印皇后在的位置
    solu.traverse(printItem);
}

可執行腳本

  • main
#include "iostream"
#include "string"
#include "Stack.h"

using namespace std;

// 打印皇后在的位置
template<typename T> void printItem(T queen)
{
    static int number = 1;

    cout <<"第" << number << "個皇后 " << "queen.x=" << queen.x << " queen.y=" << queen.y << endl;
    number ++;
}

// 試探回溯法獲取N皇后算法
// 國際象棋要求每行只可能出現一個皇后,所以每行的皇后都在(N,0)爲遠點,不斷的移動列,如果當前行越界,則回溯到上一行
 void placeQueens(int N) {
    // N必需大於1
    if (N < 2) {
        throw "N必需大於1";
    }

    // 棧存放部分皇后信息
    Stack<Queen> solu;

    // 原點皇后
    Queen queen(0, 0);
    do {
        // 如果越界, 則回溯到上一行,並試探下一列
        if (queen.y >= N) {
            queen = solu.pop();
            queen.y++;
        } else {
            // 如果棧中皇后和當前queen衝突 則繼續移動queen列
            while (solu.find(queen) && queen.y < N) {
                queen.y++;
            }

            // 如果當前queen沒有越界 則入棧
            if (queen.y <N) {
                solu.push(queen);

                // 如果找到了全部解 則中斷循環
                if (solu.size() >= N) {
                    break;
                }

                // 否則繼續下一行的第一列
                queen.x++;
                queen.y = 0;
            }
        }

        // 回溯到(0,N-1)之前都是合法的
    } while (queen.x > 0 || queen.y < N);

    // 打印皇后在的位置
    solu.traverse(printItem);
}


int main() {
    placeQueens(8);
    return 0;
}

  • 女皇結構 Queen.h
// 定義女皇結構
struct Queen {
    int x, y; // x,y座標
    Queen(int xx=0, int yy=0) : x(xx), y(yy) {};

    // 重載==運算符
    bool operator==(Queen const &q) {
        return (x == q.x) // 行衝突
               || (y == q.y) //列衝突
               || (x + y == q.x + q.y) //沿對角線衝突
               || (x - y == q.x - q.y);

    }

    // 重載 !=運算符
    bool operator!=(Queen const &q) {
        return !(*this == q);
    }
};
  • 棧結構 Stack.h
#include "Queen.h"
#include "listNode.h"
#include "list.h"

// 棧的實現
template<typename T>
class Stack : public List<T> {
public :
    // push 入棧
    void push(T data) {
        this->insertAsLast(data);
    }

    // pop 出棧
    T pop() {
        return this->remove(this->last());
    }

    //  top 取棧頂
    T &top() {
        return this->last()->data;
    }
};




  • 列表節點結構 listNode.h

using namespace std;
#define ListNodePosi(T) ListNode<T>*

typedef int Rank;
typedef int RANK;

template<typename T>
struct ListNode {
    T data;
    ListNodePosi(T) pred; // 前綴
    ListNodePosi(T) succ; // 後繼

    // 構造函數
    ListNode(){};

    // 帶參數的構造函數
    ListNode(T e, ListNodePosi(T) p = NULL, ListNodePosi(T) s =NULL) : data(e), pred(p), succ(s) {};

    // 前插入
    ListNodePosi(T) insertAsPred(T const&e);

    // 後插入
    ListNodePosi(T) insertAsSucc(T const&e);
};

template<typename T> ListNodePosi(T)   ListNode<T>::insertAsPred(T const& e){
// 插入新增的節點
    ListNodePosi(T) new_node = new ListNode(e, pred, this);
// 新節點進入連接
    pred->succ = new_node;
    pred = new_node;
    return new_node;
};

// 插入當前節點後面
template<typename T> ListNodePosi(T)  ListNode<T>::insertAsSucc(T const& e){
// 新節點
    ListNodePosi(T) new_node = new ListNode(e, this, succ);

// 當前節點的原後綴節點的前綴節點改成新節點
    succ->pred = new_node;

// 當前節點的後綴節點改成
    succ = new_node;
    return new_node;
};

  • 列表結構 list.h

using namespace std;

#define STATUS_ERROR -1
//typedef  Queen T;

template<typename T>
class List {
private:
    // 規模
    int _size;

    // 頭節點
    ListNodePosi(T) header;

    // 尾指針
    ListNodePosi(T) trailer;

protected:
    // 列表創建時初始化
    void init();

    // 清除所有節點
    int clear();

    // 複製列表中自位置p起的n項
    void copyNodes(ListNodePosi(T) p,int n);

public:
    // 構造方法
    List() { init(); };

    // 析構方法
    ~List(){
        clear();
        delete trailer;
        delete header;
    };//釋放所有節點

    // 獲取規模
    Rank size() { return _size; };

    // 列表是否爲空
    bool empty() { return _size < 1; }

    // 重載[]
    T& operator[](Rank r);

    // 獲取首節點
    ListNodePosi(T) const first() {
        return header->succ;
    }

    // 獲取末節點
    ListNodePosi(T) const last() {
        return trailer->pred;
    }

    // 某個節點是否合法
    bool valid(ListNodePosi(T) p) {
        // 節點不爲空並且不是頭尾節點
        return p && p != trailer && p != header;
    }

    // 插入首節點
    ListNodePosi(T) insertAsFirst(T const& e){
      _size ++;
      return header->insertAsSucc(e);
    };

    // 插入尾節點
    ListNodePosi(T) insertAsLast(T const& e);

    // 將e當作節點p的後繼插入
    ListNodePosi(T) insertA(ListNodePosi(T) p, T const& e);

    // 將e當作節點p的前驅插入
    ListNodePosi(T) insertB(ListNodePosi(T) p, T const& e);

    // 刪除特定的節點,返回特定的元素
    T remove(ListNodePosi(T) p);

    // 遍歷 函數指針機制遍歷
    void traverse(void (*)(T&));

    // 無序去重
    int deduplicate();

    // 無序列表中查找(全局)
    ListNodePosi(T) find(T const &e){
        return find(e, _size, trailer);
    }

    // 無序列表中查找  沒有找到返回NULL
    ListNodePosi(T) find(T const &e, int n , ListNodePosi(T) p);

    // 有序列表內節點p的個前驅元素中找到不大於e的最後一個元素
    ListNodePosi(T) search(T const &e, int n , ListNodePosi(T) p);

    // 插入排序
    void insertionSort(ListNodePosi(T) p,int n);

    // 選擇排序
    void selectionSort(ListNodePosi(T) p,int n);

    // 從啓始於p元素的n個節點中選取最大節點
    ListNodePosi(T) selectMax(ListNodePosi(T) p, int n);
};

// 從啓始於p元素的n個節點中選取最大節點 (rank(p), n+ rank(p)) 不包含
template <typename T> ListNodePosi(T) List<T>::selectMax(ListNodePosi(T) p, int n){
    // 設置默認的最大節點
    ListNodePosi(T) node_max = p;

    // 當前循環的節點
    ListNodePosi(T) node_current = p;

    while(n-- >= 0){
        // 當前節點如果大於最大節點 則替換
        if (node_current->data > node_max->data) {
            node_max = node_current;
        }

        // 進入下一次循環
        node_current = node_current->succ;
    }
    return node_max;
}

// 選擇排序 無序的前綴和有序的後繼,每次從無序的前綴中選擇最大的值填充爲後繼的首元素
template <typename T> void List<T>::selectionSort(ListNodePosi (T) p, int n) {
    // p節點是否有效
    if (!valid(p)) {
        throw STATUS_ERROR;
    }

    // 設置開始無序前綴的開始節點和結束節點 (當前節點的前一個作爲錨點)
    ListNodePosi(T) head = p->pred;

    // 設置開始無序前綴的結束節點
    ListNodePosi(T) tail = p;
    for (int i = 1; i <n ; ++i) {
        tail = tail->succ;
        if (tail == NULL) {
            throw "p節點之後沒有";
        }
    }

    // 本次循環值最大的節點和新生成的節點
    ListNodePosi(T) current_max;
    ListNodePosi(T) node_new;

    // 第一次緩存
    int first_loop = n -1;
    while (n-- > 1) { // 如果只剩下一個節點 則沒有排序的意義

        // 獲取本次循環中值最大得節點
        current_max = selectMax(head->succ, n);

        // 將最大值節點插入後繼有序序列的首元素
        // 第一次插入尾元素的下一個, 以後插入新元素的前一個
        if (n == first_loop) {
            node_new = insertA(tail, current_max->data);
        } else {
            node_new = insertB(node_new, current_max->data);
        }

        // 刪除最大節點
        remove(current_max);
    }
}

// 有序列表內節點p的個前驅元素中找到不大於e的最後一個元素
template <typename T> ListNodePosi(T) List<T>::search(T const &e, int n , ListNodePosi(T) p){
    // p可能是trailer
    // 0 <= n <= rank(p) < _size;
    if (n < 0 || n > _size) {
        throw STATUS_ERROR;
    }

    while(n-- >= 0) {
        p = p->pred;
        if (p->data <= e) {
            break;
        }
    }
    return p;
}

// 插入排序: 對於起始於節點p的n個節點進行排序(包含N)    將序列分成有序的前綴和無序的後綴, 反覆的將無序後綴的首元素插入到前綴合適的位置
template <typename T> void List<T>::insertionSort(ListNodePosi (T) p, int n) {
    // p必需是合法的節點 && rank(p) + n <+ _size
    if (!valid(p)) {
        cout << "不合法的節點p" << endl;
        throw STATUS_ERROR;
    }
    ListNodePosi (T) node_small;
    for(int i = 0; i< n; i ++) {
        // 有序前綴中不大於無序後繼首元素的最後一個節點
         node_small = search(p->data, i, p);

        // 首元素插入前綴有序列表
        insertA(node_small, p->data);

        // 更新循環依據
        p = p->succ;

        // 元素後繼的首元素
       remove(p->pred);
    }
}

// 無序列表中查找, 在節點p的n個前驅元素中查數據e, 並返回邏輯次序最大的節點
template <typename T>
ListNodePosi(T) List<T>::find(T const &e, int n , ListNodePosi(T) p){
    // 如果n 異常
    if (n < 0  || n > _size) {
        return NULL;
    }

    // 另外rank(p) >= n
    while(n-- > 0) {

        // 循環直到n個結束
        p = p->pred;

        // 如果p節點之前沒有n個節點, 則拋出異常
        if (p == header) {
            throw STATUS_ERROR;
        }

        if (p->data == e) {
            return p;
        }
    }

    return NULL;
}

// 無序列表去重
template <typename T> int List<T>::deduplicate()
{
    // 平凡列表自然無重複
    if (_size < 2) {
        return 0;
    }

    int old_size = _size;

     // 從第二個元素開始每個元素都要和邏輯次序在它之前的節點進行比較
     // 循環節點
    ListNodePosi(T) p = first();
    RANK r = 1;
    while((p = p->succ) != trailer) {

        // p節點之前是否存在這個數據
        ListNodePosi(T) exist_node = find(p->data, r, p);
        exist_node ? remove(exist_node) : r ++;
    }

    return old_size - _size;
}


// 藉助函數指針
template <typename T> void List<T>::traverse(void (*visit)(T &)) {
    for (ListNodePosi(T) p = header->succ; p != trailer ; p = p->succ) {
        visit(p->data);
    }
}

// 刪除特定的節點,返回特定的元素
template <typename T> T List<T>::remove(ListNodePosi(T) p) {
    // 備份待刪除節點
    T data = p->data;

    // 當前頁面的上一個節點的後繼節點改成p的後繼節點
    p->pred->succ = p->succ;

    // p的後繼節點改成p的前驅節點
    p->succ->pred = p->pred;

    delete p;

    // 規模減1
    _size --;
    return data;
}

// 清理掉所有的數據節點,並返回原有節點的數量
template <typename T> int List<T>::clear(){
    int old_size = _size;

    // 反覆刪除首節點,直到列表數據爲空
    while (_size > 0) {
        remove(first());
    }

    return old_size;
}

// 插入尾節點
template <typename T>
ListNodePosi(T) List<T>::insertAsLast(T const& e){
    _size ++;
    return trailer->insertAsPred(e);
}

// 將e當作節點p的後繼插入
template <typename T>
ListNodePosi(T) List<T>::insertA(ListNodePosi(T) p, T const& e){
    _size ++;
    return p->insertAsSucc(e);
}

// 將e當作節點p的前驅插入
template <typename T>
ListNodePosi(T) List<T>::insertB(ListNodePosi(T) p, T const& e){
    // 生成一個新的節點
    _size ++;
    return p->insertAsPred(e);
}


// 重載[]
template<typename T>
T& List<T>::operator[](Rank r) {
    if (r < 0 || r > _size) {
        return -1;
    }

    // 首節點
    ListNodePosi(T) p = first();

    for (int i = 1; i < r; ++i) {
        p = p->succ;
    }
    return p->data;
}

// 初始化列表函數
template<typename T>
void List<T>::init() {
    // 生成頭節點
    header = new ListNode<T>;

    // 生成尾節點
    trailer =new  ListNode<T>;

    // 生成頭尾節點的聯繫
    header->succ = trailer;
    trailer->pred = header;

    header->pred = NULL;
    trailer->succ = NULL;

    // 規模初始化0
    _size = 0;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章