Java实现八数码问题

这是本人第一次写博客,也是本人的第一份用Java语言实现的一个相对来说不是很难得一个问题,本来这个问题是人工智能的作业,只是想实现了就好,但是为了让自己进步的快点,还是发到博客上来请大牛们看看有什么还能提高的地方和注意的地方,也希望能对那些初学Java的同学有个帮助,谢谢大家!!

八数码问题:在3×3的方格棋盘上,摆放着18这八个数码,有1个方格是空的,其初始状态如图1所示,要求对空格执行空格左移、空格右移、空格上移和空格下移这四个操作使得棋盘从初始状态到目标状态。

2

5

4

   

1

2

3

3

 

7

   

8

 

4

1

8

6

   

7

6

5

                                                                                         (a)初始状态       (b)目标状态  

废话不多说,直接上代码:

package com.lpp.number;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;

/**
 * 主要收获: 
 * 1、熟悉的掌握并熟练的使用了队列这个数据结构
 * 2、在对数组的操作时,发现数组用等号赋值时,数组对应的物理地址是同一个地方。所以赋值时应该先创建一个
 * 	新的对象,然后将原数组的值相应的赋给新数组。(数组可视为对象,所以每一个对象的赋值都是这样实现的)
 * 3、解决了八数码矩阵无解的情况;如果原数组和目标数组的逆序数和的奇偶性相同,问题有解,否则无解。想要进一步了解逆序数,
 * 	戳网址:http://blog.sina.com.cn/s/blog_5d09995201019kjo.html
 * 值得改进的地方:
 * 1、本人用的是一维数组,所以在处理八数码的边界问题的时候比较繁琐,建议用二维数组,更为简便。
 * 
 * @author panpan
 *
 */
public class Eight {
	public static int[] flags = { -1, -3, 1, 3 }; // left,up,right,down

	// private int[] arr = { 2, 8, 3, 1, 0, 4, 7, 6, 5 }; // 初始数组
	private int[] arr = { 2, 5, 4, 3, 0, 7, 1, 8, 6 }; //初始数组
	private int[] ideaArr = { 1, 2, 3, 8, 0, 4, 7, 6, 5 }; // 八数码目标数组
	private Queue<Node> queue = new LinkedList<Node>(); // 队列
	private List<Integer> list = new ArrayList<Integer>(); //创建集合,用于保存方向(数字序列)
	private int step; // 记录移动需要多少步
	private int count; //记录数组的逆序数的个数

	public static void main(String[] args) {
		Eight et = new Eight();
		et.init();
	}

	private void init() { // 初始化
		if (!judge()) { // 判断是否有解
			System.out.println("该八数码矩阵无解!");
		} else {
			 Node node = new Node(arr, arr.length / 2, 0, null);
			 queue.add(node);  //将节点加入队列中
			 beginMov();
		}
	}

	private boolean judge() {
		int arrInverse = findInverseN(arr); // 查找arr逆序数
		int ideaArrInverse = findInverseN(ideaArr); //查找ideaArr逆序数

		if ((arrInverse % 2 == 0 && ideaArrInverse % 2 == 0) //如果两个数组的逆序数的奇偶性相同则有解,否则无解
				|| (arrInverse % 2 != 0 && ideaArrInverse % 2 != 0))
			return true;
		else
			return false;
	}

	private Integer findInverseN(int[] arr) { //查找逆序列
	count = 0;
	for (int i = arr.length - 1; i >= 0; i--) {
			if (arr[i] == 0)
				continue;
			else {
				for (int j = i - 1; j >= 0; j--) {
					if (arr[j] > arr[i] && arr[j] != 0)
						count++;
				}
			}
		}
		return count;
	}

