【数据结构】栈和队列的应用

 欢迎光~临~^_^

 

目录

知识树

1、栈在括号匹配中的应用

2、栈在表达式求值中的应用

1. 中缀表达式转后缀表达式

2. 后缀表达式求值

3.中缀表达式转前缀表达式

4.中缀表达式的计算

3、栈在递归中的应用

4、队列在层次遍历中的应用


知识树

1、栈在括号匹配中的应用

栈在括号匹配中的应用是一种经典的应用。

思路如下:

1. 定义一个栈,并初始化为空栈。
2. 依次遍历输入的字符序列。
3. 如果遇到左括号(包括'(', '{', '['),则将其压入栈中。
4. 如果遇到右括号(包括')', '}', ']'),则进行匹配。
   1. 如果栈为空,则匹配失败,说明有多余的右括号。
   2. 如果栈不为空,则将栈顶元素弹出并与当前右括号进行匹配。
      1. 如果匹配成功,则继续遍历序列。
      2. 如果匹配失败,则匹配失败,说明左右括号不匹配。
5. 遍历结束后,如果栈为空,则说明左右括号匹配;如果栈不为空,则说明有多余的左括号。 

代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

#define MAXSIZE 100

// 定义栈结构体
typedef struct stack{
    char data[MAXSIZE];
    int top; // 栈顶指针
}Stack;

// 初始化栈
void InitStack(Stack *s){
    s->top = -1; // 初始化时设置栈顶指针为-1
}

// 判断栈是否为空
bool IsEmpty(Stack *s){
    if(s->top == -1)
        return true;
    else
        return false;
}

// 判断栈是否已满
bool IsFull(Stack *s){
    if(s->top == MAXSIZE - 1)
        return true;
    else
        return false;
}

// 将元素入栈
bool Push(Stack *s, char c){
    if(IsFull(s))
        return false;
    else{
        s->top++; // 先移动栈顶指针
        s->data[s->top] = c; // 再放入数据
        return true;
    }
}

// 将元素出栈
bool Pop(Stack *s){
    if(IsEmpty(s))
        return false;
    else{
        s->top--; // 直接移动栈顶指针即可
        return true;
    }
}

// 获取栈顶元素
char GetTop(Stack *s){
    return s->data[s->top];
}

// 判断左右括号是否匹配
bool Match(char left, char right){
    if(left=='(' && right==')')
        return true;
    else if(left=='{' && right=='}')
        return true;
    else if(left=='[' && right==']')
        return true;
    else
        return false;
}

// 判断括号序列是否匹配
bool BracketMatch(char *str){
    Stack s;
    InitStack(&s);
    int i = 0;
    char c = str[i];
    while(c!='\0'){
        if(c=='(' || c=='{' || c=='[')
            Push(&s, c);
        else if(c==')' || c=='}' || c==']'){
            if(IsEmpty(&s)) // 栈为空说明有多余的右括号
                return false;
            else{
                char top = GetTop(&s);
                if(Match(top, c))
                    Pop(&s); // 匹配成功,左右括号出栈
                else
                    return false; // 匹配失败
            }
        }else{
            // 非括号字符,忽略
        }
        i++;
        c = str[i];
    }
    if(IsEmpty(&s))
        return true;
    else
        return false; // 栈不为空说明有多余的左括号
}

// 测试程序
int main(){
    char str[MAXSIZE];
    printf("请输入一个括号序列:");
    scanf("%s", str);
    if(BracketMatch(str))
        printf("括号匹配成功!\n");
    else
        printf("括号匹配失败!\n");
    return 0;
}

2、栈在表达式求值中的应用

栈在表达式求值中的应用是非常重要的。我们可以通过栈来实现表达式的转换和求值。以下是栈在表达式求值中的应用:

1. 中缀表达式转后缀表达式

中缀表达式是人们最常使用的表达式形式,但对于计算机程序的求值来说并不容易。因此我们需要将中缀表达式转换为后缀表达式。转换过程就可以通过栈来实现。

例如:将中缀表达式“3+5*6-7”转换为后缀表达式,具体实现过程如下:

