本文共 7716 字,大约阅读时间需要 25 分钟。
栈(stack)又名堆栈,仅允许在表的一端进行插入和删除运算。这一端被称为栈顶,相对地,把另一端称为栈底。向一个栈插入新元素又称作进栈、入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素。栈是一种后入先出的操作(LIFO)
#define MAX 100 typedef char SDataType; typedef struct Stack { SDataType arr[MAX]; int top; }Stack;
void StackInit(Stack *ps) { assert(ps); ps->top = 0; }
void StackDestory(Stack *ps) { assert(ps); ps->top = 0; }
void StackPush(Stack *ps, SDataType data) { assert(ps); assert(ps->top < MAX); ps->arr[ps->top] = data; ps->top++; }
void StackPop(Stack *ps) { assert(ps); assert(ps->top > 0); ps->top--; }
SDataType StackTop(Stack *ps) { assert(ps); return ps->arr[ps->top - 1]; }
int StackSize(Stack *ps) { assert(ps); return ps->top; }
int StackEmpty(Stack *ps) //为空返回1,否则返回0 { assert(ps); return ps->top == 0 ? 1 : 0; }
char a[] = "(())abc{[(])}"; // 左右括号次序匹配不正确 char b[] = "(()))abc{[]}"; // 右括号多于左括号 char c[] = "(()()abc{[]}"; // 左括号多于右括号 char d[] = "(())abc{[]()}";
上面四种表示测试用例,其实也代表了四种不同的情况。我们就可以运用栈来判断括号是否匹配,当遇见左括号,进行压栈,遇见右括号,判断是否与栈顶元素相匹配并出栈,以此下去。
void MatchBrackets(const char *str) { assert(str); Stack stack; StackInit(&stack); const char *p = str; while (*p != '\0') { switch (*p) { case '{': case '[': case '(': StackPush(&stack, *p); break; case '}': case ']': case ')': if (StackEmpty(&stack)) { printf("右括号多与左括号\n"); return; } char c = StackTop(&stack); StackPop(&stack); if (!((c == '{' && *p == '}') || (c == '[' && *p == ']') || (c == '(' && *p == ')'))) { printf("左右括号不匹配\n"); return; } break; default: break; } p++; } if (!StackEmpty(&stack)) { printf("左括号多于右括号\n"); } else { printf("左右括号匹配\n"); } }
结果:
逆波兰表达式又叫做后缀表达式
例如:1 * 2 + 3 * 4 可以表示为: 1 2 * 3 4 * + 逆波兰表达式就完美的运用到了栈的思想。遇见数字就压栈,遇见操作符,就将栈顶的两个元素进行操作,并出栈,并将运算结果压栈,以此下去,最终栈里只有一个元素 这个问题的思路比较简单,但是如何判断是字符还是数字尼?难道要加一个判断条件,这样显然太麻烦,我们可以运用枚举,里面就两个,操作数和操作符typedef enum { NUMBER, OPERATOR }Type; typedef enum { ADD, SUB, MUL, DIV, NOUSED } OP; typedef struct { Type type; int number; OP operator; }Item; Item g_arr[] = { { NUMBER, 1, NOUSED }, { NUMBER, 2, NOUSED }, { OPERATOR, -1, //因为这是一个操作符,不存在数字,所以就可以随便定义一个 MUL }, { NUMBER, 3, NOUSED }, { NUMBER, 4, NOUSED }, { OPERATOR, -1, MUL }, { OPERATOR, -1, ADD }, }; void RPN(Item g_arr[], int size) { Stack stack; StackInit(&stack); for (int i = 0; i < size; i++) { if (g_arr[i].type == NUMBER) { StackPush(&stack, g_arr[i].number); } else { int result = 0; int n1 = 0; int n2 = 0; n1 = StackTop(&stack); StackPop(&stack); n2 = StackTop(&stack); StackPop(&stack); switch (g_arr[i].operator) { case ADD: result = n1 + n2; break; case SUB: result = n2 - n1; break; case MUL: result = n1 * n2; break; case DIV: result = n2 / n1; break; default : break; } StackPush(&stack, result); } } assert(StackSize(&stack) == 1); printf("%d\n", StackTop(&stack)); } int main() { int size = sizeof(g_arr) / sizeof(g_arr[0]); RPN(g_arr, size); system("pause"); return 0; }
简单迷宫就是指只有一条出路的迷宫,0表示墙,1表示路,如下图:
我们按照左上右下的步骤开始走,也就是先判断左边,是路就走,不是路的话就开始判断上面,是路就走······以此类推。每走一步,都要讲现在的点(坐标)进行压栈,如果走到死路,就要开始一步一步出栈,当出到某一位置还有别的路可以走时,继续压栈,直到最终结果#define _CRE_SECURE_NO_WARNINGS 1 #include#include #include #include #define ROW (6) #define COL (6) #define MAX (100) int g_arr[ROW][COL] = { { 0, 0, 0, 0, 0, 0 }, { 0, 0, 1, 0, 0, 0 }, { 0, 0, 1, 0, 0, 0 }, { 0, 0, 1, 1, 1, 0 }, { 0, 0, 1, 0, 1, 1 }, { 0, 0, 1, 0, 0, 0 } }; typedef struct Position { int x; int y; }Position; typedef struct Stack { Position arr[MAX]; int top; }Stack; void StackInit(Stack *ps) { assert(ps); ps->top = 0; } void StackPush(Stack *ps, Position pos) { assert(ps); assert(ps->top < MAX); ps->arr[ps->top] = pos; ps->top++; } void StackPop(Stack *ps) { assert(ps); assert(ps->top > 0); ps->top--; } Position StackTop(Stack *ps) { assert(ps); return ps->arr[ps->top - 1]; } int StackEmpty(Stack *ps) { assert(ps); if (ps->top == 0) { return 1; } return 0; } //打印 void printMaze(int arr[ROW][COL]) { for (int i = 0; i < ROW; i++) { for (int j = 0; j < COL; j++) { //1表示路 if (arr[i][j] == 1) { printf(" "); } //0表示墙 else if (arr[i][j] == 0) { printf("█"); } //2表示已经走过 else if (arr[i][j] == 2) { printf("⊙"); } } printf("\n"); } } //判断栈是否为空 int IsExit(Position pos) { if (pos.y == COL - 1) { return 1; } return 0; } //判断是否可以走 int CanPass(Position pos) { if (pos.x < 0 || pos.x >= ROW) { return 0; } if (pos.y < 0 || pos.y >= ROW) { return 0; } if (g_arr[pos.x][pos.y] == 1) { return 1; } else { return 0; } } void test() { Stack stack; StackInit(&stack); Position entry = { 5, 2 }; //表示入口 Position pos = entry; Position nextPos; while (1) { g_arr[pos.x][pos.y] = 2; system("cls"); printMaze(g_arr); Sleep(300); StackPush(&stack, pos); //每走一步进行压栈 if (IsExit(pos)) { printf("找到出口\n"); return; } //按照左、上、右、下步骤走 //左 nextPos = pos; nextPos.y -= 1; if (CanPass(nextPos)) { pos = nextPos; continue; } //上 nextPos = pos; nextPos.x -= 1; if (CanPass(nextPos)) { pos = nextPos; continue; } //右 nextPos = pos; nextPos.y += 1; if (CanPass(nextPos)) { pos = nextPos; continue; } //下 nextPos = pos; nextPos.x += 1; if (CanPass(nextPos)) { pos = nextPos; continue; } //如果都不能走,就要出栈 pos = StackTop(&stack); StackPop(&stack); g_arr[pos.x][pos.y] = 2; if (StackEmpty(&stack)) { printf("结束\n"); return; } //这个pos就是上一个不能走的位置的前一个位置 pos = StackTop(&stack); StackPop(&stack); } } int main() { test(); system("pause"); return; }
结果:
if (IsExit(pos)) { printf("找到出口\n"); g_arr[pos.x][pos.y] = 0; // 只需将出口变成墙就可以了 //return; }
结果:
在开始分析复杂迷宫之前,我们先理解一个思想:回溯。
回溯法是按照深度优先的策略,从根结点出发搜索解空间树。算法搜索至解空间树的任一结点时,总是先判断该结点是否肯定不包含问题的解。如果肯定不包含,则跳过对以该结点为根的子树的系统搜索,逐层向其祖先结点回溯。否则,进入该子树,继续按深度优先的策略进行搜索。回溯法在用来求问题的所有解时,要回溯到根,且根结点的所有子树都已被搜索遍才结束。而回溯法在用来求问题的任一解时,只要搜索到问题的一个解就可以结束。这种以深度优先的方式系统地搜索问题的解的算法称为回溯法
简单通俗易懂的说就可以把某一个问题理解为树,这个树有很多分支,每一个不同的分支都有一个不同的解,如果某一个节点肯定不是问题的解,就要回溯到该节点的上一个节点处
Stack pathStack; Stack shortPathStack; //保存最短路径 void CopyStack(Stack *pdest, Stack *psrc) { memcpy(pdest->arr, psrc->arr, psrc->top * sizeof(Position)); pdest->top = psrc->top; } void test(Position pos) { g_arr[pos.x][pos.y] = 2; Position nextPos; StackPush(&pathStack, pos); //system("cls"); printMaze(g_arr); Sleep(300); if (IsExit(pos)) { g_arr[pos.x][pos.y] = 1; printf("找到出口\n"); StackPop(&pathStack); for (int i = 0; i < pathStack.top; i++) { printf("%d %d\n", pathStack.arr[i].x, pathStack.arr[i].y); } if (shortPathStack.top == 0 || pathStack.top < shortPathStack.top) { CopyStack(&shortPathStack, &pathStack); } return; } nextPos = pos; nextPos.y -= 1; if (CanPass(nextPos)) { test(nextPos); } //上 nextPos = pos; nextPos.x -= 1; if (CanPass(nextPos)) { test(nextPos); } //右 nextPos = pos; nextPos.y += 1; if (CanPass(nextPos)) { test(nextPos); } //下 nextPos = pos; nextPos.x += 1; if (CanPass(nextPos)) { test(nextPos); } g_arr[pos.x][pos.y] = 1; StackPop(&pathStack); } int main() { StackInit(&pathStack); StackInit(&shortPathStack); Position entry = { 5, 2 }; test(entry); system("pause"); return; }