1. 程式人生 > >EasyLearn--JAVA實現32個經典演算法設計(一):A*演算法

EasyLearn--JAVA實現32個經典演算法設計(一):A*演算法

A*演算法是一種靜態路網中求解最短路徑最有效的直接搜尋方法,其中經典的圖形有網格影象如下:

根據這個影象來對演算法進行說明主要思想:該演算法又稱為啟發式演算法,啟發之處在於公式F=G+H,F為最終結果值,G為所行走的步數,H就是預估值,其中H可根據不同的策略採用不同的規則定義,此處使用無視障礙物的最短距離最為預估剩餘步數,依次算出起始格周圍的最終結果F值,然後取最小值最為下一步要前進的網格,最終找到終點格。

下面具體看看用程式碼如何實現。

第一步,定義網格的含義實體BEAN

import java.io.Serializable;

/**
 * 節點類,座標上的點,包含資訊如下
 * 1.節點座標(x和y座標)、節點的值--節點類別(A為起點,B為終點,“o”表示可通節點,“%”表示牆壁,“#”代表障礙物,“*”代表演算法計算後的路徑)
 * 2.節點是否可到達(是否為障礙物)、父節點ANode
 * 3.A*演算法的核心概念F=G+H,從起始節點A開始,F當前節點走向終點B的期望結果值,G為當前節點走向起始節點A所需消耗的步數,假設移動步數只能上下左右移動
 */
public class ANode implements Serializable {

    private Integer x;
    private Integer y;
    private NodeEnum value;
    private Double FValue;
    private Double GValue;
    private Double HValue;
    private Boolean reachable;
    private ANode parentNode;

    public ANode() {
        super();
    }

    public ANode(Integer x, Integer y, NodeEnum value, Boolean reachable) {
        super();
        this.x = x;
        this.y = y;
        this.value = value;
        this.reachable = reachable;
    }

    public Integer getX() {
        return x;
    }

    public void setX(Integer x) {
        this.x = x;
    }

    public Integer getY() {
        return y;
    }

    public void setY(Integer y) {
        this.y = y;
    }

    public NodeEnum getValue() {
        return value;
    }

    public void setValue(NodeEnum value) {
        this.value = value;
    }

    public Double getFValue() {
        return FValue;
    }

    public void setFValue(Double FValue) {
        this.FValue = FValue;
    }

    public Double getGValue() {
        return GValue;
    }

    public void setGValue(Double GValue) {
        this.GValue = GValue;
    }

    public Double getHValue() {
        return HValue;
    }

    public void setHValue(Double HValue) {
        this.HValue = HValue;
    }

    public Boolean getReachable() {
        return reachable;
    }

    public void setReachable(Boolean reachable) {
        this.reachable = reachable;
    }

    public ANode getParentNode() {
        return parentNode;
    }

    public void setParentNode(ANode parentNode) {
        this.parentNode = parentNode;
    }
}

第二步,定義網格影象 AMAP類

import java.io.Serializable;

/**
 * A*各個節點的ANode陣列集合,
 * 1.定義起始節點
 * 2.初始化各個節點(包含節點的座標、類別等資訊)
 * 3.展示輸出所有節點資訊
 */
public class AMap implements Serializable {
    public static final int weight = 7;
    public static final int height = 7;

    private ANode[][] nodes;
    private ANode startNode;
    private ANode endNode;

    public AMap() {
        nodes = new ANode[weight][height];
        // 初始化節點資訊 全部設定為可通節點
        for (int w = 0; w < weight; w++) {
            for (int h = 0; h < height; h++) {
                nodes[w][h] = new ANode(w, h, NodeEnum.ONODE, true);
            }
        }
        // 初始化weight牆壁節點
        for (int d = 0; d < weight; d++) {
            nodes[d][0].setValue(NodeEnum.BNODE);
            nodes[d][0].setReachable(false);
            nodes[d][height-1].setValue(NodeEnum.BNODE);
            nodes[d][height-1].setReachable(false);
        }
        // 初始化height牆壁節點
        for (int d = 0; d < height; d++) {
            nodes[0][d].setValue(NodeEnum.BNODE);
            nodes[0][d].setReachable(false);
            nodes[weight-1][d].setValue(NodeEnum.BNODE);
            nodes[weight-1][d].setReachable(false);
        }
        // 動態變動 初始化 起始節點
        nodes[3][1].setValue(NodeEnum.STARTNODE);
        startNode = nodes[3][1];
        nodes[3][5].setValue(NodeEnum.ENDNODE);
        endNode = nodes[3][5];
        // 動態變動 初始化 障礙節點
        for (int k = 1; k <= 3; k++) {
            nodes[k + 1][3].setValue(NodeEnum.JNODE);
            nodes[k + 1][3].setReachable(false);
        }
        showMap();
    }