- 遍历中缀表达式,如果扫描到数字,直接输出到后缀表达式;
- 如果扫描到运算符,则将其与栈顶运算符做比较,如果优先级低于或等于栈顶运算符,则将栈顶运算符弹出并输出到后缀表达式中,直到栈顶运算符优先级低于当前运算符,最后将当前运算符入栈;
- 如果扫描到左括号,则将其直接入栈;
- 如果扫描到右括号,则将栈中的运算符逐一出栈并输出到后缀表达式中,直到扫描到左括号为止,最后将左括号出栈。

通过栈的实现,中缀表达式“3+5*6-7”可以转换为后缀表达式“356*+7-”。

下面是一个简单的C语言实现,可以将输入的中缀表达式转换为后缀表达式:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#define MAX_EXPR_SIZE 100

typedef enum {
    L_PAREN, R_PAREN, ADD, SUBTRACT, MULTIPLY, DIVIDE, MODULO, EXPONENT, VALUE, END
} Token;

typedef struct {
    Token type;
    int value;
} ExprToken;

typedef struct {
    ExprToken *tokens;
    int size;
    int capacity;
} ExprTokenList;

ExprTokenList* createExprTokenList(int capacity) {
    ExprTokenList *list = (ExprTokenList*) malloc(sizeof(ExprTokenList));
    list->size = 0;
    list->capacity = capacity;
    list->tokens = (ExprToken*) malloc(capacity * sizeof(ExprToken));
    return list;
}

void destroyExprTokenList(ExprTokenList *list) {
    free(list->tokens);
    free(list);
}

void addExprToken(ExprTokenList *list, Token type, int value) {
    if (list->size == list->capacity) {
        list->capacity *= 2;
        list->tokens = (ExprToken*) realloc(list->tokens, list->capacity * sizeof(ExprToken));
    }
    ExprToken token;
    token.type = type;
    token.value = value;
    list->tokens[list->size++] = token;
}

int precedence(Token op) {
    switch (op) {
        case EXPONENT:
            return 3;
        case MULTIPLY:
        case DIVIDE:
        case MODULO:
            return 2;
        case ADD:
        case SUBTRACT:
            return 1;
        default:
            return 0;
    }
}

void infixToPostfix(char *infixExpr, ExprTokenList *postfixExpr) {
    ExprTokenList *operatorStack = createExprTokenList(100);

    char *p = infixExpr;
    while (*p) {
        while (isspace(*p)) p++; // skip whitespace

        if (*p == '\0') {
            break;
        } else if (isdigit(*p)) {
            int value = 0;
            while (isdigit(*p)) {
                value = value * 10 + (*p - '0');
                p++;
            }
            addExprToken(postfixExpr, VALUE, value);
        } else if (*p == '(') {
            addExprToken(operatorStack, L_PAREN, 0);
            p++;
        } else if (*p == ')') {
            while (operatorStack->size > 0 && operatorStack->tokens[operatorStack->size - 1].type != L_PAREN) {
                ExprToken operatorToken = operatorStack->tokens[--operatorStack->size];
                addExprToken(postfixExpr, operatorToken.type, operatorToken.value);
            }
            if (operatorStack->size > 0) operatorStack->size--; // remove left parenthesis from stack
            p++;
        } else {
            Token op;
            int value = 0;
            switch (*p) {
                case '+':
                    op = ADD;
                    break;
                case '-':
                    op = SUBTRACT;
                    break;
                case '*':
                    op = MULTIPLY;
                    break;
                case '/':
                    op = DIVIDE;
                    break;
                case '%':
                    op = MODULO;
                    break;
                case '^':
                    op = EXPONENT;
                    break;
                default:
                    printf("Invalid operator: %c\n", *p);
                    destroyExprTokenList(postfixExpr);
                    destroyExprTokenList(operatorStack);
                    return;
            }

            while (operatorStack->size > 0 && precedence(op) <= precedence(operatorStack->tokens[operatorStack->size - 1].type)) {
                ExprToken operatorToken = operatorStack->tokens[--operatorStack->size];
                addExprToken(postfixExpr, operatorToken.type, operatorToken.value);
            }

            addExprToken(operatorStack, op, value);
            p++;
        }
    }

    while (operatorStack->size > 0) {
        ExprToken operatorToken = operatorStack->tokens[--operatorStack->size];
        addExprToken(postfixExpr, operatorToken.type, operatorToken.value);
    }

    destroyExprTokenList(operatorStack);
}

