github.com/qiuhoude/go-web@v0.0.0-20220223060959-ab545e78f20d/algorithm/datastructures/graph/a_star.go (about)

     1  package graph
     2  
     3  import (
     4  	"fmt"
     5  	"github.com/qiuhoude/go-web/algorithm/datastructures/queue"
     6  )
     7  
     8  /*
     9  a*算法
    10  h(i): 启发函数(heuristic function),是到目标点的曼哈顿距离(Manhattan distance)
    11  	曼哈顿距离: 指两点之间横纵坐标的距离之和 h() = abs(x1-x2) + abs(y1-y2)
    12  g(i): 起点到该点的距离
    13  f(i) = h(i) + g(i)
    14  */
    15  
    16  type point struct {
    17  	f      int    // f(i) = h(i) + g(i)
    18  	g      int    //起点到该点的距离
    19  	x, y   int    //坐标 m[y][x]
    20  	parent *point // 父节点
    21  }
    22  
    23  func (p *point) String() string {
    24  	return fmt.Sprintf("(%v,%v,%v)", p.x, p.y, p.g)
    25  }
    26  
    27  func newPoint(x, y, g, f int, p *point) *point {
    28  	return &point{
    29  		x:      x,
    30  		y:      y,
    31  		g:      g,
    32  		f:      f,
    33  		parent: p,
    34  	}
    35  }
    36  
    37  func comparePoint(v1, v2 interface{}) int {
    38  	p1 := v1.(*point)
    39  	p2 := v2.(*point)
    40  	if p1.f > p2.f {
    41  		return 1
    42  	} else if p1.f < p2.f {
    43  		return -1
    44  	}
    45  	return 0
    46  }
    47  
    48  func equalPoint(v1, v2 interface{}) bool {
    49  	p1 := v1.(*point)
    50  	p2 := v2.(*point)
    51  	return p1.x == p2.x && p1.y == p2.y
    52  }
    53  
    54  // 曼哈顿距离
    55  func hManhattan(x1, y1, x2, y2 int) int {
    56  	return abs(x1-x2) + abs(y1-y2)
    57  }
    58  
    59  func abs(a int) int {
    60  	if a < 0 {
    61  		return -a
    62  	}
    63  	return a
    64  }
    65  
    66  func xyToId(x, y, width int) int {
    67  	return y*width + x
    68  }
    69  
    70  const (
    71  	positive = 10 // 上下左右距离是 10
    72  	opposite = 14 // 对角是距离是 14
    73  )
    74  
    75  func checkToXyAvail(x, y int, m [][]uint) bool {
    76  	high := len(m)
    77  	width := len(m[0])
    78  	if y < 0 || y >= high || x < 0 || x >= width { // 超过边界
    79  		return false
    80  	}
    81  	if m[y][x] == 1 { // 障碍物不能走
    82  		return false
    83  	}
    84  	return true
    85  }
    86  
    87  func AstarSearch(sx, sy, tx, ty int, m [][]uint) {
    88  	// 都是以左上为原点
    89  	high := len(m)
    90  	width := len(m[0])
    91  
    92  	visited := make(map[int]bool)               // 相当于有些教程中的 close表
    93  	que := queue.NewPriorityQueue(comparePoint) //  open表
    94  	visited[xyToId(sx, sy, width)] = true
    95  	que.Enqueue(newPoint(sx, sy, 0, 0, nil)) // 加入起点坐标
    96  	var findPoint *point
    97  out:
    98  	for !que.IsEmpty() {
    99  		curP := que.Dequeue().(*point)
   100  		// 逐个遍历 上下左右 左上右上 左下右下
   101  		for i := -1; i <= 1; i++ { // y轴
   102  			for j := -1; j <= 1; j++ { // x轴
   103  				afY := curP.y + i // 计算后的 x, y
   104  				afX := curP.x + j
   105  				if !checkToXyAvail(afX, afY, m) || (i == 0 && j == 0) {
   106  					continue
   107  				}
   108  				// 有障碍物斜边是否能跨越
   109  				if i == -1 && j == -1 { // 左上
   110  					if !checkToXyAvail(curP.x-1, curP.y, m) || !checkToXyAvail(curP.x, curP.y-1, m) {
   111  						continue
   112  					}
   113  				} else if i == 1 && j == -1 { // 左下
   114  					if !checkToXyAvail(curP.x-1, curP.y, m) || !checkToXyAvail(curP.x, curP.y+1, m) {
   115  						continue
   116  					}
   117  				} else if i == -1 && j == 1 { // 右上
   118  					if !checkToXyAvail(curP.x+1, curP.y, m) || !checkToXyAvail(curP.x, curP.y-1, m) {
   119  						continue
   120  					}
   121  				} else if i == 1 && j == 1 { //右下
   122  					if !checkToXyAvail(curP.x+1, curP.y, m) || !checkToXyAvail(curP.x, curP.y+1, m) {
   123  						continue
   124  					}
   125  				}
   126  
   127  				isOpposite := abs(i) == 1 && abs(j) == 1 // 是否为对角
   128  				h := hManhattan(afX, afY, tx, ty)        // 曼哈顿距离
   129  				g := 0
   130  				if isOpposite {
   131  					g = curP.g + opposite // 对角+14
   132  				} else {
   133  					g = curP.g + positive
   134  				}
   135  				f := h + g //f(i) = h(i) + g(i)
   136  				id := xyToId(afX, afY, width)
   137  				np := newPoint(afX, afY, f, g, curP)
   138  				if visited[id] {
   139  					p2 := que.Remove(np, equalPoint)
   140  					if p2 != nil {
   141  						np2 := p2.(*point)
   142  						np2.g = g
   143  						np2.f = f
   144  						// 此处不要更新父节点值
   145  						que.Enqueue(np2)
   146  					}
   147  				} else {
   148  					que.Enqueue(np)
   149  					visited[id] = true // 设置已访问
   150  				}
   151  				if afX == tx && afY == ty {
   152  					findPoint = np
   153  					break out
   154  				}
   155  			}
   156  		}
   157  	}
   158  
   159  	if findPoint != nil {
   160  		m2 := make([][]uint, high)
   161  		for i := range m2 {
   162  			m2[i] = make([]uint, width)
   163  			copy(m2[i], m[i])
   164  		}
   165  		cur := findPoint
   166  		var i uint = 2
   167  		for ; cur != nil; cur = cur.parent {
   168  			m2[cur.y][cur.x] = i
   169  			i++
   170  		}
   171  		printMap(m2)
   172  	} else {
   173  		fmt.Println("不可达")
   174  	}
   175  }
   176  
   177  func printMap(m [][]uint) {
   178  	//fmt.Printf("%4d", -1)
   179  	//for i := 0; i < len(m[0]); i++ {
   180  	//	fmt.Printf("%4d", i)
   181  	//}
   182  	//fmt.Println()
   183  	for i := range m {
   184  		//fmt.Printf("%4d", i)
   185  		for j := range m[i] {
   186  			if j != 0 {
   187  			}
   188  			fmt.Printf("%4d", m[i][j])
   189  		}
   190  		fmt.Println()
   191  	}
   192  }