首页 新闻 会员 周边

骑士周游问题如何用栈(stack)来解决?

0
悬赏园豆:50 [已解决问题] 解决于 2019-08-14 20:10

最近初学数据结构,学了栈和队列一节,然后作业任务是解决“骑士游历”问题。
这个题目此前用递归写过,但任务要求不得使用递归做,只允许用栈来做。
请教各位园友,如何用栈来解决这个题目呢?

GeekDragon的主页 GeekDragon | 初学一级 | 园豆:152
提问于:2019-08-12 16:04
< >
分享
最佳答案
1

楼主,按照你所给图片的思路做就好。思路大致总结如下:

  1. 栈的 各个栈针存放的是一个结构体(i,j,k),(i,j,k)的含义是 走到 (i,j) 位置,使用了第 k 种走法 走向下一步。
  2. stack维护的是你走过的格子的坐标,比如stack = [(1,1,3),(2,3,6) ,...],
    表示你从(1,1)开始,使用第3种走法,走到了(2,3),然后从(2,3)开始,采用第6种走法,走向下一个节点.....
  3. 所以当stack.size() == n*n ,也就是说 当栈的大小 == 整个矩阵的格子个数时,
    这个走法就是可行的,打印出来即可。
  4. 楼主,你的图片上面的思路就是正确的,而且很详细,你琢磨图片上的思路一下就可以了。
  5. 下面是我根据图片描述 自己写的c+的代码,仅供参考,希望楼主自己码出来:
#include<stdio.h>
#include<stack>
using namespace std;
struct Node //栈针 
{
    int i,j,k;
    Node(int ii,int jj,int kk){
        i = ii;
        j = jj;
        k = kk;
    }
};
int dir[8][2]={ //题目所说的有8个方向 
    {-2,1},{-1,2},{1,2},{2,1},
    {2,-1},{1,-2},{-1,-2},{-2,-1},
};
int n; //矩阵大小
int beginI,beginJ; //起点 
stack<Node> s;//存放走过的路径 
int grid[128][128];//grid[i][j] = 0 ; 表示格子(i,j) 还没有走过。 
bool check(int i,int j)
{
    if(i<0 || i>=n) return false;
    if(j<0 || j>=n) return false;
    return grid[i][j] == 0;
}
int main()
{
    //输入5 , 1 ,1 结果正确
    //输入4 , 1 , 1 结果正确
    //其余情况未检查,仅供参考,谢谢。 
    scanf("%d",&n); //矩阵大小 
    scanf("%d%d",&beginI,&beginJ); 
    beginI --;
    beginJ --; //个人习惯编码从0开始。 
    int allGrid = n*n;
    s.push(Node(beginI,beginJ,0)); //这是起点 
    grid[beginI][beginJ] = 1;//表示已经走过这个格子了 
    //----------------core code begins.
    while(s.size()<allGrid){
        Node top = s.top(); //取栈顶 
        if(s.size()==1 && top.k==7){ //如果只剩起点,而且起点没办法继续走了,返回 no solution. 
            printf("no solution");
            return 0;
        }
        if(top.k==7){ //如果top这个点 不能再继续走下去了,那么更新grid,出栈 
            grid[top.i][top.j] = 0;
            s.pop();
        }else{
            // top 这点可能还可以继续往下走,那么试试看 
            bool hasNext = false;
            for(int k=top.k+1;k<8;k++){//遍历8个方向 
                int nextI = dir[k][0] + top.i;
                int nextJ = dir[k][1] + top.j;
                if(check(nextI,nextJ)){
                    hasNext = true;
                    grid[nextI][nextJ] = 1;//标记 
                    s.top().k = k;//记住更新栈顶元素的k 
                    s.push(Node(nextI,nextJ,0));//这是我们找到的新的格子,压入栈中 
                    break;
                }
            }
            if(hasNext==false){//如果top这个点无法继续走下去了 
                if(s.size()==1){//如果只剩下起点,返回no solution。 
                    printf("no solution");      
                    return 0; 
                }
                //并非只剩下 起点,还有希望,所以只需要去掉这个栈顶,返回前一步。 
                s.pop();
                grid[top.i][top.j] = 0;
            }
        }
    }
    //-----core code ends.
    
    // ---给每个格子 附上相应的步骤 
    while(allGrid>0){
        Node top = s.top();
        s.pop();
        grid[top.i][top.j] = allGrid;
        allGrid --;
    }
    //打印结果 
    printf("--exist solution--\n");
    for(int i=0;i<n;i++){
        for(int j=0;j<n;j++){
            printf("%5d",grid[i][j]);
        }
        printf("\n");
    }
    return 0;
}
收获园豆:50
Jinke2017 | 菜鸟二级 |园豆:237 | 2019-08-13 19:26