int main() {
    char infixExpr[MAX_EXPR_SIZE];
    ExprTokenList *postfixExpr = createExprTokenList(100);

    printf("Enter the infix expression: ");
    fgets(infixExpr, MAX_EXPR_SIZE, stdin);

    infixToPostfix(infixExpr, postfixExpr);
    printf("The postfix expression is: ");
    for (int i = 0; i < postfixExpr->size; i++) {
        ExprToken token = postfixExpr->tokens[i];
        switch (token.type) {
            case VALUE:
                printf("%d ", token.value);
                break;
            case ADD:
                printf("+ ");
                break;
            case SUBTRACT:
                printf("- ");
                break;
            case MULTIPLY:
                printf("* ");
                break;
            case DIVIDE:
                printf("/ ");
                break;
            case MODULO:
                printf("%% ");
                break;
            case EXPONENT:
                printf("^ ");
                break;
            default:
                break;
        }
    }
    printf("\n");

    destroyExprTokenList(postfixExpr);

    return 0;
}

此代码将输入的中缀表达式转换为后缀表达式。在进行转换时,需要使用一个栈来存储运算符,并且需要考虑运算符的优先级。

具体流程如下:

  1. 遍历输入的中缀表达式中的每个字符。

  2. 如果遇到一个数字字符,将其解析为整数,并将其作为操作数添加到后缀表达式列表中。

  3. 如果遇到一个左括号字符,将其作为运算符添加到运算符栈中。

  4. 如果遇到一个右括号字符,将运算符栈中的运算符弹出并添加到后缀表达式列表中,直到遇到左括号字符为止。左括号字符将被弹出并丢弃。

  5. 如果遇到一个运算符字符,首先弹出运算符栈中具有更高或相等优先级的所有运算符,并添加它们到后缀表达式列表中,然后将当前运算符添加到运算符栈中。

  6. 最终,弹出所有剩余的运算符并将它们添加到后缀表达式列表中。

需要注意的是,此代码中使用了动态数组来存储表达式 Token。另外,此实现仅支持加、减、乘、除、模、指数运算和括号。如果需要支持其他操作符,可以相应地扩展此代码。

 

2. 后缀表达式求值

后缀表达式也称为逆波兰表达式,是一种非常方便计算的表达式形式,这是因为后缀表达式中运算符都在操作数之后,不需要考虑括号和优先级的问题。由于后缀表达式的计算只需要一个栈,而不需要进行递归调用,因此效率更高。

例如:对于后缀表达式“356*+7-”,具体实现过程如下:

- 遍历后缀表达式,如果扫描到操作数,将其入栈;
- 如果扫描到运算符,则从栈中弹出两个操作数,并执行相应的运算,将结果入栈;
- 最终栈中只剩下一个操作数,即为表达式的值。

通过栈的实现,后缀表达式“356*+7-”可以求值为“16”。

下面是使用C语言实现后缀表达式求值的示例代码:

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>

typedef struct {
    int top; // 栈顶指针
    int capacity; // 栈容量
    int *array; // 栈元素
} Stack;

Stack* createStack(int capacity) {
    Stack *s = (Stack*) malloc(sizeof(Stack));
    s->capacity = capacity;
    s->top = -1;
    s->array = (int*) malloc(s->capacity * sizeof(int));
    return s;
}

int isFull(Stack *s) {
    return s->top == s->capacity - 1;
}

int isEmpty(Stack *s) {
    return s->top == -1;
}

void push(Stack *s, int item) {
    if (isFull(s)) {
        printf("Stack is full\n");
        return;
    }
    s->array[++s->top] = item;
}

int pop(Stack *s) {
    if (isEmpty(s)) {
        printf("Stack is empty\n");
        return -1;
    }
    return s->array[s->top--];
}

int peek(Stack *s) {
    if (isEmpty(s)) {
        printf("Stack is empty\n");
        return -1;
    }
    return s->array[s->top];
}

