数据结构——栈与队列

一、栈的基本概念

栈(Stack)是一种特殊的线性表,其特殊性在于只能在固定的一端进行操作。这种限制使得栈呈现出"后进先出"(LIFO, Last In First Out)的逻辑特性。

栈的核心特点

  • 栈顶:可以进行插入删除的一端
  • 栈底:栈顶的对端,不可操作
  • 入栈(Push):将节点插入栈顶之上
  • 出栈(Pop):将节点从栈顶删除
  • 取栈顶(Top):取得栈顶元素但不出栈

生活中的栈实例

  • 堆叠的盘子(最后放上去的盘子最先被拿走)
  • 弹匣(最后压入的子弹最先射出)
  • 电梯中的人们(最后进入的人最先出来)
  • 函数调用栈(最后调用的函数最先返回)

二、栈的存储形式

栈可以使用两种存储方式实现:

  1. 顺序栈:基于数组实现,使用顺序存储
  2. 链式栈:基于链表实现,使用链式存储

三、栈的基本操作与实现

1. 顺序栈实现

数据结构定义

typedef struct sequential_stack {
    datatype_p data_p;  // 指向顺序栈内存的指针
    int capacity;       // 顺序栈的容量
    int top;            // 顺序栈的栈顶指针
} sq_stack_t, *sq_stack_p;

关键操作

// 初始化顺序栈
sq_stack_p SEQUENTIAL_STACK_Init(int cap_size) {
    sq_stack_p p = malloc(sizeof(sq_stack_t));
    if (p != NULL) {
        p->data_p = malloc(sizeof(datatype) * cap_size);
        p->capacity = cap_size;
        p->top = -1;  // 初始时栈为空
    }
    return p;
}

// 判断栈是否为空
bool SEQUENTIAL_STACK_IfEmpty(sq_stack_p p) {
    return (p->top == -1);
}

// 判断栈是否已满
bool SEQUENTIAL_STACK_IfFull(sq_stack_p p) {
    return (p->top == p->capacity - 1);
}

// 入栈操作
int SEQUENTIAL_STACK_Push(sq_stack_p p, datatype data) {
    if (SEQUENTIAL_STACK_IfFull(p)) return -1;
    p->top++;
    p->data_p[p->top] = data;
    return 0;
}

// 出栈操作
int SEQUENTIAL_STACK_Pop(sq_stack_p p) {
    if (SEQUENTIAL_STACK_IfEmpty(p)) return -1;
    p->top--;
    return 0;
}

2. 链式栈实现

数据结构定义

// 节点结构
typedef struct node {
    datatype data;
    struct node *next_p;
} node_t, *node_p;

// 栈管理结构
typedef struct link_cir_stack {
    node_p top_p;  // 栈顶指针
    int num;       // 元素个数
} lc_stack_t, *lc_stack_p;

关键操作

// 初始化链式栈
lc_stack_p LINK_CIR_STACK_Init(void) {
    lc_stack_p p = malloc(sizeof(lc_stack_t));
    if (p != NULL) {
        p->top_p = malloc(sizeof(node_t));  // 创建头节点
        p->top_p->next_p = p->top_p;        // 循环指向自己
        p->num = 0;
    }
    return p;
}

// 入栈操作(头插法)
void LINK_CIR_STACK_Push(lc_stack_p p, node_p new_node) {
    node_p head_node = p->top_p;
    new_node->next_p = head_node->next_p;
    head_node->next_p = new_node;
    p->num++;
}

四、队列的基本概念

队列(Queue)是另一种特殊的线性表,其特殊性在于只能在固定的两端进行操作:一端插入,另一端删除。这种限制使得队列呈现出"先进先出"(FIFO, First In First Out)的逻辑特性。

队列的核心特点

  • 队头:可以删除节点的一端
  • 队尾:可以插入节点的一端
  • 入队(EnQueue):将节点插入到队尾之后
  • 出队(OutQueue):将队头节点从队列中删除
  • 取队头(Front):取得队头元素但不出队

五、队列的存储形式

队列可以使用两种存储方式实现:

  1. 顺序队列:基于数组实现,使用顺序存储
  2. 链式队列:基于链表实现,使用链式存储

六、队列的基本操作与实现

1. 顺序队列(循环队列)实现

数据结构定义

typedef struct sequential_queue {
    datatype_p data_p;  // 指向顺序队列内存的指针
    int capacity;       // 顺序队列的容量
    int front;          // 队头指针
    int rear;           // 队尾指针
} sq_queue_t, *sq_queue_p;

关键操作

