LinuxC编程一站式学习——堆栈

本科学习数据结构时实践比较少,因此最近开始复习,看到网站http://learn.akae.cn/media/ch12s03.html,既可以打基础,又可以顺便学习Linux的知识。

第12章讲“堆栈”,第3节讲“深度优先算法”寻找路径迷宫。代码已经给出了,为了方便其他同学下载调试,我这里也附上:

原始代码:

#include <stdio.h>
#define MAX_ROW 5
#define MAX_COL 5

struct point {
  int row;
  int col;
} stack[512];
int top = 0;

int maze[5][5] = {
  {0, 1, 0, 0, 0},
  {0, 1, 0, 1, 0},
  {0, 0, 0, 0, 0},
  {0, 1, 1, 1, 0},
  {0, 0, 0, 1, 0}
};

void push(struct point p)
{
  stack[top++] = p;
}

struct point pop()
{
  return stack[--top];
}

int is_empty()
{
  return top == 0;
}

void print_maze(void)
{
  int i, j;
  for (i = 0; i < MAX_ROW; i++) {
    for (j = 0; j < MAX_COL; j++)
      printf("%d ", maze[i][j]);
    putchar('\n');
  }
  printf("*********\n");
}

struct point predecessor[MAX_ROW][MAX_COL] = {
  {{-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}},
  {{-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}},
  {{-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}},
  {{-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}},
  {{-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}}
};

void visit(int row, int col, struct point pre)
{
  struct point visit_point = { row, col };
  maze[row][col] = 2;
  predecessor[row][col] = pre;
  push(visit_point);
}

int main()
{
  struct point p = { 0, 0 };
  maze[p.row][p.col] = 2;
  push(p);

  while (!is_empty()) {
    p = pop();
    if (p.row == MAX_ROW - 1 && p.col == MAX_COL - 1)
      break;
    if (p.col + 1 < MAX_COL && maze[p.row][p.col + 1] == 0)
      visit(p.row, p.col + 1, p);
    if (p.row + 1 < MAX_ROW && maze[p.row + 1][p.col] == 0)
      visit(p.row + 1, p.col, p);
    if (p.col > 0 && maze[p.row][p.col - 1] == 0)
      visit(p.row, p.col - 1, p);
    if (p.row > 0 && maze[p.row - 1][p.col] == 0)
      visit(p.row - 1, p.col, p);
    print_maze();
  }
  if (p.row == MAX_ROW - 1 && p.col == MAX_COL - 1) {
    printf("(%d, %d)\n", p.row, p.col);
    while (predecessor[p.row][p.col].row != -1) {
      p = predecessor[p.row][p.col];
      printf("(%d, %d)\n", p.row, p.col);
    }
  } else
    printf("No path!\n");
  return 0;
}

下面有三道习题。

1. 正向打印路径

文中已经分析“正向打印路径”原理上的不可行,因此我想到的方法是:

A:把输出到路径压到一个新的堆栈中,然后pop出来——这很好理解。

B:取巧的做法——把迷宫出口(4,4)当作起点,入口(0,0)当作终点,找到的路径(4,4)->(0,0)逆序输出即为(0,0)->(4,4),倘若没有路径,那么只会输出“No pathing!”

B方法代码更改的地方很少:

#include <stdio.h>
#define MAX_ROW 5
#define MAX_COL 5

struct point {
  int row;
  int col;
} stack[512];
int top = 0;

int maze[5][5] = {
  {0, 1, 0, 0, 0},
  {0, 1, 0, 1, 0},
  {0, 0, 0, 0, 0},
  {0, 1, 1, 1, 0},
  {0, 0, 0, 1, 0}
};

void push(struct point p)
{
  stack[top++] = p;
}

struct point pop()
{
  return stack[--top];
}

int is_empty()
{
  return top == 0;
}

void print_maze(void)
{
  int i, j;
  for (i = 0; i < MAX_ROW; i++) {
    for (j = 0; j < MAX_COL; j++)
      printf("%d ", maze[i][j]);
    putchar('\n');
  }
  printf("*********\n");
}

struct point predecessor[MAX_ROW][MAX_COL] = {
  {{-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}},
  {{-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}},
  {{-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}},
  {{-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}},
  {{-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1}}
};