非常非常感谢您的解答!我定会仔细研究的,若是遇到问题,还望您能指教!

GeekDragon | 园豆:152 (初学一级) | 2019-08-13 20:04

@GeekDragon: 好的,不客气。

Jinke2017 | 园豆:237 (菜鸟二级) | 2019-08-13 20:47

@Jinke2017: 您好,我现在遇到一个问题。
我自己写了一个Stack类,没有用C++自带的stack类型;然后又写了一个结构体Knight,也就是您写的Node。
但是当我尝试写Stack<Knight> s; 的时候,编译器报错说"No matching function for call to Knight::Knight",我搜索了一下是构造函数的问题。但我在Stack类中定义了默认构造函数,为什么还会报错呢?

GeekDragon | 园豆:152 (初学一级) | 2019-08-14 11:08

@GeekDragon: 请问你的Stack类是如何定义的呢?

Jinke2017 | 园豆:237 (菜鸟二级) | 2019-08-14 12:31

@GeekDragon: 这里有个demo你参考一下

// template code example
#include<stdio.h>
using namespace std;
template <class numtype>
class Stack
{
    public:
        void push(numtype s){
            printf("add success ... s.id == %d\n",s.i);
        }   
};
struct Node
{
    int i,j,k;
    Node(int ii,int jj,int kk){
        i = ii;
        j = jj;
        k = kk;
    }
};
int main(){
    Stack<Node> ss;
    ss.push(Node(1,1,1));
    return 0;
}   

如果你在学习c++的话,我觉得基本语法还是很重要的,所以有些必要的还是要熟练掌握。

Jinke2017 | 园豆:237 (菜鸟二级) | 2019-08-14 12:42

虽然基础语法比较枯燥,所以还是要熟能生巧,多用就会熟悉了。因为我现在学java了,所以有些基本语法不清楚了,但是以前用的比较多,所以还能记住一些,希望你加油,多学多用,学和用相结合。

Jinke2017 | 园豆:237 (菜鸟二级) | 2019-08-14 12:44

抱歉,这么晚才回复您。
您看,这是结构体:

这是Stack类的:

每次都是这里报错:

GeekDragon | 园豆:152 (初学一级) | 2019-08-14 14:34

@GeekDragon: 试试给 Knight 这个类加默认的构造函数

Jinke2017 | 园豆:237 (菜鸟二级) | 2019-08-14 15:17

@Jinke2017: 好了,现在编译是能过去了,但是最后的运行结果什么都打印不出来。

GeekDragon | 园豆:152 (初学一级) | 2019-08-14 15:42

@GeekDragon: 我觉得应该是 Knight 没有加 空构造函数造成的。因为Stack() 构造函数那里调用了new T[_capacity] ,但是Knight没有空构造函数,所以报错的位置 报在了Stack()那里。

Jinke2017 | 园豆:237 (菜鸟二级) | 2019-08-14 15:42

@Jinke2017:嗯嗯,确实是那里的问题。

GeekDragon | 园豆:152 (初学一级) | 2019-08-14 15:44

@GeekDragon: 这时候就需要你 调试了,首先要保证你的 Stack 类没有问题。保证Stack没有问题之后要测试你的 逻辑代码有没有问题。

Jinke2017 | 园豆:237 (菜鸟二级) | 2019-08-14 15:44

@Jinke2017: 嗯嗯,好的,我调试一下试试,非常感谢您!

GeekDragon | 园豆:152 (初学一级) | 2019-08-14 15:44

@GeekDragon: 不客气~

Jinke2017 | 园豆:237 (菜鸟二级) | 2019-08-14 15:53

@Jinke2017: 您好,我试了一下午发现是我写的Stack类有错误,目前已知是size函数有错误。
除此之外,还有几个问题想请教您:
1.在while循环的判断条件里,为什么是 s.size()<allGrid ?为什么不能换成 !s.empty() (判断栈是否为空)?
2.在最后“---给每个格子 附上相应的步骤 ”这个循环里,为什么每一个格子填的数值就是allGrid?
还请您指教。

GeekDragon | 园豆:152 (初学一级) | 2019-08-14 18:48