int isOperand(char c) {
    return isdigit(c);
}

int performOperation(char op, int num1, int num2) {
    switch (op) {
        case '+':
            return num1 + num2;
        case '-':
            return num1 - num2;
        case '*':
            return num1 * num2;
        case '/':
            return num1 / num2;
        case '^':
            return num1 ^ num2;
        default:
            return -1;
    }
}

int evaluate(char *expression) {
    Stack *stack = createStack(strlen(expression));

    for (int i = 0; expression[i] != '\0'; i++) {
        if (isOperand(expression[i])) {
            int operand = 0;
            while (isOperand(expression[i])) {
                operand = operand * 10 + (expression[i] - '0');
                i++;
            }
            push(stack, operand);
            i--;
        } else if (expression[i] == '+' || expression[i] == '-' || expression[i] == '*' || expression[i] == '/' || expression[i] == '^') {
            int num2 = pop(stack);
            int num1 = pop(stack);
            int result = performOperation(expression[i], num1, num2);
            push(stack, result);
        }
    }

    int finalResult = pop(stack);

    free(stack->array);
    free(stack);

    return finalResult;
}

int main() {
    char expression[100];
    printf("Enter the postfix expression: ");
    fgets(expression, 100, stdin);

    int result = evaluate(expression);
    printf("The result of the expression is %d\n", result);

    return 0;
}

此代码使用一个栈来求值后缀表达式。在处理后缀表达式时,需要注意以下几点:

  1. 操作数可以是多位数,因此在遇到数字字符时要将它们组合成一个整数。

  2. 当遇到操作数时,将其推入栈中。当遇到操作符时,将栈顶上的两个操作数弹出并应用于这个操作符,然后将结果推入栈中。

  3. 在处理完整个表达式之后,栈中仅剩下一个元素,即为表达式的值。

  4. 求值完成之后,释放栈的内存空间。

需要注意的是,后缀表达式中不需要考虑优先级和括号。

3.中缀表达式转前缀表达式

中缀表达式是指符号排列在两个操作数中间,比如:3 + 4、(2 - 1) * 5等。而前缀表达式是指符号排列在操作数前面,比如:+ 3 4、* - 2 1 5等。

中缀转前缀的过程可以通过以下步骤实现:

1. 将中缀表达式翻转。

2. 将翻转后的中缀表达式中的每个左括号变成右括号,每个右括号变成左括号。

3. 对翻转后的中缀表达式进行前缀表达式的转换,具体方法是:

   - 从左到右扫描表达式,如果是操作数,则直接输出到前缀表达式中。
   
   - 如果是左括号,则将其入栈。
   
   - 如果是右括号,则将栈中的操作符依次弹出并输出到前缀表达式中,直到遇到左括号为止。
   
   - 如果是操作符,则将其与栈顶操作符比较,如果优先级高于栈顶操作符,则将其入栈;否则,将栈中的操作符依次弹出并输出到前缀表达式中,直到栈为空或者栈顶操作符的优先级低于该操作符为止。
   
4. 将前缀表达式翻转即可得到结果。

举个例子,假设要将中缀表达式(3 + 4)* 5 - 6 / 2 转换为前缀表达式。按照上述步骤进行转换,得到翻转后的中缀表达式为:2 / 6 - 5 * ) 4 + 3 (。再将其转换为前缀表达式,得到:- * 5 ) + 4 3 / 6 2。最后再将其翻转,得到前缀表达式:2 / 6 - 5 * + 4 3 -

下面是一个简单的C语言实现,可以将输入的中缀表达式转换为前缀表达式:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#define MAX_EXPR_SIZE 100

typedef enum {
    L_PAREN, R_PAREN, ADD, SUBTRACT, MULTIPLY, DIVIDE, MODULO, EXPONENT, VALUE, END
} Token;

typedef struct {
    Token type;
    int value;
} ExprToken;

typedef struct {
    ExprToken *tokens;
    int size;
    int capacity;
} ExprTokenList;

ExprTokenList* createExprTokenList(int capacity) {
    ExprTokenList *list = (ExprTokenList*) malloc(sizeof(ExprTokenList));
    list->size = 0;
    list->capacity = capacity;
    list->tokens = (ExprToken*) malloc(capacity * sizeof(ExprToken));
    return list;
}

