XJTUSE 数据结构与算法第四次作业——任务2

任务2

  1. 题目

为了确保每次迷宫的生成都是成功的,将不再采用任务 1 中的随机擦除边的生成方式,而是采用 Kruskal 最小支撑树的算法来实现迷宫生成。具体的执行步骤如下:

1. 为任务 1 中用来表示迷宫的图中的每一条边都随机生成一个权值(此时的图是一个带权图,所以在表示时可能会和任务 1 的表示有出入,请注意这个细节);

2. 利用 Kruskal 算法对步骤 1 中的图生成最小支撑树 T

3. T 中的每一条边相对应在迷宫中的边擦除掉,此时迷宫就生成了,这样生成的迷宫一定是成功的。

  1. 数据设计

Kruskal算法算法步骤

1.设连通网 N = (V, E ),令最小生成树初始状态为只有 n 个顶点而无边的非连通图 T=(V, { }),每个顶点自成一个连通分量。

2.在 E 中选取代价最小的边,若该边依附 的顶点落在 T 中不同的连通分量上(即: 不能形成环),则将此边加入到 T 中;否则,舍去此边,选取下一条代价最小的边。

3.依此类推,直至 T 中所有顶点都在同一 连通分量上为止。

这个算法需要如下几种数据结构:

  1. 我们需要利用并查集树UnionFindTree判断结点是否连通,以及连通两个非连通分量。
  2. 并查集树和Kruskal算法需要树节点Node。
  3. 我们还需要利用最小优先队列MinPriorityQueue优化边的查找与删除
  4. 我们需要为Graph多加些方法,需要Graph2继承Graph1。
  5. 还需要利用队列LQueue
  6. 边Edge。

  1. 并查集树UnionFindTree

构造函数UnionFindTree(Node[] set)接受一个Node数组作为参数,用于初始化并查集。

  1. public class UnionFindTree {
  2.     //用树实现并查集
  3.     public Node[] set;
  4.     public UnionFindTree(Node[] set) {
  5.         this.set = set;
  6.     }
  7. }

find(int i)方法用于查找元素i所属的集合名字(即根节点的下标)。在查找的过程中,使用了路径压缩的优化方式:将路径上的所有节点的父指针直接指向根节点,从而使得树的深度更加平衡。

  1.     /**
  2.      * 返回包含给定元素的集合名字
  3.      *
  4.      * @param i 元素下标
  5.      * @return 以根结点下标作为集合名字
  6.      */
  7.     public int find(int i) {
  8.         //使用路劲压缩法:在查找某个元素是否属于某个集合时,将该结点到根结点路径上
  9.         // 所有结点的父指针全部改为指向根结点,这种方式可以产生极浅的树
  10.         Node current = set[i];
  11.         if (current.getParent()<0)
  12.             return i;
  13.         return current.setParent(find(current.getParent()));
  14.     }

union(int i, int j)方法用于将两个元素i和j所属的集合进行合并。首先找到两个元素所在集合的根节点,然后根据根节点所表示的树的大小(即节点数的负值)来决定合并的方式。将节点数少的一棵树的根节点的父节点设置为节点数多的一棵树的根节点,并更新节点数。

  1.     /**
  2.      * 生成一个新的集合,该集合是i所属的集合set1和j所属的集合set2的并集
  3.      *
  4.      * @param i
  5.      * @param j
  6.      */
  7.     public void union(int i, int j) {
  8.         //使用重量平衡原则
  9.         int root1 = find(i);
  10.         int num1 = set[root1].getParent();
  11.         int root2 = find(j);
  12.         int num2 = set[root2].getParent();
  13.         if (num1 <= num2) {
  14.             set[root1].setParent(num1+num2);
  15.             set[root2].setParent(root1);
  16.             //将其中结点数少的一棵树的根结点的父结点设置为
  17.             //结点数多的一棵树的根结点
  18.         } else {
  19.             set[root2].setParent(num1+num2);
  20.             set[root1].setParent(root2);
  21.             //将其中结点数少的一棵树的根结点的父结点设置为
  22.             //结点数多的一棵树的根结点
  23.         }
  24.     }

isConnected(int i, int j)方法用于判断元素i和元素j是否在同一个集合中,即判断它们的根节点是否相同。

  1.     /**
  2.      * 判断元素i和元素j是否在同一个分组中
  3.      * @param i
  4.      * @param j
  5.      * @return
  6.      */
  7.     public boolean isConnected(int i,int j){
  8.         return find(i)==find(j);
  9.     }
  10.     public void print() {
  11.         for (int i = 0; i < set.length; i++) {
  12.             System.out.print(set[i].getElement() + " ");
  13.         }
  14.     }

print()方法用于打印当前并查集中所有元素的值。这里不再给出。

测试一下:

  1.     public static void main(String[] args) {
  2.         // 创建一组元素,共有8个元素
  3.         Node[] set = new Node[8];
  4.         for (int i = 0; i < set.length; i++) {
  5.             set[i] = new Node(i, -1); // 初始时每个元素都是一个独立的集合,父节点为-1
  6.         }
  7.         UnionFindTree unionFind = new UnionFindTree(set);
  8.         // 合并一些集合
  9.         unionFind.union(01);
  10.         unionFind.union(23);
  11.         unionFind.union(45);
  12.         unionFind.union(67);
  13.         unionFind.union(02);
  14.         unionFind.union(46);
  15.         unionFind.union(04);
  16.         // 验证元素之间的连通性
  17.         System.out.println("0 和 7 是否连通?" + unionFind.isConnected(07));
  18.         System.out.println("1 和 3 是否连通?" + unionFind.isConnected(13));
  19.         System.out.println("2 和 6 是否连通?" + unionFind.isConnected(26));
  20.         System.out.println("4 和 5 是否连通?" + unionFind.isConnected(45));
  21.         // 打印并查集中的元素
  22.         unionFind.print();
  23.     }

结果如下:

图5  并查集树测试结果

  1. 并查集树树节点Node:
  1. package homework02;
  2. public class Node {
  3.     private int parent;//父结点下标
  4.     private Object element;//存储元素内容
  5.     public Node() {
  6.         this(null, -1);
  7.     }
  8.     public Node(Object element) {
  9.         this(element, -1);
  10.     }
  11.     public Node(Object element, int parent) {
  12.         this.element = element;
  13.         this.parent = parent;
  14.     }
  15.     public int getParent() {
  16.         return parent;
  17.     }
  18.     public int setParent(int parent) {
  19.         return this.parent = parent;
  20.     }
  21.     public Object getElement() {
  22.         return element;
  23.     }
  24.     public void setElement(Object element) {
  25.         this.element = element;
  26.     }
  27. }

  1. 最小优先队列MinPriorityQueue

MinPriorityQueue有三个构造方法:

MinPriorityQueue(): 无参构造函数。创建一个默认大小(DEFAULT_CAPACITY)的最小优先队列初始化了数组array,并将currentSize设置为0。

MinPriorityQueue(int size): 带参构造函数。创建一个指定大小的最小优先队列初始化了数组array,并将currentSize设置为0。

MinPriorityQueue(T[] array): 带参构造函数。使用给定的数组创建一个最小优先队列首先创建一个比给定数组长度大1的数组array,然后将给定数组中的元素复制到新数组的对应位置接下来,它将currentSize设置为给定数组的长度,并调用buildHeap()方法构建堆。

  1. public class MinPriorityQueue<T extends Comparable<T>> {
  2.     //最小优先队列
  3.     private static final int DEFAULT_CAPACITY = 10;//默认大小
  4.     private int currentSize;//当前堆的大小
  5.     private T[] array;//堆数组
  6.     public MinPriorityQueue() {
  7.         this.array = (T[]) new Comparable[DEFAULT_CAPACITY + 1];
  8.         currentSize = 0;
  9.     }
  10.     public MinPriorityQueue(int size) {
  11.         this.array = (T[]) new Comparable[size + 1];
  12.         currentSize = 0;
  13.     }
  14.     public MinPriorityQueue(T[] array) {
  15.         this.array = (T[]) new Comparable[array.length + 1];
  16.         for (int i = 0; i < array.length; i++) {
  17.             this.array[i + 1] = array[i];
  18.         }//从1开始算
  19.         currentSize = array.length;
  20.         buildHeap();
  21.     }
  22. }