void visit(int row, int col, struct point pre)
{
  struct point visit_point = { row, col };
  maze[row][col] = 2;
  predecessor[row][col] = pre;
  push(visit_point);
}

int main()
{
  struct point p = { MAX_ROW-1, MAX_COL-1 };	// 起点为迷宫终点
  maze[p.row][p.col] = 2;
  push(p);

  while (!is_empty()) {
    p = pop();
    if (p.row == 0 && p.col == 0)	// 终止条件为达到起点
      break;
    if (p.col + 1 < MAX_COL && maze[p.row][p.col + 1] == 0)
      visit(p.row, p.col + 1, p);
    if (p.row + 1 < MAX_ROW && maze[p.row + 1][p.col] == 0)
      visit(p.row + 1, p.col, p);
    if (p.col > 0 && maze[p.row][p.col - 1] == 0)
      visit(p.row, p.col - 1, p);
    if (p.row > 0 && maze[p.row - 1][p.col] == 0)
      visit(p.row - 1, p.col, p);
    print_maze();
  }
  if (p.row == 0 && p.col == 0) {
    printf("(%d, %d)\n", p.row, p.col);
    while (predecessor[p.row][p.col].row != -1) {	// 最后只剩下起点以及未被访问到点到predecessor值为(-1,-1)
      p = predecessor[p.row][p.col];
      printf("(%d, %d)\n", p.row, p.col);
    }
  } else
    printf("No path!\n");
  return 0;
}

读者会发现,这种方法最终的结果maze矩阵中出现了0,原因是:本算法是深度优先算法,假如第一条路径就能够到达终点,那么算法就不会再计算了。

由此读者可以想到延伸的问题:求迷宫有几条路径可以达到终点?进而延伸:每条路径的长度是多少?这两个问题等到我以后再研究时会再练习,本文就先略去了。


2. 缩小空间

原来predecessor存储的是point的二维数组,我首先想到的是将其改为二维整形数据——代表指向前一个位置的“方向”。visit函数需要传入这个方向值;最后逆序输出时也需要根据方向值找到下一个位置点。首先,四个方向宏定义。

#include <stdio.h>
#define MAX_ROW 5
#define MAX_COL 5
#define RIGHT	1
#define LEFT	2
#define UP	3
#define DOWN	4

struct point {
	int row;
	int col;
} stack[512];
int top = 0;

int maze[5][5] = {
	{0, 1, 0, 0, 0},
	{0, 1, 0, 1, 0},
	{0, 0, 0, 0, 0},
	{0, 1, 1, 1, 0},
	{0, 0, 0, 1, 0}
};

void push(struct point p)
{
	stack[top++] = p;
}

struct point pop()
{
	return stack[--top];
}

int is_empty()
{
	return top == 0;
}

void print_maze(void)
{
	int i, j;
	for (i = 0; i < MAX_ROW; i++) {
		for (j = 0; j < MAX_COL; j++)
			printf("%d ", maze[i][j]);
		putchar('\n');
	}
	printf("*********\n");
}

int predecessor[MAX_ROW][MAX_COL] = {
	{0, 0, 0, 0, 0},
	{0, 0, 0, 0, 0},
	{0, 0, 0, 0, 0},
	{0, 0, 0, 0, 0},
	{0, 0, 0, 0, 0}
};

void visit(int row, int col, struct point pre, int Dir)
{
	struct point visit_point = { row, col };
	maze[row][col] = 2;
	predecessor[row][col] = Dir;
	push(visit_point);
}

int main()
{
	struct point p = { 0, 0 };
	maze[p.row][p.col] = 2;
	push(p);

	while (!is_empty()) {
		p = pop();
		if (p.row == MAX_ROW - 1 && p.col == MAX_COL - 1)
			break;
		if (p.col + 1 < MAX_COL && maze[p.row][p.col + 1] == 0)
			visit(p.row, p.col + 1, p, LEFT);
		if (p.row + 1 < MAX_ROW && maze[p.row + 1][p.col] == 0)
			visit(p.row + 1, p.col, p, UP);
		if (p.col > 0 && maze[p.row][p.col - 1] == 0)
			visit(p.row, p.col - 1, p, RIGHT);
		if (p.row > 0 && maze[p.row - 1][p.col] == 0)
			visit(p.row - 1, p.col, p, DOWN);
		print_maze();
	}
	if (p.row == MAX_ROW - 1 && p.col == MAX_COL - 1) {
		printf("(%d, %d)\n", p.row, p.col);
		while (predecessor[p.row][p.col] != 0) {
			switch (predecessor[p.row][p.col]) {
			case UP:
				p.row -= 1;
				break;
			case DOWN:
				p.row += 1;
				break;
			case LEFT:
				p.col -= 1;
				break;
			case RIGHT:
				p.col += 1;
				break;
			}
			// p = predecessor[p.row][p.col];
			printf("(%d, %d)\n", p.row, p.col);
		}
	} else
		printf("No path!\n");
	return 0;
}
起先设置predecessor的初始值都是0,最终找到的路径上点到predecessor的值则为LEFT/UP/RIGHT/DOWN,因此沿着已知的方向依次判断下个座标点到位置,直到preecessor值=0的起点。


