目录
结语:栈是基于顺序表或者链表就可以实现的超简单数据结构,但是其LIFO的强大功能,又使在一些功能实现中发挥着极大的作用。
1.栈的概述
1.1 什么是栈
栈是一种特殊的线性表,只允许在表的某一端进行数据的插入删除。
栈规定,将进行数据插入删除的这一端称为“栈顶”,另一端则称为“栈底”。
1.2 栈的数据性质:LIFO
由于栈只能于栈顶一端进行数据的插入和删除,栈中的数据一定遵守先进后出的原则。
可以类比一摞碗进行理解,因为只能在上方放碗,所以最先取走的一定是最后放置的,最先放置的碗一定是最后被取走的。
1.3 栈的基本操作
push(进栈/压栈/入栈):都是栈的数据插入操作,插入数据只能在栈顶。
pop (出栈 ):栈的数据删除操作,删除数据只能在栈顶 。
图示: push操作和pop操作
、
**注意:栈顶是固定的一端, 但栈顶元素top是在栈内流动的,遵循先进后出原则。**
2.栈的实现
前言:栈的实现可以有两种形式:顺序表和链表。我们可以把栈和栈的接口理解为一个插入删除受限的顺序表或链表。
以顺序表为例:只需要实现一个只能在某一端插入数据的顺序表和顺序表接口,就实现了栈。
2.1 栈的声明
使用顺序表来实现栈,栈的成员与顺序表基本相同,只需要添加一个top变量用以维护栈只能在一端删插数据的功能。top是int类型。
typedef struct stack ST;
struct stack
{
STtype* a;//该栈的指针
int top;//栈顶(下标)
//top指向栈顶元素(要初始化为-1,因为此时没有真正的栈顶元素)
//这里初始化为-1,在后续的++top 入栈就能直接使用了
//同时top+1==size(stack)
//top==capacity时候才扩容
//初始化为0,那么无论栈空,还是只有一个元素,top都为0。
//同时top指向栈顶的下一个元素
//top-1==capacity的时候就要扩容了
int capacity;//顺序表为底层,表最大容量。
};
2.2栈的初始化和销毁
声明栈后,我们可以对栈进行初始化:也就是开辟一个栈,并标记维护栈用的两个成员变量:top和capacity。在使用结束,想要销毁一个栈时,可以进行栈的销毁:释放一个栈同时将指针置空,并标记维护栈用的两个变量。
**注意:这两个函数接口都不包括stack变量本身的申请和释放,需要初始化前自行malloc开辟空间,销毁后自行free掉空间(并置空stack指针)。**
//初始化
void InitST(ST* st)
{
assert(st);//判空
st->capacity = 20;
st->a = (STtype*)malloc((st->capacity )* sizeof(STtype));
st->top = -1;
}
//销毁
void DestroyST(ST* st)
{
assert(st);//判空
free(st->a);
st->a = NULL;
st->top = -1;
st->capacity=0;
}
2.3 入栈和出栈(增删)
栈的增删只能在某一端实现,即栈顶增删。这个特性使我们在声明栈时加入变量top(可以理解为栈顶元素的下标)。
通过对top的维护,实现栈单端的数据增删。
**栈的底层使用顺序表,所以需要考虑扩容问题。**
//扩容
ST* ReallocST(ST* st)
{
STtype* tmp = (STtype*)realloc(st->a, ((st->capacity) + 10) * sizeof(STtype));
//先赋值给tmp,避免realloc失败
assert(tmp);
st->a = tmp;
st->capacity += 10;//更新栈最大容量
return st;
}
//数据入栈
void PushST(ST* st,STtype data)
{
if (st->top == (st->capacity) - 1)
{
st = ReallocST(st);//判断是否需要扩容
}
st->top += 1;//top上移
STtype* tmp = (st->a) + (st->top);
*tmp = data;
}
//数据出栈
void PopST(ST* st)
{
st->top -= 1;
}
数据入栈:先判断是否需要扩容,然后将top加1得到新插入的栈顶元素下标,赋值。
数据出栈:直接将top-1,将要删除的数据的前一个元素标记为栈顶元素。这里不是实质性地删除了栈中的数据,只需要更换top的指向就能实现栈的删除功能。再下一次top指向这个被删除过的位置时候,就可以直接进行数据覆盖。是很巧妙的算法。
2.4栈的判空
已知top表示栈元素的下标,那么只有当top=-1时,栈为空栈。因为这里是为了测试栈的性质,最好用assert判断栈是否存在。
返回一个bool类型,用%d打印,为0则不是空的,为1则是空栈。
//判断栈是否为空
bool empty(ST* st)
{
assert(st);
if (st->top == -1)
{
return true;
}
else
return false;
}
2.5 栈的数据数量
用top反应,直接返回top+1
//获取数据个数
int size(ST* st)
{
int ret = (st->top) + 1;
return ret;
}
3.完整码源(含测试函数)
//头文件
typedef int STtype;
typedef struct stack ST;
struct stack
{
STtype* a;//该数据结构的指针
int top;//栈顶(下标)
int capacity;
};
void InitST(ST* st);
void DestroyST(ST* st);
STtype Top(ST* st);
void PushST(ST* st, STtype data);
void PopST(ST* st);
bool empty(ST* st);
int size(ST* st);
//stack.c 函数实现
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
#include"stack.h"
void InitST(ST* st)
{
assert(st);//判空
st->capacity = 20;
st->a = (STtype*)malloc((st->capacity )* sizeof(STtype));
st->top = -1;
//直接初始化栈的空间 栈顶 大小
}
//销毁
void DestroyST(ST* st)
{
free(st->a);
st->a = NULL;
st->top = -1;
st->capacity=0;
}
//扩容
ST* ReallocST(ST* st)
{
STtype* tmp = (STtype*)realloc(st->a, ((st->capacity) + 10) * sizeof(STtype));
//先赋值给tmp,避免realloc失败
assert(tmp);
st->a = tmp;
st->capacity += 10;//更新栈最大容量
return st;
}
//获取栈顶的数据
STtype Top(ST* st)
{
return *((st->a) + (st->top));
}
//数据入栈
void PushST(ST* st,STtype data)
{
if (st->top == (st->capacity) - 1)
{
st = ReallocST(st);//判断是否需要扩容
}
st->top += 1;//top上移
STtype* tmp = (st->a) + (st->top);
*tmp = data;
}
//数据出栈
void PopST(ST* st)
{
st->top -= 1;
}
//判断栈是否为空
bool empty(ST* st)
{
assert(st);
if (st->top == -1)
{
return true;
}
else
return false;
}
//获取数据个数
int size(ST* st)
{
int ret = (st->top) + 1;
return ret;
}
//test.c
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
#include"stack.h"
void test01(ST*test)
{
printf("测试初始化和入栈\n");
InitST(test);
PushST(test,1);
PushST(test,2);
for (int i = 0; i < test->capacity; i++)
{
printf("%d->",*((test->a)+i));
}
printf("\n");
}
void test02(ST*test)
{
printf("测试出栈\n");
PopST(test);
for (int i = 0; i < test->capacity; i++)
{
printf("%d->", *((test->a) + i));
}
printf("\n");
PopST(test);
for (int i = 0; i < test->capacity; i++)
{
printf("%d->", *((test->a) + i));
}
printf("\n");
PushST(test,10);
for (int i = 0; i < test->capacity; i++)
{
printf("%d->", *((test->a) + i));
}
printf("\n");
PushST(test,10);
for (int i = 0; i < test->capacity; i++)
{
printf("%d->", *((test->a) + i));
}
}
void test03(ST* st)
{
printf("\n测试判空\n");
bool a = empty(st);
printf("栈现在为空:%d",a);
}
void test04(ST* st)
{
printf("\n测试返回top\n");
STtype a = Top(st);
printf("栈顶元素现在是:%d",a);
}
void test05(ST* st)
{
printf("\n测试销毁\n");
DestroyST(st);
printf("%p\n", st->a);
printf("%d\n", st->capacity);
printf("%d\n", st->top);
}
int main()
{
ST* test = (ST*)malloc(sizeof(ST));
test01(test);
test02(test);
test03(test);
test04(test);
test05(test);
free(test);
test = NULL;
return 0;
}