void destroyExprTokenList(ExprTokenList *list) {
    free(list->tokens);
    free(list);
}

void addExprToken(ExprTokenList *list, Token type, int value) {
    if (list->size == list->capacity) {
        list->capacity *= 2;
        list->tokens = (ExprToken*) realloc(list->tokens, list->capacity * sizeof(ExprToken));
    }
    ExprToken token;
    token.type = type;
    token.value = value;
    list->tokens[list->size++] = token;
}

int precedence(Token op) {
    switch (op) {
        case EXPONENT:
            return 4;
        case MULTIPLY:
        case DIVIDE:
        case MODULO:
            return 3;
        case ADD:
        case SUBTRACT:
            return 2;
        default:
            return 0;
    }
}

void infixToPrefix(char *infixExpr, ExprTokenList *prefixExpr) {
    ExprTokenList *operatorStack = createExprTokenList(100);

    char *p = infixExpr + strlen(infixExpr) - 1;
    while (p >= infixExpr) {
        while (isspace(*p)) p--; // skip whitespace

        if (p < infixExpr) {
            break;
        } else if (isdigit(*p)) {
            int value = 0;
            int factor = 1;
            while (p >= infixExpr && isdigit(*p)) {
                value = value + (*p - '0') * factor;
                factor *= 10;
                p--;
            }
            addExprToken(prefixExpr, VALUE, value);
        } else if (*p == ')') {
            addExprToken(operatorStack, R_PAREN, 0);
            p--;
        } else if (*p == '(') {
            while (operatorStack->size > 0 && operatorStack->tokens[operatorStack->size - 1].type != R_PAREN) {
                ExprToken operatorToken = operatorStack->tokens[--operatorStack->size];
                addExprToken(prefixExpr, operatorToken.type, operatorToken.value);
            }
            if (operatorStack->size > 0) operatorStack->size--; // remove right parenthesis from stack
            p--;
        } else {
            Token op;
            int value = 0;
            switch (*p) {
                case '+':
                    op = ADD;
                    break;
                case '-':
                    op = SUBTRACT;
                    break;
                case '*':
                    op = MULTIPLY;
                    break;
                case '/':
                    op = DIVIDE;
                    break;
                case '%':
                    op = MODULO;
                    break;
                case '^':
                    op = EXPONENT;
                    break;
                default:
                    printf("Invalid operator: %c\n", *p);
                    destroyExprTokenList(prefixExpr);
                    destroyExprTokenList(operatorStack);
                    return;
            }

            while (operatorStack->size > 0 && precedence(op) < precedence(operatorStack->tokens[operatorStack->size - 1].type)) {
                ExprToken operatorToken = operatorStack->tokens[--operatorStack->size];
                addExprToken(prefixExpr, operatorToken.type, operatorToken.value);
            }

            addExprToken(operatorStack, op, value);
            p--;
        }
    }

    while (operatorStack->size > 0) {
        ExprToken operatorToken = operatorStack->tokens[--operatorStack->size];
        addExprToken(prefixExpr, operatorToken.type, operatorToken.value);
    }

    destroyExprTokenList(operatorStack);
}

void reverseExprTokenList(ExprTokenList *list) {
    for (int i = 0; i < list->size / 2; i++) {
        ExprToken tmp = list->tokens[i];
        list->tokens[i] = list->tokens[list->size - i - 1];
        list->tokens[list->size - i - 1] = tmp;
    }
}

int main() {
    char infixExpr[MAX_EXPR_SIZE];
    ExprTokenList *prefixExpr = createExprTokenList(100);

    printf("Enter the infix expression: ");
    fgets(infixExpr, MAX_EXPR_SIZE, stdin);

    infixToPrefix(infixExpr, prefixExpr);
    reverseExprTokenList(prefixExpr);
    printf("The prefix expression is: ");
    for (int i = 0; i < prefixExpr->size; i++) {
        ExprToken token = prefixExpr->tokens[i];
        switch (token.type) {
            case VALUE:
                printf("%d ", token.value);
                break;
            case ADD:
                printf("+ ");
                break;
            case SUBTRACT:
                printf("- ");
                break;
            case MULTIPLY:
                printf("* ");
                break;
            case DIVIDE:
                printf("/ ");
                break;
            case MODULO:
                printf("%% ");
                break;
            case EXPONENT:
                printf("^ ");
                break;
            default:
                break;
        }
    }
    printf("\n");

    destroyExprTokenList(prefixExpr);

    return 0;
}