更进一步,我想,predecessor和maze都是2维整形数组,为什么不能把两者合并呢?于是修改如下:(为了防止数据混淆,修改原来到宏定义)

#include <stdio.h>
#define MAX_ROW 5
#define MAX_COL 5
#define RIGHT	3
#define LEFT	4
#define UP	5
#define DOWN	6

struct point {
	int row;
	int col;
} stack[512];
int top = 0;

int maze[5][5] = {
	{0, 0, 0, 0, 0},
	{1, 0, 1, 1, 0},
	{0, 0, 1, 1, 1},
	{0, 1, 0, 0, 0},
	{0, 0, 0, 1, 0}
};

void push(struct point p)
{
	stack[top++] = p;
}

struct point pop()
{
	return stack[--top];
}

int is_empty()
{
	return top == 0;
}

void print_maze(void)
{
	int i, j;
	for (i = 0; i < MAX_ROW; i++) {
		for (j = 0; j < MAX_COL; j++)
			printf("%d ", maze[i][j]);
		putchar('\n');
	}
	printf("*********\n");
}

/*
int predecessor[MAX_ROW][MAX_COL] = {
	{0, 0, 0, 0, 0},
	{0, 0, 0, 0, 0},
	{0, 0, 0, 0, 0},
	{0, 0, 0, 0, 0},
	{0, 0, 0, 0, 0}
};
*/

void visit(int row, int col, struct point pre, int Dir)
{
	struct point visit_point = { row, col };
	maze[row][col] = Dir;
	// predecessor[row][col] = Dir;
	push(visit_point);
}

int main()
{
	struct point p = { 0, 0 };
	maze[p.row][p.col] = 2;	// 起始点的方向设置为2,代表无方向。
	push(p);

	while (!is_empty()) {
		p = pop();
		if (p.row == MAX_ROW - 1 && p.col == MAX_COL - 1)
			break;
		if (p.col + 1 < MAX_COL && maze[p.row][p.col + 1] == 0)
			visit(p.row, p.col + 1, p, LEFT);
		if (p.row + 1 < MAX_ROW && maze[p.row + 1][p.col] == 0)
			visit(p.row + 1, p.col, p, UP);
		if (p.col > 0 && maze[p.row][p.col - 1] == 0)
			visit(p.row, p.col - 1, p, RIGHT);
		if (p.row > 0 && maze[p.row - 1][p.col] == 0)
			visit(p.row - 1, p.col, p, DOWN);
		print_maze();
	}	// 倘若有路径,那么最后pop出的p即(4,4)
	if (p.row == MAX_ROW - 1 && p.col == MAX_COL - 1) {
		printf("(%d, %d)\n", p.row, p.col);
		// 起点的方向是2,其他皆为1/3/4/5/6
		// 0代表尚未访问到点:当第一条路径就打到终点时,其它路径也就没有再访问了
		while (maze[p.row][p.col] != 2) {	
			switch (maze[p.row][p.col]) {
			case UP:
				p.row -= 1;
				break;
			case DOWN:
				p.row += 1;
				break;
			case LEFT:
				p.col -= 1;
				break;
			case RIGHT:
				p.col += 1;
				break;
			}
			// p = predecessor[p.row][p.col];
			printf("(%d, %d)\n", p.row, p.col);
		}
	} else	// 倘若最后pop出的非(4,4),那么就没有路径
		printf("No path!\n");
	return 0;
}


3. 堆栈变递归

待续。。。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章