leetcode:N-Queens II 的位运算非递归解法

呼,第一篇博文,写得不好欢迎扔白菜^_^

题目如下:

Follow up for N-Queens problem.

Now, instead outputting board configurations, return the total number of distinct solutions .

      这是经典的N皇后问题,就是在一个N*N的国际象棋棋盘上放置N个皇后,使得任意一个皇后不能吃到别的皇后,按国际象棋的规则,皇后可以吃与它在同一行、同一列或者对角线上的棋子,上面是题目中给出的一种可行摆法,其中对于每一个皇后,它所在的行、列以及对角线上都没有别的皇后。自然能想到,当在一行中的某列放置一个皇后时,去检查之前放的皇后能不能吃到它,如果能吃到,则不能摆,于是在该行中的下一列进行试探,如果能摆放,则摆放,然后继续到下一行,如果一行中所有列都不能摆放,就回退到上行一,把上一行的皇后移到下一列再进入该行进行摆放,这是一个递归的过程,其中的关键在于检查之前放的皇后能不能吃到它。目前最高效的判断方法是用位运算来判断,大家可以参考http://blog.csdn.net/xadillax/article/details/6512318本文的重点不在于讲解如何使用位运算求解该问题,而是给出用位运算求解该问题的一个非递归版本,因为我在网上找到的位运算求解该问题的都是递归做的,相信已经有大牛实现过位运算求解该问题的非递归版本,也许是没贴出来而已~

     好~在给出位运算求解该问题的一个非递归版本,先给出我的一个递归的版本,方便大家对照着看,其中的详细原理请参考上面给出的链接:

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了,欢迎大家参考及吐槽哈~



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值