Skip to content

Commit 702d69c

Browse files
Merge pull request #2817 from Kuxry/astar-C
添加0126.骑士的攻击astar C语言 版本
2 parents 1c82540 + 2602359 commit 702d69c

File tree

2 files changed

+201
-0
lines changed

2 files changed

+201
-0
lines changed

problems/0203.移除链表元素.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -737,7 +737,45 @@ public class Solution
737737
}
738738
}
739739
```
740+
### Ruby#
741+
742+
```ruby
743+
# 定义链表节点
744+
class ListNode
745+
attr_accessor :val, :next
746+
def initialize(val = 0, _next = nil)
747+
@val = val
748+
@next = _next
749+
end
750+
end
751+
752+
# 删除链表中值为 val 的节点
753+
def remove_elements(head, val)
754+
# 创建一个虚拟头节点,这样可以简化删除头节点的处理
755+
# 虚拟头节点的值为 0,指向当前链表的头节点
756+
dummy = ListNode.new(0)
757+
dummy.next = head
758+
759+
# 初始化当前节点为虚拟头节点
760+
current = dummy
761+
762+
# 遍历链表,直到当前节点的下一个节点为空
763+
while current.next
764+
# 如果当前节点的下一个节点的值等于 val
765+
if current.next.val == val
766+
# 跳过该节点,即将当前节点的 next 指向下一个节点的 next
767+
current.next = current.next.next
768+
else
769+
# 否则继续遍历,当前节点向前移动
770+
current = current.next
771+
end
772+
end
773+
774+
# 返回删除 val 后的新链表的头节点,虚拟头节点的 next 就是新的头节点
775+
dummy.next
776+
end
740777

778+
```
741779

742780
<p align="center">
743781
<a href="https://programmercarl.com/other/kstar.html" target="_blank">

problems/kamacoder/0126.骑士的攻击astar.md

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -514,7 +514,170 @@ main()
514514

515515
### C
516516

517+
```C
518+
#include <stdio.h>
519+
#include <stdlib.h>
520+
#include <string.h>
521+
522+
// 定义一个结构体,表示棋盘上骑士的位置和相关的 A* 算法参数
523+
typedef struct {
524+
int x, y; // 骑士在棋盘上的坐标
525+
int g; // 从起点到当前节点的实际消耗
526+
int h; // 从当前节点到目标节点的估计消耗(启发式函数值)
527+
int f; // 总的估计消耗(f = g + h)
528+
} Knight;
529+
530+
#define MAX_HEAP_SIZE 2000000 // 假设优先队列的最大容量
531+
532+
// 定义一个优先队列,使用最小堆来实现 A* 算法中的 Open 列表
533+
typedef struct {
534+
Knight data[MAX_HEAP_SIZE];
535+
int size;
536+
} PriorityQueue;
537+
538+
// 初始化优先队列
539+
void initQueue(PriorityQueue *pq) {
540+
pq->size = 0;
541+
}
542+
543+
// 将骑士节点插入优先队列
544+
void push(PriorityQueue *pq, Knight k) {
545+
if (pq->size >= MAX_HEAP_SIZE) {
546+
// 堆已满,无法插入新节点
547+
return;
548+
}
549+
int i = pq->size++;
550+
pq->data[i] = k;
551+
// 上滤操作,维护最小堆的性质,使得 f 值最小的节点在堆顶
552+
while (i > 0) {
553+
int parent = (i - 1) / 2;
554+
if (pq->data[parent].f <= pq->data[i].f) {
555+
break;
556+
}
557+
// 交换父节点和当前节点
558+
Knight temp = pq->data[parent];
559+
pq->data[parent] = pq->data[i];
560+
pq->data[i] = temp;
561+
i = parent;
562+
}
563+
}
564+
565+
// 从优先队列中弹出 f 值最小的骑士节点
566+
Knight pop(PriorityQueue *pq) {
567+
Knight min = pq->data[0];
568+
pq->size--;
569+
pq->data[0] = pq->data[pq->size];
570+
// 下滤操作,维护最小堆的性质
571+
int i = 0;
572+
while (1) {
573+
int left = 2 * i + 1;
574+
int right = 2 * i + 2;
575+
int smallest = i;
576+
if (left < pq->size && pq->data[left].f < pq->data[smallest].f) {
577+
smallest = left;
578+
}
579+
if (right < pq->size && pq->data[right].f < pq->data[smallest].f) {
580+
smallest = right;
581+
}
582+
if (smallest == i) {
583+
break;
584+
}
585+
// 交换当前节点与最小子节点
586+
Knight temp = pq->data[smallest];
587+
pq->data[smallest] = pq->data[i];
588+
pq->data[i] = temp;
589+
i = smallest;
590+
}
591+
return min;
592+
}
593+
594+
// 判断优先队列是否为空
595+
int isEmpty(PriorityQueue *pq) {
596+
return pq->size == 0;
597+
}
598+
599+
// 启发式函数:计算从当前位置到目标位置的欧几里得距离的平方(避免开方,提高效率)
600+
int heuristic(int x, int y, int goal_x, int goal_y) {
601+
int dx = x - goal_x;
602+
int dy = y - goal_y;
603+
return dx * dx + dy * dy; // 欧几里得距离的平方
604+
}
605+
606+
// 用于记录从起点到棋盘上每个位置的最小移动次数
607+
int moves[1001][1001];
608+
609+
// 骑士在棋盘上的8个可能移动方向
610+
int dir[8][2] = {
611+
{-2, -1}, {-2, 1}, {-1, 2}, {1, 2},
612+
{2, 1}, {2, -1}, {1, -2}, {-1, -2}
613+
};
517614