	private void beginMov() { // 开始移动空格节点
		while (!queue.isEmpty()) {
			Node node = queue.poll(); //出队,获得节点
			int[] arr = node.getArr(); //获得节点的属性(数组)
			int pos = node.getPosition(); //获得节点中空白的位置

			if (isResult(node)) {
				printOpreate(node);
				showOpreate();
				break;
			} else {
				for (int i = 0; i < flags.length; i++) {
					int newPos = pos + flags[i];
					if (newPos >= 0 && newPos < node.getArr().length
							&& node.getTag() != -flags[i]) {  //保证一维数组不越界、防止本次移动方向与上次移动方向相反

						switch (flags[i]) {
						case -3: // up
							/**
							 * 如果这里不重新生成数组,直接用arr时,case1操作数组使数组改变;
							 * case2操作arr时,arr已经改变。所以需要重新定义。
							 */
							int[] arr1 = copy(arr);
							changeMov(arr1, pos, newPos);
							Node node1 = new Node(arr1, newPos, -3, node);
							queue.add(node1);
							break;
						case 3: // down
							int[] arr2 = copy(arr);
							changeMov(arr2, pos, newPos);
							Node node2 = new Node(arr2, newPos, 3, node);
							queue.add(node2);
							break;
						case -1: // left
							if (pos != 3 && pos != 6) { //对左移边界的处理
								int[] arr3 = copy(arr);
								changeMov(arr3, pos, newPos);
								Node node3 = new Node(arr3, newPos, -1, node);
								queue.add(node3);
							}
							break;
						case 1: // right
							if (pos != 2 && pos != 5) { //对右移边界的处理
								int[] arr4 = copy(arr);
								changeMov(arr4, pos, newPos);
								Node node4 = new Node(arr4, newPos, 1, node);
								queue.add(node4);
							}
							break;

						default:
							break;
						}

					}
				}
			}
		}
	}

	private void printOpreate(Node node) { //打印出操作步骤
		Node father = node.getFatherNode();
		int tag = node.getTag();
		if (father == null) {
			System.out.println("经过" + step + "步到达目标节点,步骤如下:");
			return;
		} else {
			step++;
			list.add(tag);
			printOpreate(father); //递归调用,寻找该节点的父节点
		}

	}

	private void showOpreate() { //将数字方向转换为上、下、左、右
		for (int i = list.size(); i > 0; i--) {
			switch (list.get(i - 1)) {
			case -3:
				System.out.print("上 ");
				break;
			case 3:
				System.out.print("下 ");
				break;
			case -1:
				System.out.print("左");
				break;
			case 1:
				System.out.print("右");
				break;

			default:
				break;
			}
		}

	}

	private int[] copy(int[] arr) { // 复制数组
		int[] arr4 = new int[9];
		for (int i = 0; i < arr.length; i++) {
			arr4[i] = arr[i];
		}
		return arr4;
	}

	private void changeMov(int[] arr, int pos, int newPos) { // 移动空格节点到新位置
		int temp;
		temp = arr[pos];
		arr[pos] = arr[newPos];
		arr[newPos] = temp;
	}
	
	private boolean isResult(Node node) { // 是否为目标节点
		int[] newArr = node.getArr();
		int count = 0;
		for (int i = 0; i < ideaArr.length; i++) {
			if (ideaArr[i] == newArr[i])
				count++;
		}
		if (count == ideaArr.length)
			return true;
		else
			return false;
	}
}


package com.lpp.number;

public class Node {
	private int[] arr; //节点的数组属性
	private int position; //节点的空白节点位置
	private int tag = 0;  //节点的方向标志
	private Node fatherNode;  //节点的父节点
	public Node(int[] arr, int position, int tag, Node fatherNode){
		this.arr = arr;
		this.position = position;
		this.tag = tag;
		this.fatherNode = fatherNode;
	}
	/**
	 * @return the fatherNode
	 */
	public Node getFatherNode() {
		return fatherNode;
	}
	/**
	 * @param fatherNode the fatherNode to set
	 */
	public void setFatherNode(Node fatherNode) {
		this.fatherNode = fatherNode;
	}
	/**
	 * @return the tag
	 */
	public int getTag() {
		return tag;
	}
	/**
	 * @param tag the tag to set
	 */
	public void setTag(int tag) {
		this.tag = tag;
	}
	/**
	 * @return the position
	 */
	public int getPosition() {
		return position;
	}
	/**
	 * @param position the position to set
	 */
	public void setPosition(int position) {
		this.position = position;
	}
	public int[] getArr() {
		return arr;
	}
	public void setArr(int[] arr) {
		this.arr = arr;
	}
	
}


  • 7
    点赞
  • 47
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值