算法与数据结构之栈、队列

一、栈

(一)什么是栈

栈是一种特殊的线性表,它只能先进后出,即LIFO,即栈只有一个出入口,先进入的元素被压在底部,而新进入的元素在顶部,最先弹出,在汇编语言中,使用push入栈,使用pop出栈。栈分为顺序栈和链栈两种,下面以顺序栈为例说明。

上溢:栈满时再做进栈运算(一种出错状态,应设法避免)。

下溢:栈空时再做退栈运算将产生溢出。

c语言实现:

c 复制代码
#include <stdio.h>
#include <malloc.h>
/* 栈的定义:首先,我们需要定义一个栈的结构体。栈包含三个部分:数据数组 data、栈顶指针 top 和栈底指针 bottom。*/
typedef struct {
    char data[100];
    int top;
    int bottom;
} stack;
/*创建栈:接下来,我们需要为栈分配内存空间,并初始化栈顶和栈底指针。*/
stack *StackInit()
{
    stack *p = (stack*)malloc(sizeof(stack)); // 分配新空间
    if (p == NULL) // 分配失败
        return 0;
    p->bottom = p->top = 0; // 分配成功
    return p;
}
/*入栈操作:入栈操作是将元素添加到栈顶。*/
void Push(stack *p, char item)
{
    p->data[p->top] = item; // 存入栈中
    p->top++; // 栈顶指针加1
}
/*出栈操作:出栈操作是从栈顶移除元素。*/
char Pop(stack *p, char item)
{
    if (p->top != p->bottom) { // 栈非空
        item = p->data[p->top - 1]; // 栈顶内容输出
        p->top--; // 栈顶减1
        return item;
    }
}
/*遍历栈:遍历栈是输出栈内所有元素。*/
void Seek(stack *p)
{
    while (p->top != p->bottom) {
        printf("%c", p->data[p->top - 1]);
        p->top--;
    }
}
int main()
{
    char str;
    stack *p; // 定义栈名
    p = StackInit(); // 创建栈
    Push(p, 'b'); // 将字符压入栈中
    str = Pop(p, str); // 取出栈顶内容
    printf("%c\n", str); // 输出栈顶内容
    return 0;
}

python实现:

python 复制代码
class Stack(object):
    def __init__(self):          #初始化
        self.items=[]
    def is_empty(self):       #判断栈是否为空
        return self.items==[]
    def push(self,item):             # 加入元素
        self.items.append(item)
    def pop(self):                #弹出元素
        return self.items.pop()
    def peek(self):             # 返回栈顶元素
        return self.items[len(self.items)-1]
    def size(self):                #返回栈的大小
        return len(self.items)
if __name__ == "__main__":
    stack = Stack()
    print(stack.is_empty())
    print(stack.size())
    stack.push(1)
    print(stack.peek())
    stack.push(2)
    print(stack.peek())
    stack.push(3)
    print(stack.peek())
    stack.pop()
    print(stack.peek())
    stack.pop()
    print(stack.peek())
    stack.pop()
    print(stack.is_empty())
    print(stack.size())

(二)常见用途

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

原理解析

这个转换过程通常被称为"调度场算法",其核心在于利用栈来暂存运算符并处理优先级。当我们从左到右扫描中缀表达式时,遇到数字直接输出;遇到运算符时,需要将其与栈顶运算符比较优先级:如果当前运算符优先级小于或等于栈顶运算符,就将栈顶运算符弹出并输出,直到当前运算符优先级高于栈顶或栈为空,再将当前运算符压入栈中。括号的处理比较特殊:左括号直接入栈作为边界,遇到右括号则不断弹出栈顶元素直到遇到左括号为止,从而保证括号内的运算优先被输出。

Python 代码实现

python 复制代码
def infix_to_postfix(expression):
    # 定义运算符优先级
    precedence = {'+': 1, '-': 1, '*': 2, '/': 2, '^': 3}
    stack = []
    output = []

    for char in expression:
        # 如果是操作数(字母或数字),直接加入输出
        if char.isalnum():
            output.append(char)
        # 如果是左括号,压入栈
        elif char == '(':
            stack.append(char)
        # 如果是右括号,弹出直到遇到左括号
        elif char == ')':
            while stack and stack[-1] != '(':
                output.append(stack.pop())
            stack.pop() # 弹出左括号,但不输出
        # 如果是运算符
        else:
            while (stack and stack[-1] != '(' and 
                   precedence.get(stack[-1], 0) >= precedence.get(char, 0)):
                output.append(stack.pop())
            stack.append(char)

    # 将栈中剩余的运算符全部弹出
    while stack:
        output.append(stack.pop())

    return ''.join(output)

# 测试
expr = "(A+B)*C"
print(f"中缀: {expr} -> 后缀: {infix_to_postfix(expr)}")
# 输出: ABC*+
  1. 符号检测(括号匹配)

原理解析