@GeekDragon: 有一点我们要明确的,那就是栈里的元素是什么。
栈s的 每个栈元素 就是我们走过的 每一个格子。用结构体表示就是(i,j,k) : i是格子的横坐标,j是格子的纵坐标,k表示 当前这个格子采用第k种方法 走向下一个格子。

  1. 因为栈s存放的是 我们走过的每一个格子,所以当s.size == allGrid(也就是我们走完了所有的格子)的时候,s存放的就是答案,此时可以终止循环。这就是为什么 while(s.size() < allGrid) 的原因,如果我们还没有走完所有的格子(即s.size() < allGird)时,我们还要继续循环。反之,s.size == allGrid时,意味着我们走完了所有的格子,就可以跳出循环了。
  2. 在最后“---给每个格子 附上相应的步骤 ”这个循环里,allGird是递减的,为什么这样赋值呢?
    2.1 因为栈存放的是我们依次走过的格子。
    2.2 还是那个例子,假设结果 栈的情况是这样的 = [(1,1,3),(2,3,6) ,...],
    这表示你从 (1,1)开始,使用第3种走法,走到了(2,3),然后从(2,3)开始,采用第6种走法,走向下一个节点.....
    所以你的第一步就是走向了格子(1,1) , 第二步就是走向格子(2,3) ,因为栈是先进后出,所以我们最后从顶往下遍历,就从allGrid开始递减往下赋值。 比如allGird = 25。那么依次出栈......,一直到(2,3) 的时候就是grid[2][3] = 2 ; allGird -- ; 到(1,1) 的时候 grid[1][1] = 1;
Jinke2017 | 园豆:237 (菜鸟二级) | 2019-08-14 19:03

@Jinke2017: 原来是这样,真的太感谢您了,若是没有您的帮助,恐怕我要耗上好长时间。

GeekDragon | 园豆:152 (初学一级) | 2019-08-14 20:08

@Jinke2017: 我在看其他博客时,发现有一种思想叫“启发式搜索”,一次看两步,那个我着实不理解,不知道您是否有所研究。

GeekDragon | 园豆:152 (初学一级) | 2019-08-14 20:09

@GeekDragon: 启发式算法我并没有了解过,基本的印象是 对搜索的优化,根据一个函数去选择下一个搜索的节点,研究出这个函数是关键所在。我大学时有接触过算法方面的学习,但是很遗憾,启发式算法我没有深入了解,没有办法给你提供更有价值的答案。

Jinke2017 | 园豆:237 (菜鸟二级) | 2019-08-14 20:18

@GeekDragon: @GeekDragon: 我刚搜索了一下,启发式有不少的文章可以供你参考,很抱歉这点没能帮上你。我说些题外话,如果你是大一大二刚接触算法,而且有兴趣继续往这方面学习。我希望你能由浅入深,从简单的贪心算法,到搜索,基础数据结构,再到动态规划,线段树,数论等等。像上面那道题就是属于数据结构和搜索这一块,如果你刚开始学,不建议你过早接触太过深奥的算法,从简单到复杂,逐渐去过渡比较好。在学习算法的过程中,也可以锻炼你的思维能力和编码能力,对于以后的项目开发也有极大的帮助。这只是个人建议,仅供参考,谢谢。

Jinke2017 | 园豆:237 (菜鸟二级) | 2019-08-14 20:26

@Jinke2017: 感谢您的指导与点拨,晚辈听了犹如醍醐灌顶。

GeekDragon | 园豆:152 (初学一级) | 2019-08-14 20:29

@GeekDragon: 只是一点个人建议,最后还是希望你加油。

Jinke2017 | 园豆:237 (菜鸟二级) | 2019-08-14 20:33

@Jinke2017: 我给您发了条私信,您有时间的时候看一下,主要是向您请教一些学习方法。

GeekDragon | 园豆:152 (初学一级) | 2019-08-14 20:35
其他回答(1)
0

递归实际上就是在调用栈。每递归一次,都是把一个函数压到栈中去。

循环加栈,可以代替递归。

大概就是这样吧。stack保存的不只是一个步骤,还有整个棋盘的状态。

while(stack.size()>0)
{
      if(游离完了)
         return stack;
      if(不能再走了)
         stack.pop();

      if(可以有下一步)
      stack.push(下一步);
}
Shendu.CC | 园豆:2138 (老鸟四级) | 2019-08-12 17:25

好的,我先按照您的思路写一下试试,谢谢您!

支持(0) 反对(0) GeekDragon | 园豆:152 (初学一级) | 2019-08-12 18:17
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册