    // 輸出列印各個節點資訊
    public void showMap() {
        System.out.println("運算前節點資訊如下:");
        for (int i = 0; i < height; i++) {
            for (int j = 0; j < weight; j++) {
                System.out.print(nodes[j][i].getValue().getValue() + " ");
            }
            System.out.println();
        }
    }

    public ANode[][] getNodes() {
        return nodes;
    }

    public void setNodes(ANode[][] nodes) {
        this.nodes = nodes;
    }

    public ANode getStartNode() {
        return startNode;
    }

    public void setStartNode(ANode startNode) {
        this.startNode = startNode;
    }

    public ANode getEndNode() {
        return endNode;
    }

    public void setEndNode(ANode endNode) {
        this.endNode = endNode;
    }
}

第三步,具體實現A*演算法的解

import java.util.ArrayList;
import java.util.Collections;

/**
 * AStar類是用於A*的運算過程,其中包含:
 * 1.移動步驟時需儲存的“開啟列表”和“關閉列表”
 * 2.設定各節點的F、G、H的值,結合父節點計算出該結果 定義每一步的能量值為10
 * 3.選中節點後將子節點新增到“開啟列表”
 * 4.將已走過節點新增到“關閉列表”
 * 5.根據A*演算法規則一次計算選出最優節點路徑
 * 6.將標記出最佳路徑的結果進行展示
 */
public class AStar {
    // 每一步所需消耗能量值
    private final double energyValue = 10;
    // 每一步可行動步數
    private final int step = 1;
    private final ArrayList<ANode> open = new ArrayList<>();
    private final ArrayList<ANode> close = new ArrayList<>();

    /**
     * 計算當前節點 H 的值
     * H值作為理想狀態下到達終點的預估值,可根據不同情況設定不同的演算法規則
     * 計算規則  為當前節點X和Y座標的差值之和再乘以能量值 不考慮障礙物是否存在
     *
     * @param currentNode
     * @param endNode
     * @return
     */
    public Double getHValue(ANode currentNode, ANode endNode){
        return (Math.abs(currentNode.getX()-endNode.getX()) + Math.abs(currentNode.getY()-endNode.getY())) * energyValue;
    }

    /**
     * 計算出當前節點 G 的值
     * 計算規則為如果沒有父節點說明是開始節點即G為0,如果不為空則獲取父節點的G+1*能量值
     * 每次移動位置只能移動往上下左右移動step的步數
     *
     * @param currentNode
     * @return
     */
    public Double getGValue(ANode currentNode){
        if (currentNode.getParentNode() == null){
            return 0.0;
        }
        if (currentNode.getParentNode().getGValue() == null){
            currentNode.getParentNode().setGValue(0.0);
        }
        return  currentNode.getParentNode().getGValue() + step*energyValue;
    }

    /**
     * 獲取F值 : G + H
     * @param currentNode
     * @return
     */
    public Double getFValue(ANode currentNode){
        return currentNode.getGValue() + currentNode.getHValue();
    }

    /**
     * 該函式的作用是 將當前節點周圍的節點新增到“開啟列表”中
     * 新增規則為:當前節點的上下左右為可達節點,對角的節點為不可達
     * 設定上下左右節點的父節點為當前節點,並設定F、G、H的值
     *
     * @param node
     * @param map
     */
    public void isOpen(ANode node ,AMap map){
        int x = node.getX();
        int y = node.getY();
        // 設定上下左右節點的資訊,並且新增到開啟列表中
        for (int i = 0; i < 2; i++) {
            int x1 = i == 0 ? x -1 : x + 1;
            int y1 = i == 0 ? y -1 : y + 1;
            setNodeInfo(node, map, x1, y);
            setNodeInfo(node, map, x, y1);
        }
    }

