数据结构——栈与队列

一、栈的基本概念

栈(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. 尝试实现一个双栈队列(使用两个栈实现队列功能)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值