insert(T element): 往堆中插入元素。首先检查堆是否已满,如果堆已满,则抛出异常。然后将currentSize递增,将新元素放在数组的最后位置。接着通过将新元素与其父节点进行比较并交换的方式,使新元素上浮到正确的位置,以维持堆的性质。

  1.     /**
  2.      * 往堆中插入元素
  3.      *
  4.      * @param element 元素值
  5.      */
  6.     public void insert(T element) {
  7.         try {
  8.             if (isFull()) {
  9.                 throw new Exception("Array is full!");
  10.             }
  11.             int temp = ++currentSize;
  12.             array[temp] = element;//将element放在数组最后
  13.             while ((temp != 1) && (array[temp].compareTo(array[getParent(temp)]) < 0)) {
  14.                 swap(array, temp, getParent(temp));
  15.                 temp = getParent(temp);//如果比父结点的值小则于其交换
  16.             }//注意根结点的下标为1,不是0
  17.         } catch (Exception e) {
  18.             e.printStackTrace();
  19.         }
  20.     }

findMin(): 返回堆顶元素(最小值),即数组中的第一个元素array[1]。

deleteMin(): 删除堆顶元素。它首先检查堆是否为空,如果为空,则抛出异常。否则,它将堆顶元素与数组中最后一个元素进行交换,并将currentSize减1。然后,它通过将堆顶元素与其子节点进行比较并交换的方式,使堆顶元素下沉到正确的位置,以维持堆的性质。

  1.     public T findMin() {
  2.         return array[1];
  3.     }
  4.     /**
  5.      * 删除堆顶元素
  6.      */
  7.     public void deleteMin() {
  8.         try {
  9.             if (isEmpty()) {
  10.                 throw new Exception("Array is empty!");
  11.             } else {
  12.                 swap(array, 1, currentSize--);//将堆顶元素放到最后,同时删除
  13.                 if (currentSize != 0) siftDown(1);
  14.             }
  15.         } catch (Exception e) {
  16.             e.printStackTrace();
  17.         }
  18.     }

print(): 打印当前堆中的元素。

heapSort(): 对堆进行排序,并逐个输出排序结果。循环调用findMin()方法找到堆中的最小值并打印,然后调用deleteMin()方法删除堆顶元素,直到堆为空为止。

isEmpty(): 判断堆是否为空。如果currentSize为0,则堆为空,返回true;否则,返回false。

isFull(): 判断堆是否已满。如果currentSize等于数组长度减1,则堆已满,返回true;否则,返回false。

  1.     public void print() {
  2.         for (int i = 1; i <= currentSize; i++) {
  3.             System.out.print(array[i] + " ");
  4.         }
  5.     }
  6.     public void heapSort() {
  7.         while (currentSize != 0) {
  8.             System.out.print(findMin() + " ");
  9.             deleteMin();
  10.         }
  11.     }
  12.     public boolean isEmpty() {
  13.         return currentSize == 0;
  14.     }
  15.     public boolean isFull() {
  16.         return currentSize == array.length - 1;
  17.     }

siftDown(int pos):将给定位置pos的元素在堆中下沉到正确的位置,以维持堆的性质。

检查给定位置pos是否合法,再用一个循环来判断当前位置pos是否为叶子节点。如果不是叶子节点,继续执行下面的操作;否则,循环结束。

