数据结构-------栈

顺序栈:

一、数据结构定义

  1. 数据元素 DATATYPE
typedef struct person {
    char name[32];
    char sex;
    int age;
    int score;
} DATATYPE;
  1. 顺序栈结构 SeqStack
typedef struct list {
    DATATYPE *head;  // 栈空间首地址
    int tlen;        // 栈总容量(total length)
    int top;         // 栈顶指针(相当于当前元素计数)
} SeqStack;

二、核心操作接口

  1. 创建栈
SeqStack *CreateSeqStack(int size)
{
    SeqStack *ss = malloc(sizeof(SeqStack));
    if (NULL == ss)
    {
        perror("CreateSeqStack malloc error\n");
        return NULL;
    }
    ss->head = malloc(sizeof(DATATYPE) * size);
    if (NULL == ss)
    {
        perror("CreateSeqStack malloc2 error\n");
        return NULL;
    }
    ss->tlen = size;
    ss->top = 0;
    return ss;
}
  • 实现要点:
    • 动态分配结构体内存
    • 分配连续存储空间:head = malloc(size * sizeof(DATATYPE))
    • 初始化tlen = sizetop = 0
  1. 销毁栈
int DestroySeqStack(SeqStack *ss)
{
    if (NULL == ss)
    {
        fprintf(stderr, "DestroySeqStack pamter error\n");
        return 1;
    }
    free(ss->head);
    free(ss);
    return 0;
}
  • 内存释放顺序:
    1. 释放数据空间 free(ss->head)
    2. 释放控制结构 free(ss)
  1. 入栈操作
int PushSeqStack(SeqStack *ss, DATATYPE *data) // add
{
    if (NULL == ss || NULL == data || IsFullSeqStack(ss))
    {
        fprintf(stderr, "PushSeqStack pamter error\n");
        return 1;
    }
    memcpy(&ss->head[ss->top], data, sizeof(DATATYPE));
    ss->top++;
    return 0;
}
  • 实现流程:
if 栈未满:
    复制数据到ss->head[top]位置
    top++
    返回成功
else:
    返回失败
  1. 出栈操作
int PopSeqStack(SeqStack *ss)
{
    if (NULL == ss)
    {
        fprintf(stderr, "PopSeqStack pamter error\n");
        return 1;
    }
    ss->top--;
    return 0;
}
  • 实现逻辑:
if 栈非空:
    top--
    返回成功
else:
    返回失败
  1. 判空操作
int IsEmpySeqStack(SeqStack *ss)
{
    return 0 == ss->top;
}
  • 判断条件:top == 0
  1. 判满操作
int IsFullSeqStack(SeqStack *ss)
{
    return ss->tlen == ss->top;
}
  • 判断条件:top == tlen
  1. 获取栈顶元素
DATATYPE *GetTopSeqStack(SeqStack *ss)
{
    if (NULL == ss || IsEmpySeqStack(ss))
    {
        fprintf(stderr, "GetTopSeqStack pamter error\n");
        return NULL;
    }
    return &ss->head[ss->top - 1];
}
  • 注意点:
    • 返回ss->head[top-1]的地址
    • 需先判断栈是否为空
  1. 获取栈大小
int GetSizeSeqStack(SeqStack *ss)
{
    if (NULL == ss )
    {
        fprintf(stderr, "GetSizeSeqStack pamter error\n");
        return 1;
    }
    return ss->top;
}
  • 直接返回top

三、存储结构示意图

栈底 → head[0]
       head[1]
       ...
栈顶 → head[top-1]  ← 当前有效元素
       head[top]    ← 下一个可用位置(当top<tlen时)
       ...
       head[tlen-1]

四、时间复杂度分析

操作时间复杂度说明
创建栈O(1)内存分配操作
销毁栈O(1)内存释放操作
入栈/出栈O(1)直接操作栈顶指针
获取栈顶元素O(1)随机访问特性
获取栈大小O(1)直接读取top值

五、优势与局限

  1. 优势

    • 随机访问特性,支持快速定位
    • 内存连续,缓存命中率高
    • 操作时间复杂度均为O(1)
  2. 局限

    • 固定容量,需要预先分配空间
    • 扩容成本高(需要重新分配内存)
    • 可能产生空间浪费(分配未使用的空间)

六、典型应用场景

  1. 函数调用栈的实现
  2. 表达式求值(中缀转后缀表达式)
  3. 括号匹配检测
  4. 浏览器的后退操作栈
  5. 撤销/重做功能实现

七、实现注意事项

  1. 内存管理

    • 创建时需分配两次内存(控制结构+数据空间)
    • 销毁时需按相反顺序释放
  2. 临界条件处理

    • 入栈前必须检查栈是否已满
    • 出栈前必须检查栈是否为空
    • 获取栈顶元素前必须检查非空

八、实际应用

1.符号匹配

main 函数

