《数据结构课程实践》_02_隐式图的搜索问题_实现

本文介绍了使用Java编程语言和IntelliJ IDEA开发环境,运用A*算法解决3x3九宫格重排问题的过程。通过创建节点类和算法类,实现了状态的存储和搜索策略。实验中,A*算法结合启发式函数f=g+h,其中h表示层数,g表示与目标状态的不同元素个数。实验最终输出了最短移动步骤,加深了对数据结构和算法的理解。
摘要由CSDN通过智能技术生成

一、实验题目

  • 实验要求:
  1. 对九宫重排问题,建立图的启发式搜索求解方法;
  2. 用A*算法求解九宫重排问题。
  • 实验题目
    3х3九宫棋盘,放置数码为1~8的8个棋子,棋盘中留有一个空格,空格周围的棋子可以移动到空格中,从而改变棋盘的布局。根据给定初始布局和目标布局,移动棋子从初始布局到达目标布局,求解移动步骤并输出。请设计算法,使用合适的搜索策略,在较少的空间和时间代价下找到最短路径。

实验要求

二、编程语言以及开发环境

1. 编程语言:
选择Java,因为在上学期 project 01中对Java进行了一次提高,现在习惯用Java来编写

2. 开发环境:
选择IntelliJ IDEA,使用起来更加方便

三、源代码

1.main类

package ImplicitFigure;

public class main {
    public static void main(String[]args){
        serves serves = new serves();
        serves.thisIsStart();
    }
}

2.节点类

package ImplicitFigure;

public class Node {

    //用三维数组存储当前状态的情况
    int [][] currentState = new int[3][3];
    //初始位置没有父状态
    Node DadState =null;
    //空格变化,可能引起的状态变化有四个,即四个方向,记它变化后的四个状态为四个孩子状态
    Node child1 = null , child2 = null , child3 = null , child4=null;
    //评估价值的函数 f = g + h ,  h为变化层数,假设初始状态为第0层。下一状态为第1层
    private int f;
    //层数默认初始状态为0层
    int h=0;

    /**
     * 带参数的构造函数
     * @param States
     * @param dad
     * @param child1
     * @param child2
     * @param child3
     * @param child4
     * @param h
     */
    public Node(int [][]States,Node dad,Node child1,Node child2,Node child3,Node child4,int h){

        //将外界数组传入
        for(int i=0;i<3;i++){
            for(int j=0;j<3;j++)
                currentState[i][j]=States[i][j];
        }
        //孩子状态传入
        this.child1 =child1;
        this.child2 =child2;
        this.child3 =child3;
        this.child4 =child4;
        //层数传入
        this.h=h;
    }

    public void setF(int g)
    {
        f=g+h;
    }

    public int getF(){
        return f;
    }
}

3.算法类

package ImplicitFigure;
import java.util.ArrayList;
import java.util.List;

import java.util.Scanner;
public class serves {
    
    //node类:数组,父状态,子状态,层数,价值函数f

    Node Begin;
    Node End;

    //open链表记录空格移动后的新状态
    List<Node> open = new ArrayList<>();
    //close链表存储已经确定了的状态
    List<Node> close = new ArrayList<>();
        
    Scanner scanner=new Scanner(System.in);


    /**
     * 构造函数
     */
    public serves(){
            
        //题目要求二维数组逐个输入
        System.out.println("起始位置:");
        int [][]startState=new int[3][3];
        int [][]purposeState=new int[3][3];
        for(int i=0;i<3;i++)
            for(int j=0;j<3;j++){
                System.out.println("请输入你的数字:");
                startState[i][j]=scanner.nextInt();
            }
            
        //begin存入初始状态
        Begin = new Node(startState,null,null,null,null,null,0);

        //目标要求二维数组逐个输入
        System.out.println("目标位置:");
        for(int i=0;i<3;i++)
            for (int j = 0; j < 3; j++) {
                System.out.println("请输入你的数字:");
                purposeState[i][j] = scanner.nextInt();
            }
            
        //End存入结果状态
        End =new Node(purposeState,null,null,null,null,null,0);

    }


