链表是贪吃蛇游戏的核心数据结构!蛇的身体就是一个链表。
链表是由节点组成的数据结构,每个节点包含:
┌─────────┐ ┌─────────┐ ┌─────────┐
│ 数据: A │───>│ 数据: B │───>│ 数据: C │───> NULL
│ next: ─┼───>│ next: ─┼───>│ next: ─┘
└─────────┘ └─────────┘ └─────────┘
↑
head
| 特性 | 数组 | 链表 |
|---|---|---|
| 大小 | 固定 | 动态 |
| 访问 | 随机访问 O(1) | 顺序访问 O(n) |
| 插入/删除 | 慢(需要移动) | 快(修改指针) |
| 内存 | 连续 | 不连续 |
int arr[5] = {1, 2, 3, 4, 5}; // 固定大小
arr[2] = 30; // 随机访问,快
// 可以动态增长
Node* head = create_node(1);
add_node(&head, 2);
add_node(&head, 3);
// ... 可以一直添加
typedef struct Node {
int data; // 数据
struct Node* next; // 指向下一个节点
} Node;
Node* create_node(int data) {
Node* node = malloc(sizeof(Node));
node->data = data;
node->next = NULL;
return node;
}
void insert_at_head(Node** head, int data) {
Node* new_node = create_node(data);
new_node->next = *head; // 新节点指向原头部
*head = new_node; // 更新头部
}
// 使用
Node* head = NULL;
insert_at_head(&head, 3); // 3
insert_at_head(&head, 2); // 2 -> 3
insert_at_head(&head, 1); // 1 -> 2 -> 3
void print_list(Node* head) {
Node* current = head;
while (current != NULL) {
printf("%d -> ", current->data);
current = current->next;
}
printf("NULL\n");
}
void delete_node(Node** head, int data) {
Node* current = *head;
Node* prev = NULL;
// 找到要删除的节点
while (current && current->data != data) {
prev = current;
current = current->next;
}
if (!current) return; // 没找到
// 删除
if (!prev) {
// 删除的是头节点
*head = current->next;
} else {
prev->next = current->next;
}
free(current);
}
void free_list(Node* head) {
Node* current = head;
while (current) {
Node* next = current->next;
free(current);
current = next;
}
}
蛇的身体天然就是一个链表!
// 蛇身段
typedef struct Segment {
int x; // X 坐标
int y; // Y 坐标
struct Segment* next; // 指向下一段
} Segment;
// 蛇
typedef struct {
Segment* head; // 头部
Segment* tail; // 尾部
int length; // 长度
int direction; // 方向
} Snake;
Snake* snake_create(int start_x, int start_y) {
Snake* snake = malloc(sizeof(Snake));
// 创建头部
Segment* head = malloc(sizeof(Segment));
head->x = start_x;
head->y = start_y;
head->next = NULL;
snake->head = head;
snake->tail = head;
snake->length = 1;
snake->direction = DIR_RIGHT;
return snake;
}
void snake_move(Snake* snake) {
// 1. 计算新头部位置
int new_x = snake->head->x;
int new_y = snake->head->y;
switch (snake->direction) {
case DIR_UP: new_y -= 1; break;
case DIR_DOWN: new_y += 1; break;
case DIR_LEFT: new_x -= 1; break;
case DIR_RIGHT: new_x += 1; break;
}
// 2. 创建新头部
Segment* new_head = malloc(sizeof(Segment));
new_head->x = new_x;
new_head->y = new_y;
new_head->next = snake->head;
snake->head = new_head;
snake->length++;
// 3. 移除尾部(除非在生长)
if (!snake->growing) {
// 找到倒数第二个节点
Segment* current = snake->head;
while (current->next->next) {
current = current->next;
}
// 删除最后一个节点
free(current->next);
current->next = NULL;
snake->tail = current;
snake->length--;
} else {
snake->growing = false; // 重置生长标志
}
}
void snake_render(Snake* snake) {
Segment* current = snake->head;
int is_head = 1;
while (current) {
char symbol = is_head ? 'O' : '#';
// 使用 ncurses 或 printf 绘制
mvaddch(current->y, current->x, symbol);
current = current->next;
is_head = 0;
}
}
bool snake_check_collision(Snake* snake, int width, int height) {
// 检查墙壁碰撞
if (snake->head->x <= 0 || snake->head->x >= width - 1 ||
snake->head->y <= 0 || snake->head->y >= height - 1) {
return true;
}
// 检查自身碰撞(从第二个节点开始)
Segment* current = snake->head->next;
while (current) {
if (current->x == snake->head->x &&
current->y == snake->head->y) {
return true;
}
current = current->next;
}
return false;
}
void snake_destroy(Snake* snake) {
Segment* current = snake->head;
while (current) {
Segment* next = current->next;
free(current);
current = next;
}
free(snake);
}
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
typedef enum { DIR_UP, DIR_DOWN, DIR_LEFT, DIR_RIGHT } Direction;
typedef struct Segment {
int x, y;
struct Segment* next;
} Segment;
typedef struct {
Segment* head;
Segment* tail;
int length;
Direction dir;
bool growing;
} Snake;
Snake* snake_create(int x, int y) {
Snake* s = malloc(sizeof(Snake));
Segment* seg = malloc(sizeof(Segment));
seg->x = x; seg->y = y; seg->next = NULL;
s->head = s->tail = seg;
s->length = 1;
s->dir = DIR_RIGHT;
s->growing = false;
return s;
}
void snake_move(Snake* s) {
int nx = s->head->x, ny = s->head->y;
if (s->dir == DIR_UP) ny--;
else if (s->dir == DIR_DOWN) ny++;
else if (s->dir == DIR_LEFT) nx--;
else if (s->dir == DIR_RIGHT) nx++;
Segment* new_head = malloc(sizeof(Segment));
new_head->x = nx; new_head->y = ny;
new_head->next = s->head;
s->head = new_head;
s->length++;
if (!s->growing) {
Segment* cur = s->head;
while (cur->next->next) cur = cur->next;
free(cur->next);
cur->next = NULL;
s->tail = cur;
s->length--;
} else {
s->growing = false;
}
}
void snake_destroy(Snake* s) {
Segment* cur = s->head;
while (cur) {
Segment* next = cur->next;
free(cur);
cur = next;
}
free(s);
}
int main(void) {
Snake* snake = snake_create(10, 10);
printf("蛇创建成功,长度:%d\n", snake->length);
snake_destroy(snake);
return 0;
}
实现链表的基本操作:插入、删除、查找
实现双向链表(每个节点有 prev 和 next 指针)
完善贪吃蛇的链表实现,添加吃食物生长的功能
下一课:模块化编程