此代码将输入的中缀表达式转换为前缀表达式。在进行转换时,需要使用一个栈来存储运算符,并且需要考虑运算符的优先级。

具体流程如下:

  1. 遍历输入的中缀表达式中的每个字符。从右向左遍历时,需要注意字符串的结尾字符 '\0'。

  2. 如果遇到一个数字字符,将其解析为整数,并将其作为操作数添加到前缀表达式列表中。

  3. 如果遇到一个右括号字符,将其作为运算符添加到运算符栈中。

  4. 如果遇到一个左括号字符,将运算符栈中的运算符弹出并添加到前缀表达式列表中,直到遇到右括号字符为止。右括号字符将被弹出并丢弃。

  5. 如果遇到一个运算符字符,首先弹出运算符栈中具有更高或相等优先级的所有运算符,并添加它们到前缀表达式列表中,然后将当前运算符添加到运算符栈中。

  6. 最终,弹出所有剩余的运算符并将它们添加到前缀表达式列表中。

  7. 最后,将前缀表达式列表中的 Token 顺序反转。

需要注意的是,此代码中使用了动态数组来存储表达式 Token。另外,此实现仅支持加、减、乘、除、模、指数运算和括号。如果需要支持其他操作符,可以相应地扩展此代码。

 

4.中缀表达式的计算

具体步骤如下:

  1. 创建两个栈:一个存储操作数,一个存储操作符。

  2. 从左到右扫描表达式,如果当前字符是数字,则将其转换成操作数并压入操作数栈中;如果当前字符是操作符,则判断其与操作符栈顶元素的优先级,如果优先级高于或等于栈顶元素,则将该操作符压入操作符栈中;否则,将操作符栈顶元素弹出并与操作数栈中的两个操作数进行运算,将运算结果压入操作数栈中,直到当前操作符的优先级不小于栈顶操作符的优先级。

  3. 当表达式扫描完毕后,依次将操作符栈中的元素弹出并与操作数栈中的两个操作数进行运算,将运算结果压入操作数栈中,直到操作符栈为空。

  4. 最后,操作数栈中只剩下一个元素,即为表达式的结果。

具体实现可以参考下面的 C 代码:

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>

typedef struct {
    int top; // 栈顶指针
    int capacity; // 栈容量
    int *array; // 栈元素
} Stack;

Stack* createStack(int capacity) {   //初始化栈
    Stack *s = (Stack*) malloc(sizeof(Stack));
    s->capacity = capacity;
    s->top = -1;
    s->array = (int*) malloc(s->capacity * sizeof(int));
    return s;
}

int isFull(Stack *s) {   //判满
    return s->top == s->capacity - 1;
}

int isEmpty(Stack *s) { //判空
    return s->top == -1;
}

void push(Stack *s, int item) {  //进栈
    if (isFull(s)) {
        printf("Stack is full\n");
        return;
    }
    s->array[++s->top] = item;
}

int pop(Stack *s) {    //出栈
    if (isEmpty(s)) { 
        printf("Stack is empty\n");
        return -1;
    }
    return s->array[s->top--];
}

int peek(Stack *s) {
    if (isEmpty(s)) {
        printf("Stack is empty\n");
        return -1;
    }
    return s->array[s->top];
}

int isOperand(char c) {
    return isdigit(c);
}

int precedence(char op) {
    switch (op) {
        case '+':
        case '-':
            return 1;
        case '*':
        case '/':
            return 2;
        case '^':
            return 3;
        default:
            return -1;
    }
}

int performOperation(char op, int num1, int num2) {  //
    switch (op) {
        case '+':
            return num1 + num2;
        case '-':
            return num1 - num2;
        case '*':
            return num1 * num2;
        case '/':
            return num1 / num2;
        case '^':
            return num1 ^ num2;
        default:
            return -1;
    }
}