    /**
     * 判断题目是否有答案
     * @param begin
     * @param end
     * @return
     */
    public boolean judgeIfResult(Node begin, Node end){

        //用一维数组开始记录展开
        int []oneArray = new int[9];
        int []oneArray2 = new int[9];

        int inverseNumber1  = 0,  inverseNumber2  = 0 , count1 = 0,  count2 = 0;

        //二维数组展开
        int i,j;
        for(i=0;i<3;i++) {
            for(j=0;j<3;j++){
                oneArray[count1++] = begin.currentState[i][j];
                oneArray2[count2++] = end.currentState[i][j];
            }
        }

        //找第一个状态的逆序数
        for(i=0;i<9;i++){
            for(j=0;j<i;j++){
                if(oneArray[j]>oneArray[i] && oneArray[j]!=0 && oneArray[i]!=0)
                    inverseNumber1 ++;
            }
        }

        //找第二个状态的逆序数
        for(i=0;i<9;i++){
            for(j=0;j<i;j++){
                if(oneArray2[j]>oneArray2[i] && oneArray2[j]!=0 && oneArray2[i]!=0)
                    inverseNumber2 ++;
            }
        }
        //判断两个逆序数的奇数偶性是否相等
        if(inverseNumber1 %2 == inverseNumber2 %2)
            return true;
        else
            return false;
        }


    /**
     * 判断是不是已经完成了题目的要求
     * @param begin
     * @param end
     * @return
     */
    public boolean ifFinish (Node begin, Node end) {

        //判断两个二维数组是否一样
        for(int i=0;i<3;i++) {
            for(int j=0;j<3;j++){
                if(begin.currentState[i][j] != end.currentState[i][j])
                    return false;
            }
        }
        return true;
    }


    /**
     *打印显示移动步骤
     */
    public void printShow(){

        System.out.println("移动步骤如下:");

        for(int count = close.size()-1; count >= 0 ; count--)
        {
            System.out.println("第" + (close.size()-count) + "步:");
            //输出当前已经确定的close链表里的状态的二维数组
            for(int i=0;i<3;i++) {
                for(int j=0;j<3;j++){
                    System.out.print(close.get(count).currentState[i][j]+" ");
                }
                System.out.println();
            }
        }
    }


    /**
     *添加空格移动四个方向之后的四个状态
     * @param parent
     */
    public void addState( Node parent ){

        //存放空格位置
        int count;
        //移动后的新状态
        Node newState;
        //定义四个二维数组用来存储
        int [][]twoArray1 = new int[3][3];
        int [][]twoArray2 = new int[3][3];
        int [][]twoArray3 = new int[3][3];
        int [][]twoArray4 = new int[3][3];

        //把当前状态的二维数组赋值给新定义的四个数组
        for(int i=0;i<3;i++) {
            for(int j=0;j<3;j++){
                twoArray1[i][j]=parent.currentState[i][j];
                twoArray2[i][j]=parent.currentState[i][j];
                twoArray3[i][j]=parent.currentState[i][j];
                twoArray4[i][j]=parent.currentState[i][j];
            }
        }

        //寻找空格位置
        int x=0,y=0;
        for(int i=0;i<3;i++) {
            for(int j=0;j<3;j++){
                if(twoArray1[i][j]==0)
                {
                    x=i;
                    y=j;
                }
            }
        }

        //判断空格是否能够上移
        if ( x-1 >= 0)
        {
            //空格赋值
            count = twoArray1[x][y];
            //空格上边的值换到空格位置
            twoArray1[x][y]=twoArray1[x-1][y];
            //空格放到空格上边的位置
            twoArray1[x-1][y]=count;
            //新状态定义
            newState = new Node(twoArray1,null,null,null,null,null,0);

            //判断移动后的状态是否为要求的最终状态
            if (!compare(Begin, newState))
            {
                //设置当前状态为父状态的第一个子状态
                parent.child1 = newState;
                //设置当前状态的父状态
                newState.DadState = parent;
                //层数+1
                newState.h = parent.h + 1;
                //计算g值,即当前状态与最终状态不一样的值的个数
                int count2 = differentNum(newState, End);
                //设置当前状态的f值
                newState.setF(count2);
                //将新状态添加到open链表
                open.add(newState);
            }
        }

        //判断空格是否能够下移
        if ( x+1 <= 2)
        {
            count=twoArray2[x][y];
            twoArray2[x][y]=twoArray2[x+1][y];
            twoArray2[x+1][y]=count;
            newState = new Node(twoArray2,null,null,null,null,null,0);
            if (!compare(Begin, newState))
            {
                parent.child2=newState;newState.DadState=parent;
                newState.h=parent.h+1;
                int count2 =differentNum(newState, End);
                newState.setF(count2);
                open.add(newState);
            }
        }

        //判断空格能不能左移
        if ( y-1 >= 0)
        {
            count=twoArray3[x][y];
            twoArray3[x][y]=twoArray3[x][y-1];
            twoArray3[x][y-1]=count;
            newState = new Node(twoArray3,null,null,null,null,null,0);
            if (!compare(Begin, newState))
            {
                parent.child3=newState;newState.DadState=parent;
                newState.h=parent.h+1;
                int count2=differentNum(newState, End);
                newState.setF(count2);
                open.add(newState);
            }
        }

        //判断空格能不能右移
        if ( y+1 <= 2)
        {
            count=twoArray4[x][y];
            twoArray4[x][y]=twoArray4[x][y+1];
            twoArray4[x][y+1]=count;
            newState = new Node(twoArray4,null,null,null,null,null,0);
            if (!compare(Begin, newState))
            {
                parent.child4=newState;newState.DadState=parent;
                newState.h=parent.h+1;
                int count2=differentNum(newState, End);
                newState.setF(count2);
                open.add(newState);
            }
        }
    }