这是栈最直观的"后进先出"应用场景,用于检查代码或数学表达式中的括号是否正确闭合且嵌套有序。算法逻辑非常清晰:遍历字符串,每当遇到一个"左括号"(如 (, [, {),就将其压入栈中,表示期待后续有一个对应的右括号;每当遇到一个"右括号",就从栈顶弹出一个左括号进行比对,如果类型不匹配(例如栈顶是 [ 但遇到了 ))或者栈已空(说明右括号多了),则判定为不合法;遍历结束后,如果栈为空,说明所有左括号都被正确匹配了。

Python 代码实现

python 复制代码
def is_balanced(expression):
    stack = []
    # 定义括号映射关系
    mapping = {')': '(', ']': '[', '}': '{'}

    for char in expression:
        # 如果是左括号,压入栈
        if char in mapping.values():
            stack.append(char)
        # 如果是右括号,进行检查
        elif char in mapping.keys():
            # 如果栈为空,或者弹出的栈顶元素不匹配,则返回 False
            if not stack or stack.pop() != mapping[char]:
                return False

    # 最后栈必须为空,才算完全匹配
    return len(stack) == 0

# 测试
print(is_balanced("{}"))  # True
print(is_balanced("{[(])}"))  # False
  1. 递归算法的使用

原理解析

虽然我们在写代码时看到的是函数自我调用,但在计算机底层,递归完全依赖于"系统调用栈"来实现。每当函数调用自身时,系统会将当前的局部变量、参数和返回地址压入栈中(这一过程叫"递"),随着递归深入,栈帧不断堆积;当满足终止条件时,函数开始返回,系统从栈顶逐层弹出栈帧,恢复上一层的现场继续执行(这一过程叫"归")。如果递归层数过深超过了栈的内存限制,就会发生"栈溢出",因此理解栈对于编写安全的递归代码至关重要。

Python 代码实现(以计算阶乘为例):

python 复制代码
def factorial(n):
    # 终止条件:防止无限递归(栈溢出)
    if n == 1:
        return 1
    else:
        # 递推过程:每一层调用都会压入系统栈
        # 直到 n=1 触底,然后开始回归(弹栈计算)
        return n * factorial(n - 1)

# 测试
# 调用 factorial(3) 的栈过程:
# 1. factorial(3) 压栈 -> 等待 3 * factorial(2)
# 2. factorial(2) 压栈 -> 等待 2 * factorial(1)
# 3. factorial(1) 压栈 -> 返回 1
# 4. factorial(2) 弹栈 -> 计算 2 * 1 = 2
# 5. factorial(3) 弹栈 -> 计算 3 * 2 = 6
print(factorial(5)) # 输出: 120

二、队列

队列是一种两端开口的线性表,一头出口,一头入口,遵循先入先出,即FIFO,即最先进入的元素,在低端最先离开队列,类似于买票,站在前面的人先买到票。

c语言示例:

c 复制代码
#define MaxSize 50
typedef int ElemType;
typedef struct {
   ElemType data[MaxSize];
   int front;
   int rear;
} SeqQueue;
void InitQueue(SeqQueue &q) {//创建队列
   q.front = q.rear = 0;
}
bool EnQueue(SeqQueue &q, ElemType x) {//添加队列元素
   if ((q.rear + 1) % MaxSize == q.front) {
       return false; // 队列满
   }
   q.data[q.rear] = x;
   q.rear = (q.rear + 1) % MaxSize;
   return true;
}
bool DeQueue(SeqQueue &q, ElemType &x) {//删除元素
   if (q.rear == q.front) {
       return false; // 队列空
   }
   x = q.data[q.front];
   q.front = (q.front + 1) % MaxSize;
   return true;
}
bool QueueEmpty(SeqQueue &q) {//判断是否为空队列
   return q.rear == q.front;
}

python语言示例:

python 复制代码
class Queue(object):
    def __init__(self):
        self.items=[]
    def is_empty(self):                #判断队列是否为空
        return self.items==[]
    def enqueue(self,item):             #入队列
        self.items.insert(0,item)
    def dequeue(self):                 #出队列
        return self.items.pop()
    def size(self):                     #队列元素个数
        return len(self.items)
if __name__ == "__main__":
    queue = Queue()
    print(queue.is_empty())
    print(queue.size())
    queue.enqueue(1)
    queue.enqueue(2)
    queue.enqueue(3)
    print(queue.dequeue())
    print(queue.dequeue())
    print(queue.dequeue())
    print(queue.is_empty())
print(queue.size())
相关推荐
写代码写到手抽筋2 小时前
线性插值与Sinc插值的数学原理及实战
算法
码农学院2 小时前
解决越南语PDF导出乱码问题
数据结构·pdf
孤飞10 小时前
zero2Agent:面向大厂面试的 Agent 工程教程,从概念到生产的完整学习路线
算法
技术专家11 小时前
Stable Diffusion系列的详细讨论 / Detailed Discussion of the Stable Diffusion Series
人工智能·python·算法·推荐算法·1024程序员节
csdn_aspnet11 小时前
C# (QuickSort using Random Pivoting)使用随机枢轴的快速排序
数据结构·算法·c#·排序算法
鹿角片ljp12 小时前
最长回文子串(LeetCode 5)详解
算法·leetcode·职场和发展
广师大-Wzx13 小时前
一篇文章看懂MySQL数据库(下)
java·开发语言·数据结构·数据库·windows·python·mysql
paeamecium13 小时前
【PAT甲级真题】- Cars on Campus (30)
数据结构·c++·算法·pat考试·pat
chh56314 小时前
C++--模版初阶
c语言·开发语言·c++·学习·算法