int evaluate(char *expression) {
    Stack *operandStack = createStack(strlen(expression));
    Stack *operatorStack = createStack(strlen(expression));

    for (int i = 0; expression[i] != '\0'; i++) {
        if (isOperand(expression[i])) {
            int operand = 0;
            while (isOperand(expression[i])) {
                operand = operand * 10 + (expression[i] - '0');
                i++;
            }
            push(operandStack, operand);
            i--;
        } else if (expression[i] == '(') {
            push(operatorStack, '(');
        } else if (expression[i] == ')') {
            while (!isEmpty(operatorStack) && peek(operatorStack) != '(') {
                int op = pop(operatorStack);
                int num2 = pop(operandStack);
                int num1 = pop(operandStack);
                int result = performOperation(op, num1, num2);
                push(operandStack, result);
            }
            pop(operatorStack);
        } else if (expression[i] == '+' || expression[i] == '-' || expression[i] == '*' || expression[i] == '/' || expression[i] == '^') {
            while (!isEmpty(operatorStack) && precedence(expression[i]) <= precedence(peek(operatorStack))) {
                int op = pop(operatorStack);
                int num2 = pop(operandStack);
                int num1 = pop(operandStack);
                int result = performOperation(op, num1, num2);
                push(operandStack, result);
            }
            push(operatorStack, expression[i]);
        }
    }

    while (!isEmpty(operatorStack)) {
        int op = pop(operatorStack);
        int num2 = pop(operandStack);
        int num1 = pop(operandStack);
        int result = performOperation(op, num1, num2);
        push(operandStack, result);
    }

    int finalResult = pop(operandStack);

    free(operandStack->array);
    free(operandStack);
    free(operatorStack->array);
    free(operatorStack);

    return finalResult;
}

int main() {
    char expression[100];
    printf("Enter the infix expression: ");
    fgets(expression, 100, stdin);

    int result = evaluate(expression);
    printf("The result of the expression is %d\n", result);

    return 0;
}

此代码使用两个栈(一个操作数栈和一个操作符栈)来求值中缀表达式。在处理中缀表达式时,需要注意以下几点:

  1. 操作数可以是多位数,因此在遇到数字字符时要将它们组合成一个整数。

  2. 操作数栈和操作符栈的处理方式略有不同。当遇到操作数时,将其推入操作数栈;当遇到左括号时,将其推入操作符栈;当遇到右括号时,将操作符栈中的操作符弹出直到遇到左括号,并将这些操作符应用于操作数栈顶上的操作数。最后,将左括号弹出操作符栈。

  3. 当遇到操作符时,将其推入操作符栈。但是,如果操作符栈的栈顶操作符优先级大于或等于当前操作符,则将操作符栈顶弹出并应用于操作数栈顶上的两个操作数,直到要推入的操作符成为栈顶元素。

  4. 最后,当处理完整个表达式之后,将操作符栈中余下的操作符弹出并应用于操作数栈顶上的两个操作数。

  5. 求值完成之后,释放两个栈的内存空间。

3、栈在递归中的应用

在递归中,栈的应用非常重要。当一个函数被调用时,它的所有局部变量、参数、函数返回地址等信息都会被压入栈中。当函数结束时,这些信息会从栈中弹出。因此,栈可以用来保存递归函数的状态。

举个例子,考虑一个递归函数计算斐波那契数列:

int fibonacci(int n) {
    if (n <= 1)
        return n;
    else
        return fibonacci(n-1) + fibonacci(n-2);
}

当n很大时,递归的深度会很大,这会导致栈空间的占用也很大。为了避免栈溢出,可以使用迭代方式实现斐波那契数列计算。

一种方法是使用循环:

int fibonacci(int n) {
    int a = 0, b = 1;
    for (int i = 0; i < n; i++) {
        int temp = a + b;
        a = b;
        b = temp;
    }
    return a;
}

 另一种方法是使用栈来保存递归状态。具体实现方式可以使用一个辅助栈,先将初始状态压入栈中,然后循环弹出栈顶元素,计算下一步的状态并将其压入栈中,直到得到结果。