    /**
     * 設定周圍節點的節點資訊並新增到“開啟列表” 包含父節點、F G H的值,
     * 該節點為可達節點且不在“開啟列表”中
     * @param node
     * @param map
     * @param x
     * @param y
     */
    private void setNodeInfo(ANode node, AMap map, int x, int y) {
        if (map.getNodes()[x][y].getReachable() && !open.contains(map.getNodes()[x][y])){
            // 設定父節點
            map.getNodes()[x][y].setParentNode(node);
            // 設定 F G H 的值
            map.getNodes()[x][y].setGValue(getGValue(map.getNodes()[x][y]));
            map.getNodes()[x][y].setHValue(getHValue(map.getNodes()[x][y], map.getEndNode()));
            map.getNodes()[x][y].setFValue(getFValue(map.getNodes()[x][y]));
        }
    }

    /**
     * 將該節點新增到“關閉列表”
     * 新增規則:首先判斷 開啟列表 是否存在該節點,存在則設定該節點為不可達和節點狀態,
     * 同時在開啟列表中刪除該節點並新增到關閉列表
     * @param node
     */
    public void isClose(ANode node){
        if (open.contains(node)){
            node.setReachable(false);
            open.remove(node);
        }
    }

    /**
     * 使用Collections.sort的Comparator比較器將“開啟列表”中的節點按F值從小到大排序
     */
    public void sortOpenList(){
        Collections.sort(open, (n1, n2) -> {
            if (n1.getFValue() > n2.getFValue() || n1.getGValue() < n2.getGValue()){
                return  1;
            }
            return -1;
        });
    }

    /**
     * 從起始節點開始搜尋,每次步進為1耗能為10,根據F值來追尋下一個節點,直到找到結束節點
     * 首先初始化起始節點的資訊,而後迴圈獲取下一個節點,最終找到結束節點
     *
     */
    public void startSearchPath(AMap map){
        // 對初始節點進行操作,關聯周圍節點
        isOpen(map.getStartNode(), map);
        isClose(map.getStartNode());
        map.getStartNode().setReachable(false);
        map.getStartNode().setParentNode(map.getStartNode());
        // 迴圈取下一節點,當結束節點包含
        do {
            if (open.size() == 0){
                break;
            }
            isOpen(open.get(0), map);
            isClose(open.get(0));
            sortOpenList();
        } while (open.contains(map.getEndNode()));
        // 設定搜尋最優結果路徑
        setBestPathSign(map);
        // 當開啟列表中包含了結束節點則退出迴圈,清楚open中的結束節點
        isClose(map.getEndNode());
        // 展示尋找路徑的結果
        showSearchPath(map);
    }

    private void setBestPathSign(AMap map) {
        ANode node = map.getEndNode();
        while (true){
            ANode tempNode = node.getParentNode();
            if (tempNode == null || tempNode == map.getStartNode()){
                break;
            }
            node = tempNode;
            tempNode.setValue(NodeEnum.XNODE);
        }
    }

    /**
     * 列印該Map中所有節點資訊(包含了日誌資訊)
     * @param map
     */
    public void showSearchPath(AMap map) {
        System.out.println("運算後節點資訊如下:");
        for (int i = 0; i < map.height; i++) {
            for (int j = 0; j < map.weight; j++) {
                System.out.print(map.getNodes()[j][i].getValue().getValue() + " ");
            }
            System.out.println();
        }
        if (map.getEndNode().getParentNode() == null){
            System.out.println("沒有結束節點資訊。");
        }
    }

}

以上就是A*演算法的具體實現啦!演算法已經再程式碼中說明的很清晰啦,具體就不再多說。程式碼略作修改,小夥伴們還是自己去編寫一遍吧,還是很好意思的。如果實在想要原始碼連線在此啦。程式碼

相關推薦

EasyLearn--JAVA實現32經典演算法設計()A*演算法

A*演算法是一種靜態路網中求解最短路徑最有效的直接搜尋方法,其中經典的圖形有網格影象如下: 根據這個影象來對演算法進行說明主要思想:該演算法又稱為啟發式演算法,啟發之處在於公式F=G+H,F為最終結果值,G為所行走的步數,H就是預估值,其中H可根據不同的策略採用不同的規

如何用Java實現NLP的經典關鍵詞演算法 TF-IDF

面對一篇文章,我們如何提取他的關鍵詞呢。如果是我們自己去提取,那隻需要讀一遍,然後大腦中就會有一定的印象了,但是對於計算機來說,他沒有人那樣的思考能力啊,那怎麼辦,只能依靠演算法了。今天分享的內容呢是如何用Java語言實現NLP(自然語言處理)領域中一個非常著名

java實現向量的相關係數演算法