615+
// 使用 A* 算法寻找从起点到目标点的最短路径
616+
int astar(int start_x, int start_y, int goal_x, int goal_y) {
617+
PriorityQueue pq;
618+
initQueue(&pq);
619+
620+
// 初始化 moves 数组,-1 表示未访问过的位置
621+
memset(moves, -1, sizeof(moves));
622+
moves[start_x][start_y] = 0; // 起点位置的移动次数为 0
623+
624+
// 初始化起始节点
625+
Knight start;
626+
start.x = start_x;
627+
start.y = start_y;
628+
start.g = 0;
629+
start.h = heuristic(start_x, start_y, goal_x, goal_y);
630+
start.f = start.g + start.h; // 总的估计消耗
631+
632+
push(&pq, start); // 将起始节点加入优先队列
633+
634+
while (!isEmpty(&pq)) {
635+
Knight current = pop(&pq); // 取出 f 值最小的节点
636+
637+
// 如果已经到达目标位置,返回所需的最小移动次数
638+
if (current.x == goal_x && current.y == goal_y) {
639+
return moves[current.x][current.y];
640+
}
641+
642+
// 遍历当前节点的所有可能移动方向
643+
for (int i = 0; i < 8; i++) {
644+
int nx = current.x + dir[i][0];
645+
int ny = current.y + dir[i][1];
646+
647+
// 检查新位置是否在棋盘范围内且未被访问过
648+
if (nx >= 1 && nx <= 1000 && ny >= 1 && ny <= 1000 && moves[nx][ny] == -1) {
649+
moves[nx][ny] = moves[current.x][current.y] + 1; // 更新移动次数
650+
651+
// 创建新节点,表示骑士移动到的新位置
652+
Knight neighbor;
653+
neighbor.x = nx;
654+
neighbor.y = ny;
655+
neighbor.g = current.g + 5; // 每次移动的消耗为 5(骑士移动的距离平方)
656+
neighbor.h = heuristic(nx, ny, goal_x, goal_y);
657+
neighbor.f = neighbor.g + neighbor.h;
658+
659+
push(&pq, neighbor); // 将新节点加入优先队列
660+
}
661+
}
662+
}
663+
664+
return -1; // 如果无法到达目标位置,返回 -1
665+
}
666+
667+
int main() {
668+
int n;
669+
scanf("%d", &n);
670+
while (n--) {
671+
int a1, a2, b1, b2; // 起点和目标点的坐标
672+
scanf("%d %d %d %d", &a1, &a2, &b1, &b2);
673+
674+
int result = astar(a1, a2, b1, b2); // 使用 A* 算法计算最短路径
675+
printf("%d\n", result); // 输出最小移动次数
676+
}
677+
return 0;
678+
}
679+
680+
```
518681
519682
520683

0 commit comments

Comments
 (0)