int fibonacci(int n) {
    if (n <= 1)
        return n;

    stack<pair<int, int>> s;
    s.push(make_pair(n, 0));
    int result = 0;

    while (!s.empty()) {
        int k = s.top().first;
        int v = s.top().second;
        s.pop();

        if (k == 1) {
            result += 1;
            if (!s.empty()) {
                s.top().second += result;
                result = 0;
            }
        } else if (k == 2) {
            result += v;
            if (!s.empty()) {
                s.top().second += result;
                result = 0;
            }
        } else {
            s.push(make_pair(k - 2, 0));
            s.push(make_pair(k - 1, 0));
        }
    }

    return result;
}
 

这种方法需要额外的空间来存储栈元素,但可以避免递归深度过大的问题。

4、队列在层次遍历中的应用

队列在层次遍历中的应用是非常关键的。层次遍历是一种广度优先搜索的算法,它可以从根节点开始,按照顺序依次遍历每一层的节点,直到遍历完整个树或图为止。

在层次遍历中,我们需要使用队列来维护每一层的节点。具体来说,我们从根节点开始,首先将根节点入队,然后进入循环,每次从队列中取出队首节点,将其加入结果列表中,并将其所有子节点入队。然后重复这个过程,直到队列为空。

使用队列可以保证每次遍历时都是先遍历完上一层的节点,再遍历下一层的节点。因此,队列是层次遍历算法的核心数据结构。

队列在层次遍历中的应用可以用C语言实现。下面是一个示例代码:

#include <stdio.h>
#include <stdlib.h>

#define MAX_QUEUE_SIZE 100

// 定义队列结构体
typedef struct Queue {
    int data[MAX_QUEUE_SIZE];
    int front, rear;
} Queue;

// 初始化队列
void initQueue(Queue *q) {
    q->front = -1;
    q->rear = -1;
}

// 判断队列是否为空
int isEmpty(Queue *q) {
    return q->front == -1;
}

// 判断队列是否已满
int isFull(Queue *q) {
    return q->rear == MAX_QUEUE_SIZE - 1;
}

// 入队列
void enqueue(Queue *q, int value) {
    if (isFull(q)) {
        printf("Queue is full.\n");
        return;
    }
    if (isEmpty(q)) {
        q->front = 0;
    }
    q->rear++;
    q->data[q->rear] = value;
}

// 出队列
int dequeue(Queue *q) {
    if (isEmpty(q)) {
        printf("Queue is empty.\n");
        return -1;
    }
    int value = q->data[q->front];
    if (q->front == q->rear) {
        q->front = q->rear = -1;
    } else {
        q->front++;
    }
    return value;
}

// 层次遍历二叉树
void levelOrderTraversal(Node *root) {
    if (root == NULL) {
        return;
    }
    Queue q;
    initQueue(&q);
    enqueue(&q, root);
    while (!isEmpty(&q)) {
        Node *node = dequeue(&q);
        printf("%d ", node->data);
        if (node->left) {
            enqueue(&q, node->left);
        }
        if (node->right) {
            enqueue(&q, node->right);
        }
    }
    printf("\n");
}

int main() {
    Node *root = createTree();
    printf("Level order traversal of binary tree is: ");
    levelOrderTraversal(root);
    return 0;
}
 

在这个示例代码中,我们定义了一个队列结构体 Queue,用于维护层次遍历的节点。initQueue 函数用于初始化队列,isEmpty 函数和 isFull 函数分别用于判断队列是否为空和已满。enqueue 函数用于将节点入队列,dequeue 函数用于将节点出队列。levelOrderTraversal 函数用于层次遍历二叉树,其中我们利用队列实现了广度优先搜索。最后,我们在 main 函数中调用 levelOrderTraversal 函数来打印二叉树的层次遍历结果。

 🤞❤️🤞❤️🤞❤️栈和队列的应用的知识点总结就到这里啦,如果对博文还满意的话,劳烦各位看官动动“发财的小手”留下您对博文的赞和对博主的关注吧🤞❤️🤞❤️🤞❤️

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

十三衙

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值