leetcode:N-Queens II 的位运算非递归解法
2014-06-28 14:47
302 查看
呼,第一篇博文,写得不好欢迎扔白菜^_^
题目如下:
Follow up for N-Queens problem.
Now, instead outputting board configurations, return the total number of distinct solutions .
![](http://www.leetcode.com/wp-content/uploads/2012/03/8-queens.png)
这是经典的N皇后问题,就是在一个N*N的国际象棋棋盘上放置N个皇后,使得任意一个皇后不能吃到别的皇后,按国际象棋的规则,皇后可以吃与它在同一行、同一列或者对角线上的棋子,上面是题目中给出的一种可行摆法,其中对于每一个皇后,它所在的行、列以及对角线上都没有别的皇后。自然能想到,当在一行中的某列放置一个皇后时,去检查之前放的皇后能不能吃到它,如果能吃到,则不能摆,于是在该行中的下一列进行试探,如果能摆放,则摆放,然后继续到下一行,如果一行中所有列都不能摆放,就回退到上行一,把上一行的皇后移到下一列再进入该行进行摆放,这是一个递归的过程,其中的关键在于检查之前放的皇后能不能吃到它。目前最高效的判断方法是用位运算来判断,大家可以参考/article/10225093.html。本文的重点不在于讲解如何使用位运算求解该问题,而是给出用位运算求解该问题的一个非递归版本,因为我在网上找到的用位运算求解该问题的都是递归做的,相信已经有大牛实现过位运算求解该问题的非递归版本,也许是没贴出来而已~
好~在给出用位运算求解该问题的一个非递归版本,先给出我的一个递归的版本,方便大家对照着看,其中的详细原理请参考上面给出的链接:
下面讨论非递归的版本,要把递归转化成非递归,就必须先明白递归的工作原理,其中的关键是递归工作栈,大家看看上面的func函数,当它在自己的函数体中再调用自己的时候,那么下一次进入func时,它是不是修改了里面的变量?那么但一次调用结束后,函数退出了,回到上一层,这些变量不是被修改了么?如何找回修改之前的值?原来函数在每次调用自己前,将变量压入递归工作栈中,那么在一次进入func时,里面的变量就随它爱怎么修改就怎么修改,反正我已经保存了之前的变量值了。一次调用结束后,回到上一层,就把刚才保存的变量弹出栈,恢复调用前的值,明白了这些原理,就可以自己用栈去模拟递归啦~下面给出我的非递归版本,其中的变量含义与递归版本中的同名变量相同:
以上两个版本代码均在leetcode中AC了,欢迎大家参考及吐槽哈~
题目如下:
Follow up for N-Queens problem.
Now, instead outputting board configurations, return the total number of distinct solutions .
![](http://www.leetcode.com/wp-content/uploads/2012/03/8-queens.png)
这是经典的N皇后问题,就是在一个N*N的国际象棋棋盘上放置N个皇后,使得任意一个皇后不能吃到别的皇后,按国际象棋的规则,皇后可以吃与它在同一行、同一列或者对角线上的棋子,上面是题目中给出的一种可行摆法,其中对于每一个皇后,它所在的行、列以及对角线上都没有别的皇后。自然能想到,当在一行中的某列放置一个皇后时,去检查之前放的皇后能不能吃到它,如果能吃到,则不能摆,于是在该行中的下一列进行试探,如果能摆放,则摆放,然后继续到下一行,如果一行中所有列都不能摆放,就回退到上行一,把上一行的皇后移到下一列再进入该行进行摆放,这是一个递归的过程,其中的关键在于检查之前放的皇后能不能吃到它。目前最高效的判断方法是用位运算来判断,大家可以参考/article/10225093.html。本文的重点不在于讲解如何使用位运算求解该问题,而是给出用位运算求解该问题的一个非递归版本,因为我在网上找到的用位运算求解该问题的都是递归做的,相信已经有大牛实现过位运算求解该问题的非递归版本,也许是没贴出来而已~
好~在给出用位运算求解该问题的一个非递归版本,先给出我的一个递归的版本,方便大家对照着看,其中的详细原理请参考上面给出的链接:
int totalNQueens(int n) { //ans:记录找到的解的个数。 //finished:用n位全1表示行已经放好,这是显然的, //因为每行都可以放一个,关键是放在该行的哪一列 long ans=0,finished=1; //将finished变成n位全1 finished=(finished<<n)-1; func(0,0,0,ans,finished); return ans; } void func(int row,int leftDiag,int rightDiag,long &ans,long finished){ //row:当前哪一列已经摆好了,那一列对应的位就是1,否则是0 /*leftDiag:对当前行,之前已经摆好的皇后中,由于从右上角到左下角的对角线的影响(之前的皇后可以通过这个对角线吃掉要摆的皇后), 造成当前行的哪一列不能摆,那一列对应的位就是1,否则是0*/ /*rightDiag:和leftDiag类似,对当前行,之前已经摆好的皇后中,由于从左上角到右下角的对角线的影响, 造成当前行的哪一列不能摆,那一列对应的位就是1,否则是0*/ if(row==finished){ //当row==finished,即row的n位全是1, //就是说每一列都摆好了,则找到了一个解 ans++; }else{ //canPlace:当前行的哪一列可以摆,那一列对应的位就是1,否则是0 long canPlace=finished&(~(row|leftDiag|rightDiag)); while(canPlace){//如果至少一列可以摆 long pos=canPlace&(-canPlace);//取出最右边的那一列 canPlace-=pos;//把该列从能摆的列中去掉 func(row|pos,(leftDiag|pos)<<1,(rightDiag|pos)>>1,ans,finished);//递归进入下一行 } } }
下面讨论非递归的版本,要把递归转化成非递归,就必须先明白递归的工作原理,其中的关键是递归工作栈,大家看看上面的func函数,当它在自己的函数体中再调用自己的时候,那么下一次进入func时,它是不是修改了里面的变量?那么但一次调用结束后,函数退出了,回到上一层,这些变量不是被修改了么?如何找回修改之前的值?原来函数在每次调用自己前,将变量压入递归工作栈中,那么在一次进入func时,里面的变量就随它爱怎么修改就怎么修改,反正我已经保存了之前的变量值了。一次调用结束后,回到上一层,就把刚才保存的变量弹出栈,恢复调用前的值,明白了这些原理,就可以自己用栈去模拟递归啦~下面给出我的非递归版本,其中的变量含义与递归版本中的同名变量相同:
int totalNQueens(int n) { //下面注释中提到的func函数是指递归版本中的func函数 long ans=0,finished=1,row=0,leftDiag=0,rightDiag=0,canPlace=1; finished=(finished<<n)-1; stack<long> myStack;//用栈模拟递归压栈 /*大家想想调用func函数的时候,哪些变量需要保存(即压栈)? 是row,leftDiag,rightDiag,canPlace。为什么呢?因为递归进入下一次的func函数时, 里面的row,leftDiag,rightDiag,canPlace要被修改,如果不保存,那么本次func调用结束后, 就找不到原来的row,leftDiag,rightDiag,canPlace啦*/ myStack.push(row); myStack.push(leftDiag); myStack.push(rightDiag); canPlace=finished&(~(row|leftDiag|rightDiag)); myStack.push(canPlace); while(!myStack.empty()){ //按压栈相反的顺序弹出栈,这个不用解释了吧? canPlace=myStack.top(); myStack.pop(); rightDiag=myStack.top(); myStack.pop(); leftDiag=myStack.top(); myStack.pop(); row=myStack.top(); myStack.pop(); //下面就是func中的while循环 while(canPlace){ long pos=canPlace&(-canPlace); canPlace-=pos; //大家看func中是不是算完canPlace-=pos之后就要下一次调用func了?所以马上压栈~ myStack.push(row); myStack.push(leftDiag); myStack.push(rightDiag); myStack.push(canPlace); row=row|pos; if(row==finished) ans++; leftDiag=(leftDiag|pos)<<1; rightDiag=(rightDiag|pos)>>1; canPlace=finished&(~(row|leftDiag|rightDiag)); } } return ans; }
以上两个版本代码均在leetcode中AC了,欢迎大家参考及吐槽哈~
相关文章推荐
- [LeetCode系列]爬梯问题的递归解法转换为迭代解法
- LeetCode 22:Generate Parentheses的递归,回溯两种解法
- [LeetCode]Reverse Linked List(该题也可以使用递归解法==还没有用递归写过)
- 【LeetCode】104. Maximum Depth of Binary Tree 解法及注释,递归,深度搜索
- Binary Tree Inorder Traversal [leetcode] 非递归的三种解法
- LeetCode Same Tree 简单,简洁的递归解法
- LeetCode之maxDepth非递归解法
- LeetCode Binary Tree Postorder Traversal 二叉树后序遍历 递归和非递归解法
- merge two sort list-leetcode 有序链表合并的二级指针简洁非递归解法
- leetcode-371. Sum of Two Integers 位运算的应用,递归,按位异或a^b
- 【LeetCode】99. Recover Binary Search Tree 分析、解法、注释、中序遍历、递归
- 【LeetCode】108. Convert Sorted Array to Binary Search Tree 解法及注释,分治法,递归
- [LeetCode系列] 二叉树最大深度求解问题(C++递归解法)
- leetcode:path sum(I) 递归与非递归解法
- LeetCode path Sum i ,ii递归和迭代解法
- Leetcode【二叉树】:Binary Tree Postorder Traversal 题解(非递归解法),可以说是【二叉树】入门必须掌握
- 【LeetCode】109. Convert Sorted List to Binary Search Tree 解法及注释,分治,递归
- LeetCode Binary Tree Preorder Traversal 前序遍历二叉树 递归和非递归解法
- [LeetCode系列]N皇后问题递归解法 -- 位操作方式
- [LeetCode系列]有序链表转换为平衡BST的递归解法