#include "./SeqStack.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int do_check(char *buf, SeqStack *ss, int num)
{
    int col = 1;
    DATATYPE data;

    while (*buf)
    {
        DATATYPE *tmp = NULL;
        bzero(&data, sizeof(data));
        int c = *buf;

        switch (c)
        {
        case '(':
        case '[':
        case '{':
            handle_left_bracket(c, num, col, &data, ss);
            break;

        case ')':
        case ']':
        case '}':
            if (handle_right_bracket(c, num, col, ss))
            {
                return 1;
            }
            break;
        }

        buf++;
        col++;
    }
    return 0;
}

// 封装左括号的处理逻辑
void handle_left_bracket(int c, int num, int col, DATATYPE *data, SeqStack *ss)
{
    data->sym = c;
    data->linenum = num;
    data->colnum = col;
    PushSeqStack(ss, data);
}

// 封装右括号的处理逻辑
int handle_right_bracket(int c, int num, int col, SeqStack *ss)
{
    DATATYPE *tmp = GetTopSeqStack(ss);
    if (tmp == NULL)
    {
        printf("read sym:%c ,line:%d col:%d\n", c, num, col);
        return 1;
    }

    if ((c == ')' && tmp->sym == '(') ||
        (c == ']' && tmp->sym == '[') ||
        (c == '}' && tmp->sym == '{'))
    {
        PopSeqStack(ss);
        return 0;
    }
    else
    {
        printf("read sym:%c ,line:%d col:%d  or top sym:%c ,line:%d col:%d\n", c, num, col, tmp->sym, tmp->linenum, tmp->colnum);
        return 1;
    }
}
int main(int argc, char const *argv[])
{
    SeqStack *ss = CreateSeqStack(5);
    FILE *fp = fopen("./open.c", "r+");
    if (NULL == fp)
    {
        perror("fopen");
        return 1;
    }
    int num = 1;
    int ret = 0;
    while (1)
    {
        char buf[256] = {0};
        if (NULL == fgets(buf, sizeof(buf), fp))
        {
            break;
        }
        ret = do_check(buf, ss, num);
        if(1== ret)
        {
            DestroySeqStack(ss);
            exit(1);
        }
        num++;
        /* code */
    }
    if(IsEmpySeqStack(ss))
    {
        printf("file ok\n");
    }
    else{
        DATATYPE *tmp = GetTopSeqStack(ss);
        printf("top sym:%c ,line:%d col:%d\n", tmp->sym, tmp->linenum, tmp->colnum);
    }
    return 0;
}

2.四则运算(栈)

int applyOperator(int a, int b, char op)
{
    switch (op)
    {
    case '+':
        return a + b;
    case '-':
        return a - b;
    case '*':
        return a * b;
    case '/':
        return a / b;
    default:
        fprintf(stderr, "applyOperator error\n");
        return 0;
    }
}
int precedence(char op)
{
    if (op == '+' || op == '-')
    {
        return 1;
    }
    if (op == '*' || op == '/')
    {
        return 2;
    }
    return 0;
}
int evaluateExpression(char *expression)
{
    SeqStack *operand = CreateSeqStack(100);
    SeqStack *opertor = CreateSeqStack(100);

    int i = 0;
    while (expression[i] != '\0')
    {
        char c = expression[i];
        if (isdigit(c))
        {
            int num = 0;
            while (isdigit(expression[i]))
            {
                num = num * 10 + (expression[i] - '0');
                i++;
            }
            DATATYPE data;
            data.op = '0';
            data.num = num;
            PushSeqStack(operand, &data);
        }
        else if (c == '+' || c == '-' || c == '*' || c == '/')
        {
            while (!IsEmpySeqStack(opertor) && precedence(c) <= precedence(GetTopSeqStack(opertor)->op))
            {
                DATATYPE opData = *GetTopSeqStack(opertor);
                PopSeqStack(opertor);

                DATATYPE bData = *GetTopSeqStack(operand);
                PopSeqStack(operand);

                DATATYPE aData = *GetTopSeqStack(operand);
                PopSeqStack(operand);
                int ret = applyOperator(aData.num, bData.num, opData.op);
                DATATYPE retData;
                retData.op = '0';
                retData.num = ret;
                PushSeqStack(operand, &retData);
            }
            DATATYPE data;
            data.op = c;
            PushSeqStack(opertor, &data);
            i++;
        }
        else
        {
            i++;
        }
    }
    // 处理运算符栈中剩余的运算符
    while (!IsEmpySeqStack(opertor))
    {
        DATATYPE opData = *GetTopSeqStack(opertor);
        PopSeqStack(opertor);

        DATATYPE bData = *GetTopSeqStack(operand);
        PopSeqStack(operand);

        DATATYPE aData = *GetTopSeqStack(operand);
        PopSeqStack(operand);

        int ret = applyOperator(aData.num, bData.num, opData.op);

        DATATYPE retData;
        retData.op = '0';
        retData.num = ret;
        PushSeqStack(operand, &retData);
    }
    int result = GetTopSeqStack(operand)->num;
    DestroySeqStack(operand);
    DestroySeqStack(opertor);

    return result;
}