    /**
     * 比较是否完成运算
     * @param begin
     * @param newState
     * @return
     */
    public boolean compare(Node begin,Node newState){
        //判断是否完成
        if (begin != null && ifFinish(begin,newState))
            return true;

        if (begin.child1 != null)
            compare(begin.child1, newState);
        if (begin.child2 != null)
            compare(begin.child2, newState);
        if (begin.child3 != null)
            compare(begin.child3, newState);
        if (begin.child4 != null)
            compare(begin.child4, newState);
        return false;
    }


    /**
     * 计算g值,即当前状态与最终状态不一样的值的个数
     * @param first
     * @param second
     * @return
     */
        public int differentNum(Node first,Node second){
            int count=0;
            for (int i = 0; i < 3; i++)
                for (int j = 0; j < 3; j++)
                    if(first.currentState[i][j]!=second.currentState[i][j])
                        count++;
            return count;
        }


    /**
     *寻找open链表里面状态F值最小的一个,然后重新排列open,双向冒泡排序
     */
    public void openSort(){

        //替换节点
        Node node;

        int left=0,right= open.size()-1;

        while(left<right)
        {
            for(int i=left+1 ; i <= right ; i++){
                if(open.get( left).getF() > open.get(i).getF() ){
                    node = open.get(i);
                    open.set(i, open.get(left));
                    open.set(left,node);
                }
            }

            left++;

            for(int i=right-1;i >= left ; i--){
                if(open.get(right).getF() < open.get(i).getF()){
                    node= open.get(i);
                    open.set(i, open.get(right));
                    open.set(right,node);
                }
            }

            right--;
        }
    }


    /**
     * 程序执行开关
     */
    public void thisIsStart(){

        //判断题目是否有答案
        if (judgeIfResult(Begin, End)) {

            Node replace = Begin;

            //将初始态加入到Open链表
            open.add(replace);

            //判断是否初始态与结果相同,如果不同
            while(!ifFinish(replace, End)){
                //删除该状态
                open.remove(0);
                //对初始状态运行空格移动的方法
                addState(replace);
                //之后对open链表进行排序
                openSort();
                //将open链表里f值最小的一个赋值给replace
                replace = open.get(0);
            }

            //将replace添加到close链表中
            Node willAddClose = replace;
            while (willAddClose != null)
            {
                close.add(willAddClose);
                willAddClose = willAddClose.DadState;
            }
            //打印输出
            printShow();
        }

        //没有答案
        else{
            System.out.println("没有结果!");
        }
    }

}

四、运行结果

1. 初始界面:
初始界面

2. 输入起始位置:
输入起始位置

3. 输入目标位置:
输入目标位置

4. 开始运行:

开始运行

五、实验小结

  1. 在本次实验中,关于A*算法存在知识盲区,需要进行查询才能做下去,同时对于数据结构有了更深的理解,对于算法有关的问题开始不再恐惧,逐渐有了解决这一方面问题的自信心。

  2. 发现自己预习的 A* 算法跟当前九宫格隐式图使用的 A* 算法存在不同,但是原理是一样的,我预习的A*算法是存在障碍物的,然后 f = g + h 中,h取自初始位置移动到下一位置的价值,g取自下一位置距离中点位置的绝对价值。在九宫格中,h取自层数,g取自下一位置距离终点位置不同数字的个数。

  3. A*算法重点是价值函数,不同的价值函数可能存在不一样的结果。


今日喜欢的句子:

如果有一个人爱上在这亿万颗星星中仅有的一朵花,这人望着星空的时候,就会觉得幸福。

@刘英俊
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值