一、栈的概念
栈是一种特殊的线性表,只允许在栈的一端入数据和出数据,这一端称为栈顶,另一端称为栈底。
入数据时称为入栈或进栈或压栈,出数据时(删除数据)称为出栈。
在进出数据时,需要遵照先进后出,后进先出的规则。
比如
出栈顺序是5,4,3,2,1。但不一定就是5,4,3,2,1。
比如入一个数据就出一个数据,那么出栈顺序就变成了1,2,3,4,5。
二、实现栈
实现一个栈可以用数组也可以用链表,使用数组时,在数组尾插入数据,消耗小。因此选择使用数组实现栈,实现队列时会使用链表。
栈的结构体
typedef int STDataType; //方便对数据类型进行修改
typedef struct StackNode
{
STDataType* arr; //数组
int capacity; //最大容量
int top; //栈顶数据的下一个位置
}STNode;
栈的一些操作包括入栈,出栈,获取栈顶元素,获取栈的大小,判空,初始化和销毁等。
1、初始化和销毁
在初始化时,top的值可以为-1或者0,如果是-1,说明top位置处是栈顶。
如果是0,说明top位置处是栈顶位置的下一个。对于top初始化的不同,其他的接口的代码实现也不同,这里选择初始化为0;
//初始化顺序栈
void StackInit(STNode* ps)
{
assert(ps);
ps->arr = NULL;
ps->capacity = 0;
ps->top = 0;
}
//销毁栈
void StackDestroy(STNode* ps)
{
assert(ps);
free(ps->arr);
ps->arr = NULL;
ps->capacity = ps->top = 0;
}
2、入栈
入栈非常简单,因为top是指向栈顶位置的下一个,那么入栈就是将数据放在top处,在++top即可。
//入栈
void StackPush(STNode* ps, STDataType x)
{
assert(ps);
//没有空间或者空间满了,先扩容
if (ps->capacity == ps->top)
{
int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
STDataType* tmp = (STDataType*)realloc(ps->arr,sizeof(STNode) * newcapacity);
if (tmp == NULL)
{
perror("malloc()");
return;
}
ps->arr = tmp;
ps->capacity = newcapacity;
}
//插入数据
ps->arr[ps->top++] = x;
}
其中由于使用的是数组,可能存在空间不够的情况,因此需要判断是否需要扩容。
3、出栈
出栈就是删除栈顶元素,让我们不能当前栈顶,那么修改top就可以做到
//出栈
void StackPop(STNode* ps)
{
assert(ps);
assert(!StackEmpty(ps));
ps->top--;
}
4、获得栈顶数据
top是指向栈顶数据的下一个,那么–top后top位置处就是栈顶数据。
//获得栈顶元素
int FindTop(STNode* ps)
{
assert(ps);
assert(!StackEmpty(ps));
return ps->arr[ps->top - 1];
}
获得栈顶元素前提是栈内需要有数据。
5、判空
判空只需要判断top是否等于-1或者0,那么代码也是很简单。
//判断栈空
bool StackEmpty(STNode* ps)
{
return ps->top == 0;
}
返回这个表达式的真假即可。4
6、获得栈的大小
因为我们在结构体内定义了capacity这个成员,因此这个接口也很容易实现
//获得栈的大小
int StackSize(STNode* ps)
{
assert(ps);
return ps->capacity;
}
三、完整代码
依旧是使用多个文件编写。
stack.h
#pragma once
#include<stdio.h>
#include<stdbool.h>
#include<assert.h>
#include<stdlib.h>
typedef int STDataType;
typedef struct StackNode
{
STDataType* arr; //数组
int capacity; //最大容量
int top; //栈顶数据的下一个位置
}STNode;
//初始化
void StackInit(STNode* ps);
//销毁
void StackDestroy(STNode* ps);
//入栈
void StackPush(STNode* ps, STDataType x);
//出栈
void StackPop(STNode* ps);
//判断栈空
bool StackEmpty(STNode* ps);
//获得栈顶元素
int FindTop(STNode* ps);
//栈的大小
int StackSize(STNode* ps);
stack.c
#define _CRT_SECURE_NO_WARNINGS 1
#include"stack.h"
//初始化顺序栈
void StackInit(STNode* ps)
{
assert(ps);
ps->arr = NULL;
ps->capacity = 0;
ps->top = 0;
}
//销毁栈
void StackDestroy(STNode* ps)
{
assert(ps);
free(ps->arr);
ps->arr = NULL;
ps->capacity = ps->top = 0;
}
//入栈
void StackPush(STNode* ps, STDataType x)
{
assert(ps);
//没有空间或者空间满了,先扩容
if (ps->capacity == ps->top)
{
int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
STDataType* tmp = (STDataType*)realloc(ps->arr,sizeof(STNode) * newcapacity);
if (tmp == NULL)
{
perror("malloc()");
return;
}
ps->arr = tmp;
ps->capacity = newcapacity;
}
//插入数据
ps->arr[ps->top++] = x;
}
//出栈
void StackPop(STNode* ps)
{
assert(ps);
assert(!StackEmpty(ps));
ps->top--;
}
//判断栈空
bool StackEmpty(STNode* ps)
{
return ps->top == 0;
}
//获得栈顶元素
int FindTop(STNode* ps)
{
assert(ps);
assert(!StackEmpty(ps));
return ps->arr[ps->top - 1];
}
//获得栈的大小
int StackSize(STNode* ps)
{
assert(ps);
return ps->capacity;
}
关于栈的介绍就到这里了 ,下篇会使用链表实现一个队列。