【数据结构】03栈(C&Python)

栈的介绍

这是某城市大街的一张快照,尽管它看起来并不清晰并且似乎历史久远,但我们也想利用它来说明一些问题。

现在我们让这条大街大堵车,所有车都无法动弹。

在前几次的介绍中,我们接触到的线性结构都和这条静止的大街类似,以前做的删除、添加等操作就好像你是指挥交通的,对这些相对静态的车辆进行管理,即使是链表。

接下来,我们要看的结构,就会模拟汽车们自己在大街上的运行过程,比如栈和队列。

(Stack),是一种只允许在一段进行插入或者删除操作的线性表。就好似这是一条“断头路”,小汽车们开进来,只能原出口返回。

在此,我们先说明一些简单的概念:

栈顶:线性表允许进行插入和删除操作的一端

栈底:线性表允许进行插入和删除操作的一端

空栈:不含任何元素

栈的操作特性:先进后出

n个不同元素进栈,出栈元素不同的排列个数为

栈的基本语句实现

和我们前两次研究的顺序表一样,我们的“栈”,也有两种存储方式。

首先,我们来尝试提供顺序栈的存储定义实现。这个任务基本对现在的我们来说无压力。

#include <stdbool.h>
#define MAX_SIZE 100
typedef int ElementType;

typedef struct {
    ElementType data[MAX_SIZE];  // 使用数组存储栈中元素
    int top;  // 栈顶元素的下标
} SeqStack;

对于C语言的顺序表,初始化栈顶指针是构造一个空栈的关键步骤。

void initStack(SeqStack *stack) {
    stack->top = -1;  // 初始化栈顶指针为-1,表示栈为空
}

紧接着,我们来利用栈顶指针来判断栈是否为空,即我们常说的判空栈

bool isEmpty(SeqStack *stack) {
    return stack->top == -1;
}

进栈(或称为压栈)操作将一个新元素放置到栈顶。我们这里一起提供进栈和出栈的代码。在栈的操作中,push的反义词往往是pop,也就是压栈和出栈。

bool push(SeqStack *stack, ElementType item) {
    if (stack->top == MAX_SIZE - 1) {
        return false;  // 栈满,无法插入
    }
    stack->data[++stack->top] = item; // 先将栈顶指针增加1,再存放新元素
    return true;
}
bool pop(SeqStack *stack, ElementType *item) {
    if (isEmpty(stack)) {
        return false;  // 栈为空,无法出栈
    }
    *item = stack->data[stack->top--]; // 返回栈顶元素,并将栈顶指针减1
    return true;
}

最后我们来读栈元素。

bool peek(SeqStack *stack, ElementType *item) {
    if (isEmpty(stack)) {
        return false;  // 栈为空
    }
    *item = stack->data[stack->top];
    return true;
}

通过上面的研究,我们发现在C语言中,管理栈顶指针top的值是保证栈操作正确性的不二法门。在Python中,我们利用列表的动态性质,可以更直观地实现栈的操作,但仍需注意不超出设定的容量。我们还是用面向对象编程来编写类和函数。

class SeqStack:
    def __init__(self, capacity=100):
        self.stack = []
        self.capacity = capacity

def is_empty(self):
    return len(self.stack) == 0


def push(self, item):
    if len(self.stack) >= self.capacity:
        return False  # 栈满,无法插入
    self.stack.append(item)
    return True

def pop(self):
    if self.is_empty():
        return None  # 栈为空,无法出栈
    return self.stack.pop()

def peek(self):
    if self.is_empty():
        return None  # 栈为空
    return self.stack[-1]

共享栈

共享栈,这个名字似乎不太清晰明了。我这样给大家解释下,我们一般研究的栈是一条射线的话,共享栈就是一条线段。共享栈让两个顺序栈共享一个一维数组空间,这样我们可以更有效地利用存储空间。如图示意,我们先叫他们0号栈和1号栈。

top0 = -1时,0号栈为空,

top1 = MaxSize时,1号栈为空,

top1和top0相邻的时候(top1 - top0 =1),栈满。

我们依此来用C语言来构造一个共享栈及其基本操作。

#include <stdio.h>
#include <stdbool.h>
#define MaxSize 100  // 定义栈的最大容量

typedef struct {
    int data[MaxSize];  // 用数组存储栈中的元素
    int top0;  // 栈0的栈顶指针
    int top1;  // 栈1的栈顶指针
} ShrdStack;

// 初始化共享栈
void initShrdStack(ShrdStack *stack) {
    stack->top0 = -1;  // 栈0为空时的栈顶指针
    stack->top1 = MaxSize;  // 栈1为空时的栈顶指针
}

// 判断栈0是否为空
bool isEmpty0(ShrdStack *stack) {
    return stack->top0 == -1;
}

// 判断栈1是否为空
bool isEmpty1(ShrdStack *stack) {
    return stack->top1 == MaxSize;
}

bool isFull(ShrdStack *stack) {
    return stack->top0 + 1 == stack->top1;
}

栈的链式结构

有顺序栈,也就必然有链栈

链栈的每个节点包含两个部分:数据域和指向下一个节点的指针域。我们规定所有操作在单链表的表头进行,并且没有头结点,Lhead指向栈顶元素。

下面分别用C语言和Python语言来实现链栈的定义。

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

typedef int ElementType;

typedef struct StackNode {
    ElementType data;
    struct StackNode* next;
} StackNode, *LinkStackPtr;

typedef struct LinkStack {
    LinkStackPtr top;
    int count;
} LinkStack;

// 初始化栈
void initStack(LinkStack *s) {
    s->top = NULL;
    s->count = 0;
}
class StackNode:
    def __init__(self, data):
        self.data = data
        self.next = None

class LinkStack:
    def __init__(self):
        self.top = None
        self.count = 0

 好的,有关栈的介绍就到这里,我们下次来看看“队列”。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值