在循环内部,方法首先获取当前位置pos的左子节点的索引j,即getLeft(pos)。然后,它检查当前位置pos是否有右子节点,并且右子节点的值比左子节点的值更小。如果满足条件,将右子节点的索引j更新为j+1,即取右子节点方法比较当前位置pos的值和子节点array[j]的值。如果当前位置的值比子节点的值小,说明当前位置的值已经比子树中的值都小,不需要继续下沉,直接返回如果当前位置的值比子节点的值大,说明当前位置的值需要下沉。方法调用swap(array, pos, j)交换当前位置pos和子节点array[j]的值,将当前位置的值下沉到子节点的位置最后,将当前位置pos更新为子节点的位置j,继续下次循环,直到当前位置成为叶子节点为止。

  1.     /**
  2.      * 大的值下沉
  3.      *
  4.      * @param pos 当前位置
  5.      */
  6.     private void siftDown(int pos) {
  7.         try {
  8.             if (pos < 0 || pos > currentSize) {
  9.                 throw new Exception("Illegal position!");
  10.             }
  11.             while (!isLeaf(pos)) {
  12.                 int j = getLeft(pos);
  13.                 if ((j < currentSize) && (array[j].compareTo(array[j + 1])) > 0) j++;
  14.                 //跟子树中最小的值交换
  15.                 if (array[pos].compareTo(array[j]) < 0return;
  16.                 //当前值已经比子树中的值都小,则返回
  17.                 swap(array, pos, j);//交换
  18.                 pos = j;
  19.             }
  20.         } catch (Exception e) {
  21.             e.printStackTrace();
  22.         }
  23.     }

一些辅助的私有方法:

  1.     private boolean isLeaf(int i) {
  2.         return i > currentSize / 2;
  3.     }
  4.     private void buildHeap() {
  5.         for (int i = currentSize / 2; i > 0; i--) {
  6.             siftDown(i);//对每个非叶子结点进行下沉操作
  7.             //从右到左,从下到上
  8.         }
  9.     }
  10.     private int getLeft(int i) {
  11.         return 2 * i;
  12.     }
  13.     private int getRight(int i) {
  14.         return 2 * i + 1;
  15.     }
  16.     private int getParent(int i) {
  17.         return i / 2;
  18.     }
  19.     private void swap(T[] array, int x, int y) {
  20.         T temp = array[y];
  21.         array[y] = array[x];
  22.         array[x] = temp;
  23.     }

测试一下:

  1.     public static void main(String[] args) throws Exception {
  2.         Edge edge1 = new Edge(121);
  3.         Edge edge2 = new Edge(122);
  4.         Edge edge3 = new Edge(126);
  5.         Edge edge4 = new Edge(124);
  6.         Edge edge5 = new Edge(125);
  7.         Edge edge6 = new Edge(127);
  8.         Edge edge7 = new Edge(123);
  9. //        Integer[] elements = {1, 2, 6, 4, 5, 7, 3};
  10. //        MinPriorityQueue<Integer> test = new MinPriorityQueue<Integer>(elements);
  11.         Edge[] edges = {edge1, edge2, edge3, edge4, edge5, edge6, edge7};
  12.         MinPriorityQueue<Edge> test = new MinPriorityQueue<Edge>(edges);
  13.         test.print();
  14.         System.out.println();
  15.         test.heapSort();
  16.         System.out.println();
  17.         test.print();
  18.     }

结果如下:

图6  最小优先队列测试结果

  1. Graph2:

不再详细讲解,只列出深度优先搜索和广度优先搜索。

  1.     public void DFS() {
  2.         for (int i = 0; i < getVertexNum(); i++) {
  3.             setMark(i, UNVISITED);
  4.         }//初始化所有顶点
  5.         for (int i = 0; i < getVertexNum(); i++) {
  6.             if (getMark(i) == UNVISITED) {
  7.                 DFSHelp(i);
  8.             }
  9.         }
  10.     }
  11.     public void DFSHelp(int v) {
  12.         System.out.print(V.get(v) + " ");
  13.         setMark(v, VISITED);
  14.         for (int edge = getFirst(v); isEdge(v, edge); edge = getNext(v, edge)) {
  15.             if (getMark(edge) == UNVISITED) {
  16.                 DFSHelp(edge);
  17.             }
  18.         }
  19.     }
  20.     public void BFS() {
  21.         for (int i = 0; i < getVertexNum(); i++) {
  22.             setMark(i, UNVISITED);
  23.         }//初始化所有顶点
  24.         for (int i = 0; i < getVertexNum(); i++) {
  25.             if (getMark(i) == UNVISITED) {
  26.                 BFSHelp(i);
  27.             }
  28.         }
  29.     }
  30.     public void BFSHelp(int v) {
  31.         LQueue<Integer> queue = new LQueue<>();
  32.         queue.enqueue(v);
  33.         setMark(v, VISITED);
  34.         while (!queue.isEmpty()) {
  35.             int temp = queue.dequeue();
  36.             System.out.print(V.get(temp) + " ");
  37.             for (int index = getFirst(temp); isEdge(temp, index); index = getNext(temp, index)) {
  38.                 if (getMark(index) == UNVISITED) {
  39.                     queue.enqueue(index);
  40.                     setMark(index, VISITED);
  41.                 }
  42.             }
  43.         }
  44.     }

测试一下:

  1.     public static void main(String[] args) {
  2.         //定义图的所有顶点
  3.         String[] vertexs = {"A""B""C""D""E""F"};
  4.         //创建图
  5.         GraphM2 graph = new GraphM2(vertexs.length);
  6.         //添加顶点到图中
  7.         for (String vertex : vertexs) {
  8.             graph.insertVertex(vertex);
  9.         }
  10.         //添加边到图中
  11.         graph.insertEdge(021);
  12.         graph.insertEdge(041);
  13.         graph.insertEdge(121);
  14.         graph.insertEdge(151);
  15.         graph.insertEdge(231);
  16.         graph.insertEdge(251);
  17.         graph.insertEdge(351);
  18.         graph.insertEdge(451);
  19.         graph.print();
  20.         graph.DFS();
  21.         System.out.println();
  22.         graph.BFS();
  23.     }

结果如下:

图6  图测试结果

  1. 队列LQueue
  1. package homework02;
  2. import java.util.NoSuchElementException;
  3. public class LQueue<T> {
  4.     private Node<T> front;
  5.     private Node<T> rear;
  6.     private class Node<T> {
  7.         private T data;
  8.         private Node<T> next;
  9.         public Node(T data) {
  10.             this.data = data;
  11.             this.next = null;
  12.         }
  13.     }
  14.     public LQueue() {
  15.         front = null;
  16.         rear = null;
  17.     }
  18.     public boolean isEmpty() {
  19.         return front == null;
  20.     }
  21.     public void enqueue(T item) {
  22.         Node<T> newNode = new Node<>(item);
  23.         if (isEmpty()) {
  24.             front = newNode;
  25.             rear = newNode;
  26.         } else {
  27.             rear.next = newNode;
  28.             rear = newNode;
  29.         }
  30.     }
  31.     public T dequeue() {
  32.         if (isEmpty()) {
  33.             throw new NoSuchElementException("Queue is empty");
  34.         }
  35.         T data = front.data;
  36.         front = front.next;
  37.         if (front == null) {
  38.             rear = null;
  39.         }
  40.         return data;
  41.     }
  42.     public T peek() {
  43.         if (isEmpty()) {
  44.             throw new NoSuchElementException("Queue is empty");
  45.         }
  46.         return front.data;
  47.     }
  48. }

  1. 边Edge:
  1. package homework02;
  2. import org.jetbrains.annotations.NotNull;
  3. //这个类的目的就是为了Kruskal算法中的优先队列创建的
  4. public class Edge implements Comparable<Edge> {
  5.     private int v1;//起点
  6.     private int v2;//终点
  7.     private int weight;//权重
  8.     public Edge(int v1, int v2) {
  9.         this.v1 = v1;
  10.         this.v2 = v2;
  11.     }
  12.     public Edge(int v1, int v2, int weight) {
  13.         this.v1 = v1;
  14.         this.v2 = v2;
  15.         this.weight = weight;
  16.     }
  17.     public int getV1() {
  18.         return v1;
  19.     }
  20.     public void setV1(int v1) {
  21.         this.v1 = v1;
  22.     }
  23.     public int getV2() {
  24.         return v2;
  25.     }
  26.     public void setV2(int v2) {
  27.         this.v2 = v2;
  28.     }
  29.     public int getWeight() {
  30.         return weight;
  31.     }
  32.     public void setWeight(int weight) {
  33.         this.weight = weight;
  34.     }
  35.     @Override
  36.     public int compareTo(@NotNull Edge edge) {
  37.         return Integer.compare(this.weight, edge.weight);
  38.     }
  39.     @Override
  40.     public String toString() {
  41.         return "Edge{" +
  42.                 v1 +
  43.                 "->" + v2 +
  44.                 " weight=" + weight +
  45.                 '}';
  46.     }
  47. }

  1. 算法设计

Kruskal算法算法步骤

1.设连通网 N = (V, E ),令最小生成树初始状态为只有 n 个顶点而无边的非连通图 T=(V, { }),每个顶点自成一个连通分量。

2.在 E 中选取代价最小的边,若该边依附 的顶点落在 T 中不同的连通分量上(即: 不能形成环),则将此边加入到 T 中;否则,舍去此边,选取下一条代价最小的边。

3.依此类推,直至 T 中所有顶点都在同一 连通分量上为止。

  1. public class Kruskal {
  2.     private ArrayList<Edge> MST;//最小生成树中的所有边
  3.     private UnionFindTree ufTree;//并查集树,放索引
  4.     private MinPriorityQueue<Edge> allEdges;//图中所有的边
  5.     private GraphM2 originalGraph;//用相邻矩阵实现的原图
  6.     private int vertexNum;//结点数
  7.     private GraphM2 newGraph;//用相邻矩阵实现的新图
  8.     public Kruskal(GraphM2 originalGraph) {
  9.         this.originalGraph = originalGraph;
  10.         this.vertexNum = originalGraph.getVertexNum();
  11.     }
  12.     public void generateMST() {
  13.         this.MST = new ArrayList<Edge>();//初始化MST
  14.         Node[] nodes = new Node[vertexNum];
  15.         for (int i = 0; i < vertexNum; i++) {
  16.             nodes[i] = new Node(i);
  17.         }
  18.         this.ufTree = new UnionFindTree(nodes);//初始化并查集
  19.         this.allEdges = new MinPriorityQueue<>(originalGraph.getEdgeNum());//初始化优先队列
  20.         //把图中的边放到储存边的最小优先队列中,目的是为了快速检索
  21.         for (int i = 0; i < vertexNum; i++) {
  22.             for (int j = i + 1; j < vertexNum; j++) {
  23.                 if (originalGraph.getWeight(i, j) != GraphM2.UNCONNECTED) {
  24.                     allEdges.insert(new Edge(i, j, originalGraph.getWeight(i, j)));
  25.                 }
  26.             }
  27.         }
  28.         while (!allEdges.isEmpty() && MST.size() < vertexNum - 1) {
  29.             Edge e = allEdges.findMin();
  30.             allEdges.deleteMin();
  31.             int v1 = e.getV1();
  32.             int v2 = e.getV2();
  33.             //判断v1和v2是否已经连通
  34.             if (ufTree.isConnected(v1, v2)) continue;
  35.             ufTree.union(v1, v2);//不连通则连接
  36.             MST.add(e);//将边并入MST
  37.         }
  38.     }
  39.     public ArrayList<Edge> getMST() {
  40.         return MST;
  41.     }
  42. }

这样我们就得到了MST,那么我们还需要作图以及求解路径,我们只需得到down,right,matrix三个矩阵,我们就能根据第一问的算法绘图以及求解路径。下面是得到三个二维数组的方法。

  1.     public int[][] getMatrix(){
  2.         newGraph = new GraphM2(Constants.GRID_SIZE * Constants.GRID_SIZE);
  3.         for (Edge edge : MST){
  4.             int cell1 = Integer.parseInt(originalGraph.getVertexValue(edge.getV1()));
  5.             int cell2 = Integer.parseInt(originalGraph.getVertexValue(edge.getV2()));
  6.             newGraph.insertEdge(cell1,cell2,1);
  7.         }
  8.         return newGraph.getMatrix();
  9.     }
  10.     public int[][] getDown(){
  11.         //把down所有数都写为1000
  12.         int[][] down = new int[Constants.GRID_SIZE][Constants.GRID_SIZE];
  13.         for (int i = 0; i < Constants.GRID_SIZE; i++) {
  14.             for (int j = 0; j < Constants.GRID_SIZE; j++) {
  15.                 down[i][j] = Constants.INNEIGHBOURING;
  16.             }
  17.         }
  18.         for (Edge edge : MST){
  19.             int cell1 = Integer.parseInt(originalGraph.getVertexValue(edge.getV1()));
  20.             int cell2 = Integer.parseInt(originalGraph.getVertexValue(edge.getV2()));
  21.             //保证cell1小于cell2
  22.             if(cell1 > cell2){
  23.                 int temp = cell1;
  24.                 cell1 = cell2;
  25.                 cell2 = temp;
  26.             }
  27.             //保证同一列,将上边cell1格子的写为1
  28.             if(getCol(cell1) == getCol(cell2)){
  29.                 down[getRow(cell1)][getCol(cell1)] = 1;
  30.             }
  31.         }
  32.         return down;
  33.     }
  34.     public int[][] getRight(){
  35.         //把right所有数都写为1000
  36.         int[][] right = new int[Constants.GRID_SIZE][Constants.GRID_SIZE];
  37.         for (int i = 0; i < Constants.GRID_SIZE; i++) {
  38.             for (int j = 0; j < Constants.GRID_SIZE; j++) {
  39.                 right[i][j] = Constants.INNEIGHBOURING;
  40.             }
  41.         }
  42.         for (Edge edge : MST){
  43.             int cell1 = Integer.parseInt(originalGraph.getVertexValue(edge.getV1()));
  44.             int cell2 = Integer.parseInt(originalGraph.getVertexValue(edge.getV2()));
  45.             //保证cell1小于cell2
  46.             if(cell1 > cell2){
  47.                 int temp = cell1;
  48.                 cell1 = cell2;
  49.                 cell2 = temp;
  50.             }
  51.             //保证同一行,将左边cell1格子的写为1
  52.             if(getRow(cell1) == getRow(cell2)){
  53.                 right[getRow(cell1)][getCol(cell1)] = 1;
  54.             }
  55.         }
  56.         return right;
  57.     }

这样我们就可以调用这三个方法得到三个矩阵,再调用第一问的方法进行绘图,求解路径,绘制路径。

下面是Test2类:

首先调用方法得到三个矩阵。

  1. public class Test2 extends JPanel {
  2.     public GraphM2 graph;
  3.     public int padding = Constants.PADDING;
  4.     public int width = (Constants.WIDTH - Constants.PADDING - Constants.PADDING) / Constants.GRID_SIZE;
  5.     public Cell[][] maze;
  6.     public int [][] down;
  7.     public int [][] right;
  8.     public int [][] matrix;
  9.     public List<Integer> path;
  10.     public Puzzle puzzle = new Puzzle();
  11.     public Test2() {
  12.         maze = new Cell[Constants.GRID_SIZE][Constants.GRID_SIZE];
  13.         helpTest();
  14.     }
  15.     public void helpTest(){
  16.         //创建抽象的全连接的图
  17.         //定义图的所有顶点
  18.         Integer[] vertexs = new Integer[Constants.GRID_SIZE * Constants.GRID_SIZE];
  19.         for (int i = 0; i < vertexs.length; i++) {
  20.             vertexs[i] = i;
  21.         }
  22.         //创建图并把顶点放在图中
  23.         graph = new GraphM2(vertexs.length);
  24.         for (Integer vertex : vertexs) {
  25.             graph.insertVertex(vertex);
  26.         }
  27.         //添加随机边到图中
  28.         Random r = new Random();
  29.         for (int i = 0; i < Constants.GRID_SIZE - 1; i++) {
  30.             for (int j = 0; j < Constants.GRID_SIZE; j++) {
  31.                 graph.insertEdge(i * Constants.GRID_SIZE + j, (i + 1) * Constants.GRID_SIZE + j, r.nextInt(300));
  32.             }
  33.         }
  34.         for (int i = 0; i < Constants.GRID_SIZE; i++) {
  35.             for (int j = 0; j < Constants.GRID_SIZE - 1; j++) {
  36.                 graph.insertEdge(i * Constants.GRID_SIZE + j, i * Constants.GRID_SIZE + j + 1, r.nextInt(300));
  37.             }
  38.         }
  39.         //调用Kruskal 算法对步骤 1 中的图生成最小支撑树
  40.         Kruskal kruskal = new Kruskal(graph);
  41.         kruskal.generateMST();
  42.         //得到down和right数组
  43.         down = kruskal.getDown();
  44.         right = kruskal.getRight();
  45.         matrix = kruskal.getMatrix();
  46.         path = puzzle.getPath(matrix);
  47.     }
  48. }

进行绘图:

  1.     //这是个自动调用的方法,必须设置为public
  2.     public void paintComponent(Graphics g) {
  3.         super.paintComponent(g);
  4.         // 画NUM*NUM条黑线
  5.         for (int i = 0; i <= Constants.GRID_SIZE; i++) {
  6.             g.drawLine(padding + i * width, padding, padding + i * width,
  7.                     padding + Constants.GRID_SIZE * width);
  8.         }
  9.         for (int j = 0; j <= Constants.GRID_SIZE; j++) {
  10.             g.drawLine(padding, padding + j * width, padding + Constants.GRID_SIZE * width,
  11.                     padding + j * width);
  12.         }
  13.         // 使用背景色,在有路径的格子之间画边,把墙抹掉
  14.         g.setColor(this.getBackground());
  15.         //根据Puzzle中的数组进行抹边
  16.         for (int i = 0; i < Constants.GRID_SIZE; i++) {
  17.             for (int j = 0; j < Constants.GRID_SIZE; j++) {
  18.                 maze[i][j] = new Cell(i, j);
  19.                 if (down[i][j] == Constants.NEIGHBOURING) {
  20.                     maze[i][j].setDown(Constants.NEIGHBOURING);
  21.                 }
  22.                 if (down[i][j] == Constants.INNEIGHBOURING) {
  23.                     maze[i][j].setDown(Constants.INNEIGHBOURING);
  24.                 }
  25.                 if (right[i][j] == Constants.NEIGHBOURING) {
  26.                     maze[i][j].setRight(Constants.NEIGHBOURING);
  27.                 }
  28.                 if (right[i][j] == Constants.INNEIGHBOURING) {
  29.                     maze[i][j].setRight(Constants.INNEIGHBOURING);
  30.                 }
  31.             }
  32.         }
  33.         for (int i = Constants.GRID_SIZE - 1; i >= 0; i--) {
  34.             for (int j = Constants.GRID_SIZE - 1; j >= 0; j--) {
  35.                 if (maze[i][j].getRight() == Constants.NEIGHBOURING) {
  36.                     clearFence(i, j, i, j + 1, g);
  37.                 }
  38.                 if (maze[i][j].getDown() == Constants.NEIGHBOURING) {
  39.                     clearFence(i, j, i + 1, j, g);
  40.                 }
  41.             }
  42.         }
  43.         // 画左上角的入口
  44.         g.drawLine(padding, padding + 1, padding, padding + width - 1);
  45.         int last = padding + Constants.GRID_SIZE * width;
  46.         // 画右下角出口
  47.         g.drawLine(last, last - 1, last, last - width + 1);
  48.         // 画下边框
  49.         g.setColor(Color.BLACK);
  50.         g.drawLine(padding, last, last, last);
  51.         // 画右边框
  52.         g.drawLine(last, padding, last, last - width);
  53.         // 画路径
  54.         drawPath(g);
  55.     }
  56.     // 私有方法,目的是抹掉两个格子之间的边
  57.     private void clearFence(int i, int j, int fx, int fy, Graphics g) {
  58.         int sx = padding + ((j > fy ? j : fy) * width),
  59.                 sy = padding + ((i > fx ? i : fx) * width),
  60.                 dx = (i == fx ? sx : sx + width),
  61.                 dy = (i == fx ? sy + width : sy);
  62.         if (sx != dx) {
  63.             sx++;
  64.             dx--;
  65.         } else {
  66.             sy++;
  67.             dy--;
  68.         }
  69.         g.drawLine(sx, sy, dx, dy);
  70.     }
  71.     // 由格子的行数列数,得到格子中心点的像素XY坐标
  72.     private int getCenterX(Cell p) {
  73.         return padding + p.getY() * width + width / 2;
  74.     }
  75.     private int getCenterY(Cell p) {
  76.         return padding + p.getX() * width + width / 2;
  77.     }
  78.     // 由序列数得到对应的行和列
  79.     private int getRow(int num) {
  80.         return (num ) / Constants.GRID_SIZE;
  81.     }
  82.     private int getCol(int num) {
  83.         return num % Constants.GRID_SIZE;
  84.     }
  85.     private void drawPath(Graphics g) {
  86.         g.setColor(Color.red);
  87.         // 已经得到初始路径到最后路径的list,接下来对照相应的格子,再画出来就行
  88.         for (int i = 0; i < path.size() - 1; i++) {
  89.             int first = path.get(i);
  90.             int second = path.get(i + 1);
  91.             g.drawLine(getCenterX(maze[getRow(first)][getCol(first)]), getCenterY(maze[getRow(first)][getCol(first)]), getCenterX(maze[getRow(second)][getCol(second)]), getCenterY(maze[getRow(second)][getCol(second)]));
  92.         }
  93.     }

进行测试:

  1.     public static void main(String[] args) {
  2.         JPanel test2 = new Test2();
  3.         JFrame frame = new JFrame("homework02.Maze");
  4.         //下面一行自动调用paintComponent方法
  5.         frame.setContentPane(test2);
  6.         frame.setSize(Constants.WIDTH + Constants.PADDING, Constants.WIDTH + Constants.PADDING + Constants.PADDING);
  7.         frame.setLocation(Constants.LX, Constants.LY);
  8.         frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  9.         frame.setVisible(true);
  10.     }

  1. 运行结果展示

图7  测试结果

  1. 总结和收获

复习了很多数据结构和算法,包括最小优先队列,堆排序,队列,最小支撑树,用Kruskal 算法,并查集树等等,收获很大。

 

附录:
 

  1. 任务2
  1. UnionFindTree:
  1. package homework02;
  2. public class UnionFindTree {
  3.     //用树实现并查集
  4.     public Node[] set;
  5.     public UnionFindTree(Node[] set) {
  6.         this.set = set;
  7.     }
  8.     /**
  9.      * 返回包含给定元素的集合名字
  10.      *
  11.      * @param i 元素下标
  12.      * @return 以根结点下标作为集合名字
  13.      */
  14.     public int find(int i) {
  15.         //使用路劲压缩法:在查找某个元素是否属于某个集合时,将该结点到根结点路径上
  16.         // 所有结点的父指针全部改为指向根结点,这种方式可以产生极浅的树
  17.         Node current = set[i];
  18.         if (current.getParent()<0)
  19.             return i;
  20.         return current.setParent(find(current.getParent()));
  21.     }
  22.     /**
  23.      * 生成一个新的集合,该集合是i所属的集合set1和j所属的集合set2的并集
  24.      *
  25.      * @param i
  26.      * @param j
  27.      */
  28.     public void union(int i, int j) {
  29.         //使用重量平衡原则
  30.         int root1 = find(i);
  31.         int num1 = set[root1].getParent();
  32.         int root2 = find(j);
  33.         int num2 = set[root2].getParent();
  34.         if (num1 <= num2) {
  35.             set[root1].setParent(num1+num2);
  36.             set[root2].setParent(root1);
  37.             //将其中结点数少的一棵树的根结点的父结点设置为
  38.             //结点数多的一棵树的根结点
  39.         } else {
  40.             set[root2].setParent(num1+num2);
  41.             set[root1].setParent(root2);
  42.             //将其中结点数少的一棵树的根结点的父结点设置为
  43.             //结点数多的一棵树的根结点
  44.         }
  45.     }
  46.     /**
  47.      * 判断元素i和元素j是否在同一个分组中
  48.      * @param i
  49.      * @param j
  50.      * @return
  51.      */
  52.     public boolean isConnected(int i,int j){
  53.         return find(i)==find(j);
  54.     }
  55.     public void print() {
  56.         for (int i = 0; i < set.length; i++) {
  57.             System.out.print(set[i].getElement() + " ");
  58.         }
  59.     }
  60.     public static void main(String[] args) {
  61.         // 创建一组元素,共有8个元素
  62.         Node[] set = new Node[8];
  63.         for (int i = 0; i < set.length; i++) {
  64.             set[i] = new Node(i, -1); // 初始时每个元素都是一个独立的集合,父节点为-1
  65.         }
  66.         UnionFindTree unionFind = new UnionFindTree(set);
  67.         // 合并一些集合
  68.         unionFind.union(01);
  69.         unionFind.union(23);
  70.         unionFind.union(45);
  71.         unionFind.union(67);
  72.         unionFind.union(02);
  73.         unionFind.union(46);
  74.         unionFind.union(04);
  75.         // 验证元素之间的连通性
  76.         System.out.println("0 和 7 是否连通?" + unionFind.isConnected(07));
  77.         System.out.println("1 和 3 是否连通?" + unionFind.isConnected(13));
  78.         System.out.println("2 和 6 是否连通?" + unionFind.isConnected(26));
  79.         System.out.println("4 和 5 是否连通?" + unionFind.isConnected(45));
  80.         // 打印并查集中的元素
  81.         unionFind.print();
  82.     }
  83. }

  1. MinPriorityQueue:
  1. package homework02;
  2. public class MinPriorityQueue<T extends Comparable<T>> {
  3.     //最小优先队列
  4.     private static final int DEFAULT_CAPACITY = 10;//默认大小
  5.     private int currentSize;//当前堆的大小
  6.     private T[] array;//堆数组
  7.     public MinPriorityQueue() {
  8.         this.array = (T[]) new Comparable[DEFAULT_CAPACITY + 1];
  9.         currentSize = 0;
  10.     }
  11.     public MinPriorityQueue(int size) {
  12.         this.array = (T[]) new Comparable[size + 1];
  13.         currentSize = 0;
  14.     }
  15.     public MinPriorityQueue(T[] array) {
  16.         this.array = (T[]) new Comparable[array.length + 1];
  17.         for (int i = 0; i < array.length; i++) {
  18.             this.array[i + 1] = array[i];
  19.         }//从1开始算
  20.         currentSize = array.length;
  21.         buildHeap();
  22.     }
  23.     /**
  24.      * 往堆中插入元素
  25.      *
  26.      * @param element 元素值
  27.      */
  28.     public void insert(T element) {
  29.         try {
  30.             if (isFull()) {
  31.                 throw new Exception("Array is full!");
  32.             }
  33.             int temp = ++currentSize;
  34.             array[temp] = element;//将element放在数组最后
  35.             while ((temp != 1) && (array[temp].compareTo(array[getParent(temp)]) < 0)) {
  36.                 swap(array, temp, getParent(temp));
  37.                 temp = getParent(temp);//如果比父结点的值小则于其交换
  38.             }//注意根结点的下标为1,不是0
  39.         } catch (Exception e) {
  40.             e.printStackTrace();
  41.         }
  42.     }
  43.     public T findMin() {
  44.         return array[1];
  45.     }
  46.     /**
  47.      * 删除堆顶元素
  48.      */
  49.     public void deleteMin() {
  50.         try {
  51.             if (isEmpty()) {
  52.                 throw new Exception("Array is empty!");
  53.             } else {
  54.                 swap(array, 1, currentSize--);//将堆顶元素放到最后,同时删除
  55.                 if (currentSize != 0) siftDown(1);
  56.             }
  57.         } catch (Exception e) {
  58.             e.printStackTrace();
  59.         }
  60.     }
  61.     /**
  62.      * 大的值下沉
  63.      *
  64.      * @param pos 当前位置
  65.      */
  66.     private void siftDown(int pos) {
  67.         try {
  68.             if (pos < 0 || pos > currentSize) {
  69.                 throw new Exception("Illegal position!");
  70.             }
  71.             while (!isLeaf(pos)) {
  72.                 int j = getLeft(pos);
  73.                 if ((j < currentSize) && (array[j].compareTo(array[j + 1])) > 0) j++;
  74.                 //跟子树中最小的值交换
  75.                 if (array[pos].compareTo(array[j]) < 0return;
  76.                 //当前值已经比子树中的值都小,则返回
  77.                 swap(array, pos, j);//交换
  78.                 pos = j;
  79.             }
  80.         } catch (Exception e) {
  81.             e.printStackTrace();
  82.         }
  83.     }
  84.     public void print() {
  85.         for (int i = 1; i <= currentSize; i++) {
  86.             System.out.print(array[i] + " ");
  87.         }
  88.     }
  89.     public void heapSort() {
  90.         while (currentSize != 0) {
  91.             System.out.print(findMin() + " ");
  92.             deleteMin();
  93.         }
  94.     }
  95.     public boolean isEmpty() {
  96.         return currentSize == 0;
  97.     }
  98.     public boolean isFull() {
  99.         return currentSize == array.length - 1;
  100.     }
  101.     private boolean isLeaf(int i) {
  102.         return i > currentSize / 2;
  103.     }
  104.     private void buildHeap() {
  105.         for (int i = currentSize / 2; i > 0; i--) {
  106.             siftDown(i);//对每个非叶子结点进行下沉操作
  107.             //从右到左,从下到上
  108.         }
  109.     }
  110.     private int getLeft(int i) {
  111.         return 2 * i;
  112.     }
  113.     private int getRight(int i) {
  114.         return 2 * i + 1;
  115.     }
  116.     private int getParent(int i) {
  117.         return i / 2;
  118.     }
  119.     private void swap(T[] array, int x, int y) {
  120.         T temp = array[y];
  121.         array[y] = array[x];
  122.         array[x] = temp;
  123.     }
  124.     public static void main(String[] args) throws Exception {
  125.         Edge edge1 = new Edge(121);
  126.         Edge edge2 = new Edge(122);
  127.         Edge edge3 = new Edge(126);
  128.         Edge edge4 = new Edge(124);
  129.         Edge edge5 = new Edge(125);
  130.         Edge edge6 = new Edge(127);
  131.         Edge edge7 = new Edge(123);
  132. //        Integer[] elements = {1, 2, 6, 4, 5, 7, 3};
  133. //        MinPriorityQueue<Integer> test = new MinPriorityQueue<Integer>(elements);
  134.         Edge[] edges = {edge1, edge2, edge3, edge4, edge5, edge6, edge7};
  135.         MinPriorityQueue<Edge> test = new MinPriorityQueue<Edge>(edges);
  136.         test.print();
  137.         System.out.println();
  138.         test.heapSort();
  139.         System.out.println();
  140.         test.print();
  141.     }
  142. }

  1. GraphM2:
  1. package homework02;
  2. import homework01.GraphM1;
  3. import java.util.ArrayList;
  4. import java.util.Arrays;
  5. public class GraphM2 extends GraphM1 {
  6.     //用相邻矩阵实现无向图
  7.     private ArrayList<String> V;//顶点
  8.     private int E;//边数
  9.     private int[][] matrix;//相邻矩阵
  10.     private int[] mark;//判定一个结点是否被访问过
  11.     public static final int VISITED = -2;//已经访问
  12.     public static final int UNVISITED = -3;//未访问
  13.     public static final int UNCONNECTED = 1000;//未访问
  14.     public GraphM2() {
  15.     }
  16.     /**
  17.      * 初始化无向图
  18.      *
  19.      * @param n 结点数
  20.      */
  21.     public GraphM2(int n) {
  22.         matrix = new int[n][n];
  23.         mark = new int[n];
  24.         for (int i = 0; i < n; i++) {
  25.             mark[i] = UNVISITED;
  26.             for (int j = 0; j < n; j++) {
  27.                 matrix[i][j] = UNCONNECTED;
  28.             }
  29.         }
  30.         V = new ArrayList<String>(n);
  31.     }
  32.     public int[][] getMatrix() {
  33.         return matrix;
  34.     }
  35.     /**
  36.      * 添加结点
  37.      *
  38.      * @param vertex 结点值
  39.      */
  40.     public void insertVertex(String vertex) {
  41.         V.add(vertex);
  42.     }
  43.     public void insertVertex(Integer vertex) {
  44.         V.add(String.valueOf(vertex));
  45.     }
  46.     /**
  47.      * 插入边
  48.      *
  49.      * @param v1     起点下标
  50.      * @param v2     终点下标
  51.      * @param weight 权值,这里0表示不相邻,1表示相邻
  52.      */
  53.     public void insertEdge(int v1, int v2, int weight) {
  54.         matrix[v1][v2] = weight;
  55.         matrix[v2][v1] = weight;
  56.         E++;//边数+1
  57.     }
  58.     /**
  59.      * 获得v1->v2的权值
  60.      *
  61.      * @param v1
  62.      * @param v2
  63.      * @return
  64.      */
  65.     public int getWeight(int v1, int v2) {
  66.         return matrix[v1][v2];
  67.     }
  68.     /**
  69.      * 获得边的数量
  70.      *
  71.      * @return
  72.      */
  73.     public int getEdgeNum() {
  74.         return E;
  75.     }
  76.     /**
  77.      * 获得结点数
  78.      *
  79.      * @return
  80.      */
  81.     public int getVertexNum() {
  82.         return V.size();
  83.     }
  84.     public String getVertexValue(int index) {
  85.         return V.get(index);
  86.     }
  87.     /**
  88.      * 获得某个结点的第一个邻接点下标
  89.      *
  90.      * @param v 某个顶点下标
  91.      * @return 第一个邻接点下标
  92.      */
  93.     public int getFirst(int v) {
  94.         for (int i = 0; i < V.size(); i++) {
  95.             if (matrix[v][i] > 0) {
  96.                 return i;
  97.             }
  98.         }
  99.         return -1;
  100.     }
  101.     /**
  102.      * @param v1 某个顶点坐标
  103.      * @param v2 前一个邻接结点坐标
  104.      * @return 下一个邻接结点坐标
  105.      */
  106.     public int getNext(int v1, int v2) {
  107.         for (int i = v2 + 1; i < V.size(); i++) {
  108.             if (matrix[v1][i] > 0) {
  109.                 return i;
  110.             }
  111.         }
  112.         return -1;
  113.     }
  114.     public boolean isEdge(int v1, int v2) {
  115.         return isLegalIndex(v1) && isLegalIndex(v2) && matrix[v1][v2] > 0;
  116.     }
  117.     public boolean isLegalIndex(int v) {
  118.         return v >= 0 && v < V.size();
  119.     }
  120.     public void DFS() {
  121.         for (int i = 0; i < getVertexNum(); i++) {
  122.             setMark(i, UNVISITED);
  123.         }//初始化所有顶点
  124.         for (int i = 0; i < getVertexNum(); i++) {
  125.             if (getMark(i) == UNVISITED) {
  126.                 DFSHelp(i);
  127.             }
  128.         }
  129.     }
  130.     public void DFSHelp(int v) {
  131.         System.out.print(V.get(v) + " ");
  132.         setMark(v, VISITED);
  133.         for (int edge = getFirst(v); isEdge(v, edge); edge = getNext(v, edge)) {
  134.             if (getMark(edge) == UNVISITED) {
  135.                 DFSHelp(edge);
  136.             }
  137.         }
  138.     }
  139.     public void BFS() {
  140.         for (int i = 0; i < getVertexNum(); i++) {
  141.             setMark(i, UNVISITED);
  142.         }//初始化所有顶点
  143.         for (int i = 0; i < getVertexNum(); i++) {
  144.             if (getMark(i) == UNVISITED) {
  145.                 BFSHelp(i);
  146.             }
  147.         }
  148.     }
  149.     public void BFSHelp(int v) {
  150.         LQueue<Integer> queue = new LQueue<>();
  151.         queue.enqueue(v);
  152.         setMark(v, VISITED);
  153.         while (!queue.isEmpty()) {
  154.             int temp = queue.dequeue();
  155.             System.out.print(V.get(temp) + " ");
  156.             for (int index = getFirst(temp); isEdge(temp, index); index = getNext(temp, index)) {
  157.                 if (getMark(index) == UNVISITED) {
  158.                     queue.enqueue(index);
  159.                     setMark(index, VISITED);
  160.                 }
  161.             }
  162.         }
  163.     }
  164.     public void setMark(int v, int val) {
  165.         mark[v] = val;
  166.     }
  167.     public int getMark(int v) {
  168.         return mark[v];
  169.     }
  170.     //打印邻接矩阵
  171.     public void print() {
  172.         for (int[] edge : matrix) {
  173.             System.out.println(Arrays.toString(edge));
  174.         }
  175.     }
  176.     public static void main(String[] args) {
  177.         //定义图的所有顶点
  178.         String[] vertexs = {"A""B""C""D""E""F"};
  179.         //创建图
  180.         GraphM2 graph = new GraphM2(vertexs.length);
  181.         //添加顶点到图中
  182.         for (String vertex : vertexs) {
  183.             graph.insertVertex(vertex);
  184.         }
  185.         //添加边到图中
  186.         graph.insertEdge(021);
  187.         graph.insertEdge(041);
  188.         graph.insertEdge(121);
  189.         graph.insertEdge(151);
  190.         graph.insertEdge(231);
  191.         graph.insertEdge(251);
  192.         graph.insertEdge(351);
  193.         graph.insertEdge(451);
  194.         graph.print();
  195.         graph.DFS();
  196.         System.out.println();
  197.         graph.BFS();
  198.     }
  199. }

  1. LQueue:
  1. package homework02;
  2. import java.util.NoSuchElementException;
  3. public class LQueue<T> {
  4.     private Node<T> front;
  5.     private Node<T> rear;
  6.     private class Node<T> {
  7.         private T data;
  8.         private Node<T> next;
  9.         public Node(T data) {
  10.             this.data = data;
  11.             this.next = null;
  12.         }
  13.     }
  14.     public LQueue() {
  15.         front = null;
  16.         rear = null;
  17.     }
  18.     public boolean isEmpty() {
  19.         return front == null;
  20.     }
  21.     public void enqueue(T item) {
  22.         Node<T> newNode = new Node<>(item);
  23.         if (isEmpty()) {
  24.             front = newNode;
  25.             rear = newNode;
  26.         } else {
  27.             rear.next = newNode;
  28.             rear = newNode;
  29.         }
  30.     }
  31.     public T dequeue() {
  32.         if (isEmpty()) {
  33.             throw new NoSuchElementException("Queue is empty");
  34.         }
  35.         T data = front.data;
  36.         front = front.next;
  37.         if (front == null) {
  38.             rear = null;
  39.         }
  40.         return data;
  41.     }
  42.     public T peek() {
  43.         if (isEmpty()) {
  44.             throw new NoSuchElementException("Queue is empty");
  45.         }
  46.         return front.data;
  47.     }
  48. }

  1. Node:
  1. package homework02;
  2. public class Node {
  3.     private int parent;//父结点下标
  4.     private Object element;//存储元素内容
  5.     public Node() {
  6.         this(null, -1);
  7.     }
  8.     public Node(Object element) {
  9.         this(element, -1);
  10.     }
  11.     public Node(Object element, int parent) {
  12.         this.element = element;
  13.         this.parent = parent;
  14.     }
  15.     public int getParent() {
  16.         return parent;
  17.     }
  18.     public int setParent(int parent) {
  19.         return this.parent = parent;
  20.     }
  21.     public Object getElement() {
  22.         return element;
  23.     }
  24.     public void setElement(Object element) {
  25.         this.element = element;
  26.     }
  27. }

  1. Edge:
  1. package homework02;
  2. import org.jetbrains.annotations.NotNull;
  3. //这个类的目的就是为了Kruskal算法中的优先队列创建的
  4. public class Edge implements Comparable<Edge> {
  5.     private int v1;//起点
  6.     private int v2;//终点
  7.     private int weight;//权重
  8.     public Edge(int v1, int v2) {
  9.         this.v1 = v1;
  10.         this.v2 = v2;
  11.     }
  12.     public Edge(int v1, int v2, int weight) {
  13.         this.v1 = v1;
  14.         this.v2 = v2;
  15.         this.weight = weight;
  16.     }
  17.     public int getV1() {
  18.         return v1;
  19.     }
  20.     public void setV1(int v1) {
  21.         this.v1 = v1;
  22.     }
  23.     public int getV2() {
  24.         return v2;
  25.     }
  26.     public void setV2(int v2) {
  27.         this.v2 = v2;
  28.     }
  29.     public int getWeight() {
  30.         return weight;
  31.     }
  32.     public void setWeight(int weight) {
  33.         this.weight = weight;
  34.     }
  35.     @Override
  36.     public int compareTo(@NotNull Edge edge) {
  37.         return Integer.compare(this.weight, edge.weight);
  38.     }
  39.     @Override
  40.     public String toString() {
  41.         return "Edge{" +
  42.                 v1 +
  43.                 "->" + v2 +
  44.                 " weight=" + weight +
  45.                 '}';
  46.     }
  47. }

  1. Kruskal:
  1. package homework02;
  2. import java.util.ArrayList;
  3. import homework01.*;
  4. public class Kruskal {
  5.     private ArrayList<Edge> MST;//最小生成树中的所有边
  6.     private UnionFindTree ufTree;//并查集树,放索引
  7.     private MinPriorityQueue<Edge> allEdges;//图中所有的边
  8.     private GraphM2 originalGraph;//用相邻矩阵实现的原图
  9.     private int vertexNum;//结点数
  10.     private GraphM2 newGraph;//用相邻矩阵实现的新图
  11.     public Kruskal(GraphM2 originalGraph) {
  12.         this.originalGraph = originalGraph;
  13.         this.vertexNum = originalGraph.getVertexNum();
  14.     }
  15.     public void generateMST() {
  16.         this.MST = new ArrayList<Edge>();//初始化MST
  17.         Node[] nodes = new Node[vertexNum];
  18.         for (int i = 0; i < vertexNum; i++) {
  19.             nodes[i] = new Node(i);
  20.         }
  21.         this.ufTree = new UnionFindTree(nodes);//初始化并查集
  22.         this.allEdges = new MinPriorityQueue<>(originalGraph.getEdgeNum());//初始化优先队列
  23.         //把图中的边放到储存边的最小优先队列中,目的是为了快速检索
  24.         for (int i = 0; i < vertexNum; i++) {
  25.             for (int j = i + 1; j < vertexNum; j++) {
  26.                 if (originalGraph.getWeight(i, j) != GraphM2.UNCONNECTED) {
  27.                     allEdges.insert(new Edge(i, j, originalGraph.getWeight(i, j)));
  28.                 }
  29.             }
  30.         }
  31.         while (!allEdges.isEmpty() && MST.size() < vertexNum - 1) {
  32.             Edge e = allEdges.findMin();
  33.             allEdges.deleteMin();
  34.             int v1 = e.getV1();
  35.             int v2 = e.getV2();
  36.             //判断v1和v2是否已经连通
  37.             if (ufTree.isConnected(v1, v2)) continue;
  38.             ufTree.union(v1, v2);//不连通则连接
  39.             MST.add(e);//将边并入MST
  40.         }
  41.     }
  42.     public ArrayList<Edge> getMST() {
  43.         return MST;
  44.     }
  45.     // 由序列数得到对应的行和列
  46.     private int getRow(int num) {
  47.         return num / Constants.GRID_SIZE;
  48.     }
  49.     private int getCol(int num) {
  50.         return num % Constants.GRID_SIZE;
  51.     }
  52.     public int[][] getMatrix(){
  53.         newGraph = new GraphM2(Constants.GRID_SIZE * Constants.GRID_SIZE);
  54.         for (Edge edge : MST){
  55.             int cell1 = Integer.parseInt(originalGraph.getVertexValue(edge.getV1()));
  56.             int cell2 = Integer.parseInt(originalGraph.getVertexValue(edge.getV2()));
  57.             newGraph.insertEdge(cell1,cell2,1);
  58.         }
  59.         return newGraph.getMatrix();
  60.     }
  61.     public int[][] getDown(){
  62.         //把down所有数都写为1000
  63.         int[][] down = new int[Constants.GRID_SIZE][Constants.GRID_SIZE];
  64.         for (int i = 0; i < Constants.GRID_SIZE; i++) {
  65.             for (int j = 0; j < Constants.GRID_SIZE; j++) {
  66.                 down[i][j] = Constants.INNEIGHBOURING;
  67.             }
  68.         }
  69.         for (Edge edge : MST){
  70.             int cell1 = Integer.parseInt(originalGraph.getVertexValue(edge.getV1()));
  71.             int cell2 = Integer.parseInt(originalGraph.getVertexValue(edge.getV2()));
  72.             //保证cell1小于cell2
  73.             if(cell1 > cell2){
  74.                 int temp = cell1;
  75.                 cell1 = cell2;
  76.                 cell2 = temp;
  77.             }
  78.             //保证同一列,将上边cell1格子的写为1
  79.             if(getCol(cell1) == getCol(cell2)){
  80.                 down[getRow(cell1)][getCol(cell1)] = 1;
  81.             }
  82.         }
  83.         return down;
  84.     }
  85.     public int[][] getRight(){
  86.         //把right所有数都写为1000
  87.         int[][] right = new int[Constants.GRID_SIZE][Constants.GRID_SIZE];
  88.         for (int i = 0; i < Constants.GRID_SIZE; i++) {
  89.             for (int j = 0; j < Constants.GRID_SIZE; j++) {
  90.                 right[i][j] = Constants.INNEIGHBOURING;
  91.             }
  92.         }
  93.         for (Edge edge : MST){
  94.             int cell1 = Integer.parseInt(originalGraph.getVertexValue(edge.getV1()));
  95.             int cell2 = Integer.parseInt(originalGraph.getVertexValue(edge.getV2()));
  96.             //保证cell1小于cell2
  97.             if(cell1 > cell2){
  98.                 int temp = cell1;
  99.                 cell1 = cell2;
  100.                 cell2 = temp;
  101.             }
  102.             //保证同一行,将左边cell1格子的写为1
  103.             if(getRow(cell1) == getRow(cell2)){
  104.                 right[getRow(cell1)][getCol(cell1)] = 1;
  105.             }
  106.         }
  107.         return right;
  108.     }
  109.     /**
  110.      * 打印MST的所有边
  111.      */
  112.     public void printMST() {
  113.         for (Edge edge : MST) {
  114.             System.out.println(originalGraph.getVertexValue(edge.getV1()) + "-"
  115.                     + originalGraph.getWeight(edge.getV1(), edge.getV2())
  116.                     + "->" + originalGraph.getVertexValue(edge.getV2()));
  117.         }
  118.     }
  119.     public static void main(String[] args) {
  120.         String[] vertexs = {"A""B""C""D""E""F"};
  121.         //创建图
  122.         GraphM2 graph = new GraphM2(vertexs.length);
  123.         //添加顶点到图中
  124.         for (String vertex : vertexs) {
  125.             graph.insertVertex(vertex);
  126.         }
  127.         //添加边到图中
  128.         graph.insertEdge(027);
  129.         graph.insertEdge(049);
  130.         graph.insertEdge(125);
  131.         graph.insertEdge(156);
  132.         graph.insertEdge(231);
  133.         graph.insertEdge(252);
  134.         graph.insertEdge(352);
  135.         graph.insertEdge(451);
  136.         Kruskal test = new Kruskal(graph);
  137.         test.generateMST();
  138.         test.printMST();
  139.     }
  140. }

  1. Test2:
  1. package homework02;
  2. import homework01.*;
  3. import javax.swing.*;
  4. import java.awt.*;
  5. import java.util.List;
  6. import java.util.Random;
  7. public class Test2 extends JPanel {
  8.     public GraphM2 graph;
  9.     public int padding = Constants.PADDING;
  10.     public int width = (Constants.WIDTH - Constants.PADDING - Constants.PADDING) / Constants.GRID_SIZE;
  11.     public Cell[][] maze;
  12.     public int [][] down;
  13.     public int [][] right;
  14.     public int [][] matrix;
  15.     public List<Integer> path;
  16.     public Puzzle puzzle = new Puzzle();
  17.     public Test2() {
  18.         maze = new Cell[Constants.GRID_SIZE][Constants.GRID_SIZE];
  19.         helpTest();
  20.     }
  21.     public void helpTest(){
  22.         //创建抽象的全连接的图
  23.         //定义图的所有顶点
  24.         Integer[] vertexs = new Integer[Constants.GRID_SIZE * Constants.GRID_SIZE];
  25.         for (int i = 0; i < vertexs.length; i++) {
  26.             vertexs[i] = i;
  27.         }
  28.         //创建图并把顶点放在图中
  29.         graph = new GraphM2(vertexs.length);
  30.         for (Integer vertex : vertexs) {
  31.             graph.insertVertex(vertex);
  32.         }
  33.         //添加随机边到图中
  34.         Random r = new Random();
  35.         for (int i = 0; i < Constants.GRID_SIZE - 1; i++) {
  36.             for (int j = 0; j < Constants.GRID_SIZE; j++) {
  37.                 graph.insertEdge(i * Constants.GRID_SIZE + j, (i + 1) * Constants.GRID_SIZE + j, r.nextInt(300));
  38.             }
  39.         }
  40.         for (int i = 0; i < Constants.GRID_SIZE; i++) {
  41.             for (int j = 0; j < Constants.GRID_SIZE - 1; j++) {
  42.                 graph.insertEdge(i * Constants.GRID_SIZE + j, i * Constants.GRID_SIZE + j + 1, r.nextInt(300));
  43.             }
  44.         }
  45.         //调用Kruskal 算法对步骤 1 中的图生成最小支撑树
  46.         Kruskal kruskal = new Kruskal(graph);
  47.         kruskal.generateMST();
  48.         //得到down和right数组
  49.         down = kruskal.getDown();
  50.         right = kruskal.getRight();
  51.         matrix = kruskal.getMatrix();
  52.         path = puzzle.getPath(matrix);
  53.     }
  54.     //这是个自动调用的方法,必须设置为public
  55.     public void paintComponent(Graphics g) {
  56.         super.paintComponent(g);
  57.         // 画NUM*NUM条黑线
  58.         for (int i = 0; i <= Constants.GRID_SIZE; i++) {
  59.             g.drawLine(padding + i * width, padding, padding + i * width,
  60.                     padding + Constants.GRID_SIZE * width);
  61.         }
  62.         for (int j = 0; j <= Constants.GRID_SIZE; j++) {
  63.             g.drawLine(padding, padding + j * width, padding + Constants.GRID_SIZE * width,
  64.                     padding + j * width);
  65.         }
  66.         // 使用背景色,在有路径的格子之间画边,把墙抹掉
  67.         g.setColor(this.getBackground());
  68.         //根据Puzzle中的数组进行抹边
  69.         for (int i = 0; i < Constants.GRID_SIZE; i++) {
  70.             for (int j = 0; j < Constants.GRID_SIZE; j++) {
  71.                 maze[i][j] = new Cell(i, j);
  72.                 if (down[i][j] == Constants.NEIGHBOURING) {
  73.                     maze[i][j].setDown(Constants.NEIGHBOURING);
  74.                 }
  75.                 if (down[i][j] == Constants.INNEIGHBOURING) {
  76.                     maze[i][j].setDown(Constants.INNEIGHBOURING);
  77.                 }
  78.                 if (right[i][j] == Constants.NEIGHBOURING) {
  79.                     maze[i][j].setRight(Constants.NEIGHBOURING);
  80.                 }
  81.                 if (right[i][j] == Constants.INNEIGHBOURING) {
  82.                     maze[i][j].setRight(Constants.INNEIGHBOURING);
  83.                 }
  84.             }
  85.         }
  86.         for (int i = Constants.GRID_SIZE - 1; i >= 0; i--) {
  87.             for (int j = Constants.GRID_SIZE - 1; j >= 0; j--) {
  88.                 if (maze[i][j].getRight() == Constants.NEIGHBOURING) {
  89.                     clearFence(i, j, i, j + 1, g);
  90.                 }
  91.                 if (maze[i][j].getDown() == Constants.NEIGHBOURING) {
  92.                     clearFence(i, j, i + 1, j, g);
  93.                 }
  94.             }
  95.         }
  96.         // 画左上角的入口
  97.         g.drawLine(padding, padding + 1, padding, padding + width - 1);
  98.         int last = padding + Constants.GRID_SIZE * width;
  99.         // 画右下角出口
  100.         g.drawLine(last, last - 1, last, last - width + 1);
  101.         // 画下边框
  102.         g.setColor(Color.BLACK);
  103.         g.drawLine(padding, last, last, last);
  104.         // 画右边框
  105.         g.drawLine(last, padding, last, last - width);
  106.         // 画路径
  107.         drawPath(g);
  108.     }
  109.     // 私有方法,目的是抹掉两个格子之间的边
  110.     private void clearFence(int i, int j, int fx, int fy, Graphics g) {
  111.         int sx = padding + ((j > fy ? j : fy) * width),
  112.                 sy = padding + ((i > fx ? i : fx) * width),
  113.                 dx = (i == fx ? sx : sx + width),
  114.                 dy = (i == fx ? sy + width : sy);
  115.         if (sx != dx) {
  116.             sx++;
  117.             dx--;
  118.         } else {
  119.             sy++;
  120.             dy--;
  121.         }
  122.         g.drawLine(sx, sy, dx, dy);
  123.     }
  124.     // 由格子的行数列数,得到格子中心点的像素XY坐标
  125.     private int getCenterX(Cell p) {
  126.         return padding + p.getY() * width + width / 2;
  127.     }
  128.     private int getCenterY(Cell p) {
  129.         return padding + p.getX() * width + width / 2;
  130.     }
  131.     // 由序列数得到对应的行和列
  132.     private int getRow(int num) {
  133.         return (num ) / Constants.GRID_SIZE;
  134.     }
  135.     private int getCol(int num) {
  136.         return num % Constants.GRID_SIZE;
  137.     }
  138.     private void drawPath(Graphics g) {
  139.         g.setColor(Color.red);
  140.         // 已经得到初始路径到最后路径的list,接下来对照相应的格子,再画出来就行
  141.         for (int i = 0; i < path.size() - 1; i++) {
  142.             int first = path.get(i);
  143.             int second = path.get(i + 1);
  144.             g.drawLine(getCenterX(maze[getRow(first)][getCol(first)]), getCenterY(maze[getRow(first)][getCol(first)]), getCenterX(maze[getRow(second)][getCol(second)]), getCenterY(maze[getRow(second)][getCol(second)]));
  145.         }
  146.     }
  147.     public static void main(String[] args) {
  148.         JPanel test2 = new Test2();
  149.         JFrame frame = new JFrame("homework02.Maze");
  150.         //下面一行自动调用paintComponent方法
  151.         frame.setContentPane(test2);
  152.         frame.setSize(Constants.WIDTH + Constants.PADDING, Constants.WIDTH + Constants.PADDING + Constants.PADDING);
  153.         frame.setLocation(Constants.LX, Constants.LY);
  154.         frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  155.         frame.setVisible(true);
  156.     }
  157. }

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值