栈的介绍
这是某城市大街的一张快照,尽管它看起来并不清晰并且似乎历史久远,但我们也想利用它来说明一些问题。
现在我们让这条大街大堵车,所有车都无法动弹。
在前几次的介绍中,我们接触到的线性结构都和这条静止的大街类似,以前做的删除、添加等操作就好像你是指挥交通的,对这些相对静态的车辆进行管理,即使是链表。
接下来,我们要看的结构,就会模拟汽车们自己在大街上的运行过程,比如栈和队列。
而栈(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
好的,有关栈的介绍就到这里,我们下次来看看“队列”。