C语言-栈与递归-汉诺塔&八皇后(回溯法)

本文详细解析了汉诺塔与八皇后问题的算法实现。汉诺塔部分介绍了递归函数的设计与堆栈逻辑;八皇后部分则通过回溯算法解决了皇后布局问题,并附带完整代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

汉诺塔问题:

(1)问题描述

1.有三根杆子X,Y,Z,X杆上有n只碟子
2. 每次移动一块碟子,小的只能叠在大的上面
3. 把所有碟子从X杆经Y杆全部移到z杆上。

(2)移动步骤

(下图参考懒猫老师的《数据结构》相关课程)

 只要将这三步用函数调用的形式展现,并且以当n=1时,只用将最后一个碟子从Y->Z,即可实现递归求步骤

(3)函数说明

  • void hanoi(int n, char x, char y, char z)

参数n--要移动的汉诺塔的层数(从上往下数总共的) 

x,y,z---将x上的n个碟子经过y这个中转站,最终全部移动到z上

  • void move(char c1, char c2)

打印由c1->c2,表示把X杆上第n个碟子移到Z杆

(4)完整代码

#include <stdio.h>

int main() {
	int n;
	printf("Please input n:");
	scanf("%d", &n);
	hanoi(n, 'a', 'b', 'c');
}

void hanoi(int n, char x, char y, char z) {
	if (n == 1)//递归函数的出口
		move(x, z);
	else {
		hanoi(n - 1, x, z, y);
		move(x, z);
		hanoi(n - 1, y, x, z);
	}
}

void move(char c1, char c2) {//打印移动情况的函数
	printf("%c->%c\n", c1, c2);
}

(5)堆栈逻辑

递归入栈保存的方式是,保存当前函数执行的行数,以及各参数值;将当前的函数执行行入栈后,从函数头行重新执行该函数,直至达到递归函数的出口条件,再逐个将栈中的函数执行x

 八皇后问题:

(1)问题描述

在一个8×8的棋盘里放置 8个皇后,要求每个皇后两两之间不相“冲”,即在每一横列,竖列,斜列只有一个皇后, 要求输出所有的皇后排列组合可能,具体可以参考下这个图:

(2)算法思路

(下面是参考懒猫老师的《数据结构》相关课程的笔记,讲的超级清楚!)

  • 判断格子是否被占

(循环一行一行的找皇后可以选择的位置,所以行不用加额外的判断)

(1)  int flag[8],初始化值为0,当该列被占后赋值为1

(2)对角线  上对角线:int d1[15];下对角线:int d2[15];初始化值为0,当该列被占后赋值为1

关于对角线的处理,要保证每条对角线上的每个点的判断值一致,才能一次判断一整条,对于上对角线,采取的是di[行-列+7],加7是因为(行-列)的值域为[-7~7],为使数组可以搜索,就+7为正

 对于下对角线,采取的是di[行+列]:

  •  回溯算法

概念:回溯法是一种选优搜索法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。

基本实现:

void dfs(参数)
{
   if(满足递归终止条件)
     {
          存放结果
          return;
     }else{
    for(遍历本层集合中的元素)
    {
        处理结点;
        dfs(参数);//递归
        撤销处理该结点; //回溯
     }
  }
}

本题应用:

以图示八皇后其中一个解为例来说明递归过程:(如果不好理解的话可以直接看代码)

(1)退出递归条件:n==7

自相似:寻找第(n+1)个皇后的位置

(2)一直循环递归调用gernerate(n+1),直至寻找到最后一行的皇后,打印当前解,然后回溯寻找7行(以0行为开始计算的)是否有另一种皇后放法(这里的列是从上一次放过的皇后位置后面找)若没有,从栈中弹出上一次函数调用,即gernerate(7),直接执行回溯(这里要注意递归函数出栈时直接执行入栈行的下一个语句),回溯后循环看第6行是否有另一种皇后放法,以此类推;若有,就宣布占领,入栈调用递归函数gernerate(n+1),往后直至n==7,再次打印新的解。

 (3)完整代码

#include <stdio.h>
#include <stdlib.h>



int place[8];//下标代表行号,值代表列号,用于输出
int flag[8] = {1, 1, 1, 1, 1, 1, 1, 1}; //列
int d1[15] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; //上对角线
int d2[15] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; //下对角线
int count = 1;//解的编号

main() {
	gernerate(0);
	return 0;
}

void gernerate(int n) {
	int col;//列号
	for (col = 0; col < 8; col++) { //每个皇后都要遍历行找
		if (flag[col] != 0 && d1[n - col + 7] != 0 && d2[n + col] != 0) { //该点还未被占领
			place[n] = col; //放置皇后,记录输出
			flag[col] = 0;//更新占领状态
			d1[n - col + 7] = 0;
			d2[n + col] = 0;
			if (n < 7)
				gernerate(n + 1); //入栈
			else
				display();//执行完这步后,一步步往回回溯

			//清除回溯,下面只有在出栈时会执行
			flag[col] = 1;//清除本行占领状态
			d1[n - col + 7] = 1;
			d2[n + col] = 1;
		}

	}
}

void display() {
	printf("第%d组八皇后的解分布图为:\n", count);
	count++;
	for (int i = 0; i < 8; i++) {
		for (int j = 0; j < 8; j++) {
			if (place[i] == j)
				printf("& ");
			else
				printf(". ");
		}
		printf("\n");
	}
}

输出:(共92组解)

 初学小白,有错误欢迎指正喔~!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

想写好代码的小猫头

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值