【Java】Java实现八数码问题

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
 
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;
	}
}
 
 
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;
	}
	
}

运行结果:
在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值