有兩個向量V1和V2 V1={1:3,2:2,3:1,5:0},V2={1:3,3:1,4:2,5:0} 以表格的形式展現: 將向量V1和V2帶入相關係數公式並展開展開,結果為: n值(n = 4):從表格可以看出,向量V1和V2 的第五位元素上都是0,因此該位置

java 實現接口 方法重名的解決辦法——內部類

nehe run .get tar extend pac 接口 內部 java package com.kk.innerClass; /** * 通過內部類實現接口 * 解決多個接口中方法重名問題 * */interface Machine { void run(

java實現int數交換

操作 使用 不用 col int 個數 span 定義 交換 普通方法,進階方法,大神方法 1 @Test 2 public void test3(){ 3 int m = 5; 4 int n = 12; 5

Java 實現32位MD5加密

https utf baidu static post AD pos byte tar MD5介紹【鏈接】 Java代碼實現 1 public class Md5Util { 2 private String Md5Util(String s) { 3

筆記Java實現執行緒A B C,BC執行緒執行完再執行A

final Lock lc = new ReentrantLock(); .. run() { lc.lock(); ... lc.unlock(); } 可能開啟方式不對吧,沒實現! 改用join() 可以實現(BC與A以單執行緒模式執行),程式碼如下: package

java實現檔案以壓縮包匯出到本地

描述:使用java將多個檔案同時壓縮為壓縮包,並匯出到本地 /** *壓縮檔案並匯出 */ public static void zipFiles() throws IOException {   File file = null;   String zipFileName = "";   File[

Java實現有序的單項鍊表的合併

  無意中看到一道題,如下: 現有兩個有序(升序)的單向連結串列,請編寫程式將這兩個連結串列合併成一個,並確保合併後的連結串列也是升序的 單向升序連結串列定義: public class ListNode {  int val;  ListNode next;

java實現執行緒達到一個闕伐值後一起執行

給大家推薦個靠譜的公眾號程式設計師探索之路,大家一起加油 1. CountDownLatch 1.1 簡介 CountDownLatch是一個同步輔助類,通過它可以完成類似於阻塞當前執行緒的功能,即:一個執行緒或多個執行緒一直等待,直到其他執行緒執行的操作完成。CountDownLatch用

java實現有序連結串列合併為一個有序連結串列

節點類 public class Node { private Node next; private Integer number; Node(Integer number) { this.number=number; next=null; } Node() {

JAVA實現附件傳輸

需要引入以下jar包 <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> &l

Java實現圖的深度和廣度優先遍歷演算法

<link rel="stylesheet" href="https://csdnimg.cn/release/phoenix/template/css/

演算法設計與分析P8演算法實現第一題

#include<stdio.h> #include<string.h> int main() { int n, t, i,temp; int count[10]; mem

Java實現有序的連結串列合併

Java實現兩個有序的連結串列合併 先實現兩個有序連結串列 程式碼如下: SingleLinkedList sll1 = new SingleLinkedList(); for (int i = 0; i < 10; i += 2) {

java實現矩陣乘法 有錯誤希望有大佬幫忙

//java實現兩個矩陣相乘 有個錯誤在下邊 有沒有哪個大佬幫我看看 十分感謝 package 實驗五; import java.util.Scanner; public class Matrix { private int rows; private int cols;

C++實現超大的字元數字相加的演算法的程式碼

如下資料是關於C++實現兩個超大的字元數字相加的演算法的程式碼。 #include <iostream> #include <string> #include <stack> using namespace std; void deleteLeadingZeros(st

8皇后以及N皇后演算法探究,回溯演算法JAVA實現,遞迴方案(

八皇后問題,是一個古老而著名的問題,是回溯演算法的典型案例。該問題是國際西洋棋棋手馬克斯·貝瑟爾於1848年提出:在8×8格的國際象棋上擺放八個皇后,使其不能互相攻擊,即任意兩個皇后都不能處於同一行、同一列或同一斜線上,問有多少種擺法。 高斯認為有76種方案。1854年在柏林的象棋雜誌

java實現數值相除並保留指定精度

/** * @param v1 除數 * @param v2 被除數 * @param scale 小數點精度 * @return */ public static double

java實現字串排序

編寫應用程式,該類中有一個方法sort()(其原型為:void sort(String str[])),從命令列傳入多個字串,呼叫方法sort()對該字串陣列按字典順序從小到大排序。 程式碼如下: import java.util.*; public clas