// 初始化顺序队列
sq_queue_p SEQUENTIAL_QUEUE_Init(int cap_size) {
    sq_queue_p p = malloc(sizeof(sq_queue_t));
    if (p != NULL) {
        p->data_p = malloc(sizeof(datatype) * cap_size);
        p->capacity = cap_size;
        p->front = 0;
        p->rear = 0;
    }
    return p;
}

// 判断队列是否为空
bool SEQUENTIAL_QUEUE_IfEmpty(sq_queue_p p) {
    return (p->front == p->rear);
}

// 判断队列是否已满
bool SEQUENTIAL_QUEUE_IfFull(sq_queue_p p) {
    return ((p->rear + 1) % p->capacity == p->front);
}

// 入队操作
int SEQUENTIAL_QUEUE_EnQueue(sq_queue_p p, datatype data) {
    if (SEQUENTIAL_QUEUE_IfFull(p)) return -1;
    p->data_p[p->rear] = data;
    p->rear = (p->rear + 1) % p->capacity;  // 循环利用数组空间
    return 0;
}

2. 链式队列实现

数据结构定义

// 节点结构
typedef struct node {
    datatype data;
    struct node *next_p;
} node_t, *node_p;

// 队列管理结构
typedef struct link_cir_queue {
    node_p front_p;  // 队头指针
    node_p rear_p;   // 队尾指针
    int num;         // 元素个数
} lc_queue_t, *lc_queue_p;

关键操作

// 初始化链式队列
lc_queue_p LINK_CIR_QUEUE_Init(void) {
    lc_queue_p p = malloc(sizeof(lc_queue_t));
    if (p != NULL) {
        node_p head_node = malloc(sizeof(node_t));
        head_node->next_p = head_node;  // 循环指向自己
        p->front_p = head_node;
        p->rear_p = head_node;
        p->num = 0;
    }
    return p;
}

// 入队操作(尾插法)
void LINK_CIR_QUEUE_EnQueue(lc_queue_p p, node_p new_node) {
    p->rear_p->next_p = new_node;
    new_node->next_p = p->front_p;  // 指向头节点,形成循环
    p->rear_p = new_node;
    p->num++;
}

七、栈与队列的对比

特性栈(Stack)队列(Queue)
操作原则LIFO(后进先出)FIFO(先进先出)
操作端仅一端(栈顶)两端(队头删除,队尾插入)
基本操作Push, Pop, TopEnQueue, OutQueue, Front
应用场景函数调用、表达式求值、回溯算法消息队列、任务调度、广度优先搜索

八、常见应用场景

栈的应用

  1. 函数调用栈:记录函数调用关系和局部变量
  2. 表达式求值:中缀表达式转后缀表达式,以及后缀表达式求值
  3. 括号匹配:检查代码中的括号是否正确匹配
  4. 浏览器前进后退:使用两个栈实现网页浏览历史记录

队列的应用

  1. 消息队列:系统间异步通信
  2. 任务调度:CPU调度、打印机任务排队
  3. 广度优先搜索:图中按层次遍历节点
  4. 缓存实现:FIFO缓存淘汰策略

九、学习重点与常见错误

学习重点

  1. 理解栈的LIFO特性和队列的FIFO特性
  2. 掌握顺序和链式两种存储方式的实现
  3. 熟悉基本操作的时间复杂度分析
  4. 理解循环队列如何解决"假溢出"问题

常见错误

  1. 栈空时执行Pop操作:需要先判断栈是否为空
  2. 栈满时执行Push操作:需要先判断栈是否已满
  3. 队列空时执行出队操作:需要先判断队列是否为空
  4. 未正确处理循环队列的边界条件:特别是队满的判断条件

十、总结

栈和队列是两种基础但非常重要的数据结构,它们分别体现了LIFO和FIFO两种不同的数据处理原则。

关键记忆点

  1. 栈是后进先出(LIFO)结构,只能在栈顶进行操作
  2. 队列是先进先出(FIFO)结构,在队尾插入,在队头删除
  3. 两者都可以用顺序或链式方式实现
  4. 循环队列通过取模运算实现数组空间的循环利用
  5. 栈和队列在算法和系统设计中有着广泛的应用

掌握栈和队列的实现原理和操作特性,对于理解更复杂的数据结构和算法具有重要意义。


练习题

  1. 使用栈实现一个简单的表达式求值器
  2. 使用队列实现一个简单的消息队列系统
  3. 比较顺序实现和链式实现的性能差异
  4. 尝试实现一个双栈队列(使用两个栈实现队列功能)
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值