board.push_back("0010110"); board.push_back("1001001"); board.push_back("0110010"); board.push_back("1001000"); board.push_back("0100001"); board.push_back("0001101");
利用十字链表就表示如下:
其中第一行是特殊的节点,我们可以称为列节点,第一个是头节点,他们都是不存储数据,然后其他节点就是矩阵中的1,为了调试方便,把它们的节点数据都写为节点序号,说了那么多,都忘记给出节点的数据结构了:
struct Cell{ Cell* up,*down,*left,*right; Cell* col; int rowIndex; Cell(int r):up(NULL),down(NULL),left(NULL),right(NULL),col(NULL),rowIndex(r){} };细心的读者肯定会发现,节点结构中除了上下左右指针外,还有一个col指针,没错,这个是指向每一列的列节点的指针。说了这里,也应该开始讲算法思路了,
1.取出nextCell=head->right,如果nextCell等于head,则算法结束,返回答案,否则进入第2步。
2.删除nextCell,遍历nextCell下面的所有节点,将这些节点所在的行都删除。
3.取出downCell=nextCell->down,如果downCell等于nextCell,则代表不能继续搜索,需要回溯,进入第5步,否则进入第4步。
4.遍历downCell右边的所有节点,将每个节点的列节点都按照第2步的方法删除,然后重新进入第1步。
5.回溯恢复到删除downCell所有右节点的列节点的状态,然后downCell=downCell->down,如果downCell等于nextCell,证明也不能回溯了,需要恢复nextCell,继续返回上一级恢复,否则进入第4步。
算法的思路就只有这么多了,其中恢复节点看上去很难,但只要按照删除的顺序逆回去,堆栈就会很好地帮你实现了。具体删除和恢复的代码如下:
bool removeColCell(Cell* colCell){ colCell->left->right=colCell->right; colCell->right->left=colCell->left; Cell* downCell=colCell->down; if(downCell==colCell) return false; while(downCell!=colCell){ Cell* downRightCell=downCell->right; while(downRightCell!=downCell){ downRightCell->up->down=downRightCell->down; downRightCell->down->up=downRightCell->up; downRightCell=downRightCell->right; } downCell=downCell->down; } return true; } void recoverColCell(Cell* colCell){ Cell* downCell=colCell->down; while(downCell!=colCell){ Cell* downRightCell=downCell->right; while(downRightCell!=downCell){ downRightCell->up->down=downRightCell; downRightCell->down->up=downRightCell; downRightCell=downRightCell->right; } downCell=downCell->down; } colCell->left->right=colCell; colCell->right->left=colCell; }
DLX算法主体代码如下:
bool solveWithDLX(Cell* head,vector<int>& answers){ if(head->right==head) return true; Cell* nextCell=head->right; if(!removeColCell(nextCell)) return false; Cell* downCell=nextCell->down; while(downCell!=nextCell){ Cell* downRightCell=downCell->right; while(downRightCell!=downCell){ Cell* rightColCell=downRightCell->col; removeColCell(rightColCell); downRightCell=downRightCell->right; } if(solveWithDLX(head,answers)){ answers.push_back(downCell->rowIndex); return true; }else{ Cell* downRightCell=downCell->right; while(downRightCell!=downCell){ Cell* rightColCell=downRightCell->col; recoverColCell(rightColCell); downRightCell=downRightCell->right; } downCell=downCell->down; } } recoverColCell(nextCell); return false; }
然后构建十字链表的代码如下:
vector<string> board; board.push_back("0010110"); board.push_back("1001001"); board.push_back("0110010"); board.push_back("1001000"); board.push_back("0100001"); board.push_back("0001101"); Cell* head=new Cell(0); Cell* lastCell=head; vector<Cell*> columnCells; for(int i=0;i<7;i++){ Cell* cell=new Cell(-1-i); lastCell->right=cell; cell->left=lastCell; cell->up=cell; cell->down=cell; columnCells.push_back(cell); lastCell=cell; } lastCell->right=head; head->left=lastCell; int index=1; for(int i=0;i<board.size();i++){ Cell *lastCell=NULL,*firstCell=NULL; for(int j=0;j<board[i].length();j++){ if(board[i][j]=='1'){ Cell* columnCell=columnCells[j]; Cell* cell=new Cell(i+1); cell->up=columnCell->up; cell->down=columnCell; columnCell->up->down=cell; columnCell->up=cell; cell->col=columnCell; if(lastCell==NULL){ firstCell=cell; lastCell=cell; } cell->left=lastCell; lastCell->right=cell; lastCell=cell; } } firstCell->left=lastCell; lastCell->right=firstCell; } vector<int>answers; solveWithDLX(head,answers); for(int i=0;i<answers.size();i++){ cout<<answers[i]<<endl; }最终代码会输出选择的行号。
最后的最后,虽然上面的代码解决一般的X问题没问题,但是当我将数独问题转化成X问题时,再用DLX算法却始终没跑出来,还请各位大神帮忙看一眼,其中删除和恢复的代码都是一样的,只是构建十字链表不太一样,这个十字链表一共有324列,也就是81*4,第一个81是代表81个格子中第几个格子有数,第二个81是代表第几行有哪个数字,第三个81是代表第几列有哪个数字,第四列是代表第几个九宫格有哪个数字,举个例子,如果第2个格子是9,那么在十字链表中将插入一行,这一行在第1列是1,第81+8列是1,第81*2+9+8列是1,以及第81*3+8列是1,而如果第2个格子是空的,那么这个格子会有9种可能,所以会插入9行,第0行中第1列是1,第81+0列是1,第81*2+9+0列是1,第81*3+0是1,其他行也如此类推。代码如下:
void solveSudoku(vector<vector<char>>& board) { Cell* head=new Cell(0); Cell* lastCell=head; vector<Cell*> columnCells; for(int i=0;i<324;i++){ Cell* cell=new Cell(-i-1); cell->up=cell; cell->down=cell; lastCell->right=cell; cell->left=lastCell; columnCells.push_back(cell); lastCell=cell; } lastCell->right=head; head->left=lastCell; for(int i=0;i<9;i++){ for(int j=0;j<9;j++){ if(board[i][j]!='.'){ int num=board[i][j]-'1'; int rowIndex=(i*9+j)*10+num; Cell* posCell=new Cell(rowIndex); Cell* posColumnCell=columnCells[i*9+j]; posCell->up=posColumnCell->up; posCell->down=posColumnCell; posColumnCell->up->down=posCell; posColumnCell->up=posCell; posCell->col=posColumnCell; Cell* rowCell=new Cell(rowIndex); rowCell->left=posCell; posCell->right=rowCell; Cell* rowColumnCell=columnCells[i*9+num+81]; rowCell->up=rowColumnCell->up; rowCell->down=rowColumnCell; rowColumnCell->up->down=rowCell; rowColumnCell->up=rowCell; rowCell->col=rowColumnCell; Cell* colCell=new Cell(rowIndex); colCell->left=rowCell; rowCell->right=colCell; Cell* colColumnCell=columnCells[j*9+num+81*2]; colCell->up=colColumnCell->up; colCell->down=colColumnCell; colColumnCell->up->down=colCell; colColumnCell->up=colCell; colCell->col=colColumnCell; Cell* subCell=new Cell(rowIndex); subCell->left=colCell; subCell->right=posCell; posCell->left=subCell; colCell->right=subCell; int subIndex=i/3*3+j/3; Cell* subColumnCell=columnCells[subIndex*9+num+81*3]; subCell->up=subColumnCell->up; subCell->down=subColumnCell; subColumnCell->up->down=subCell; subColumnCell->up=subCell; subCell->col=subColumnCell; }else{ for(int num=0;num<9;num++){ int rowIndex=(i*9+j)*10+num; Cell* posCell=new Cell(rowIndex); Cell* posColumnCell=columnCells[i*9+j]; posCell->up=posColumnCell->up; posCell->down=posColumnCell; posColumnCell->up->down=posCell; posColumnCell->up=posCell; posCell->col=posColumnCell; Cell* rowCell=new Cell(rowIndex); rowCell->left=posCell; posCell->right=rowCell; Cell* rowColumnCell=columnCells[i*9+num+81]; rowCell->up=rowColumnCell->up; rowCell->down=rowColumnCell; rowColumnCell->up->down=rowCell; rowColumnCell->up=rowCell; rowCell->col=rowColumnCell; Cell* colCell=new Cell(rowIndex); colCell->left=rowCell; rowCell->right=colCell; Cell* colColumnCell=columnCells[j*9+num+81*2]; colCell->up=colColumnCell->up; colCell->down=colColumnCell; colColumnCell->up->down=colCell; colColumnCell->up=colCell; colCell->col=colColumnCell; Cell* subCell=new Cell(rowIndex); subCell->left=colCell; subCell->right=posCell; posCell->left=subCell; colCell->right=subCell; int subIndex=i/3*3+j/3; Cell* subColumnCell=columnCells[subIndex*9+num+81*3]; subCell->up=subColumnCell->up; subCell->down=subColumnCell; subColumnCell->up->down=subCell; subColumnCell->up=subCell; subCell->col=subColumnCell; } } } } vector<int> answers; solveWithDLX(head,answers); for(int i=0;i<answers.size();i++){ int rowIndex=answers[i]; int val=rowIndex%10; rowIndex/=10; int row=rowIndex/9; int col=rowIndex%9; if(board[row][col]=='.') board[row][col]=val+'1'; } }
鸣谢http://www.cnblogs.com/grenet/p/3145800.html给出了大量的图和详细的讲解。