链式栈(Linked Stack)


一、链式栈核心结构

1. 节点定义
// 数据元素类型
typedef struct person {
    char name[32];
    char sex;
    int age;
    int score;
} DATATYPE;

// 栈节点结构
typedef struct stacknode {
    DATATYPE data;           // 数据域
    struct stacknode *next;  // 指针域
} LinkStackNode;

// 栈管理结构
typedef struct list {
    LinkStackNode *top;      // 栈顶指针
    int clen;                // 当前元素个数
} LinkStack;

二、核心操作实现

1. 创建空栈
LinkStack*CreateLinkStack()
{

    LinkStack* ls = malloc(sizeof(LinkStack));
    if(NULL == ls)
    {
        perror("CreateLinkStack malloc error\n");
        return NULL;
    }

    ls->top = NULL;
    ls->clen = 0;
    return ls;

}
2. 销毁栈
int DestroyLinkStack(LinkStack*ls)
{
     if(NULL == ls )
    {
        fprintf(stderr,"DestroyLinkStack paramter error\n");
        return 1;
    }

    while(!IsEmptyLinkStack(ls))
    {
        PopLinkStack(ls);
    }

    free(ls);
    return 0;
}

三、栈操作实现

1. 入栈操作
int PushLinkStack(LinkStack*ls,DATATYPE*data)
{

    if(NULL == ls || NULL == data)
    {
        fprintf(stderr,"PushLinkStack paramter error\n");
        return 1;
    }
    LinkStackNode* newnode = malloc(sizeof(LinkStackNode));
    if(NULL == newnode)
    {
        perror("PushLinkStack malloc error\n");
        return 1;
    }
    memcpy(&newnode->data,data,sizeof(DATATYPE));
    newnode->next = NULL;

    if(IsEmptyLinkStack(ls))
    {
        ls->top = newnode;
    }
    else 
    {
        newnode->next = ls->top;
        ls->top = newnode;
    }
    ls->clen++;
    return 0;

}
2. 出栈操作
int PopLinkStack(LinkStack*ls)
{
     if(NULL == ls  )
    {
        fprintf(stderr,"PopLinkStack paramter error\n");
        return 1;
    }
    if(IsEmptyLinkStack(ls))
    {
        fprintf(stderr,"PopLinkStack empty\n");
        return 1;
    }
    LinkStackNode* tmp = ls->top;
    ls->top = ls->top->next;
    free(tmp);
    ls->clen--;

    return 0;

}

四、辅助操作实现

1. 判空检测
int IsEmptyLinkStack(LinkStack* ls) {
    return ls->clen == 0;
    // 或 return ls->top == NULL;
}
2. 获取栈顶元素
DATATYPE*GetTopLinkStack(LinkStack*ls)
{
      if(NULL == ls  )
    {
        fprintf(stderr,"GetTopLinkStack paramter error\n");
        return NULL;
    }
    if(IsEmptyLinkStack(ls))
    {
        fprintf(stderr,"GetTopLinkStack empty\n");
        return NULL;
    }

    return &ls->top->data;
}
3. 获取栈大小
int GetSizeLinkStack(LinkStack* ls) {
    return ls->clen;
}

五、链式栈 VS 顺序栈

对比维度链式栈顺序栈
存储结构离散内存节点连续内存空间
容量限制动态扩展固定大小
内存开销每个节点含指针(+8/16字节)无额外开销
入栈操作动态分配内存可能需扩容
缓存友好性较差优秀
实现复杂度需处理指针操作数组索引操作简单

六、典型应用场景

1. 函数调用栈
void funcA() {
    // 保存当前上下文入栈
    funcB();
    // 弹出上下文
}

void funcB() {
    // 新的栈帧入栈
}
2. 表达式求值
// 中缀转后缀表达式算法
while(输入队列不为空) {
    读取token;
    if(是数字) 加入输出队列;
    else if(是运算符) {
        while(栈顶运算符优先级 >= 当前运算符)
            弹出栈顶到输出队列;
        当前运算符入栈;
    }
}
3. 括号匹配检测
bool isValid(char* s) {
    LinkStack *ls = CreateLinkStack();
    while(*s) {
        if(*s == '(' || *s == '[' || *s == '{') {
            PushLinkStack(ls, *s);
        } else {
            if(IsEmptyLinkStack(ls)) return false;
            char top = GetTopLinkStack(ls);
            if((*s == ')' && top != '(') ||
               (*s == ']' && top != '[') ||
               (*s == '}' && top != '{')) return false;
            PopLinkStack(ls);
        }
        s++;
    }
    return IsEmptyLinkStack(ls);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值