这里写目录标题
- 栈的结构与概念
- 栈底层结构的选取
- 栈的代码实现(stack)
- 头文件(stack.h)
- 栈的初始化
- 栈的销毁
- 入栈
- 出栈
- 获取栈顶数据
- 获取栈大小
- 代码的测试
栈的结构与概念
栈:⼀种特殊的线性表,其只允许在固定的⼀端进行插⼊和删除元素操作。进行数据插入和删除操作的⼀端称为栈顶,另⼀端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。
压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。
出栈:栈的删除操作叫做出栈。出数据也在栈顶。
栈底层结构的选取
数组:
入栈的时间复杂度:O(1)
出栈的时间复杂度:O(1)
单链表:
入栈需要找到最后一个节点,所以要遍历单链表,时间复杂度:O(N)
出栈跟入栈一样,也需要遍历寻找最后一个节点,时间复杂度:O(N)
双向链表:
入栈可以通过哨兵位来找到最后一个节点,时间复杂度:O(1)
出栈的话同理,时间复杂度:O(1)
这样的话就很明显了,数组跟双向链表时比较合适的。
但是因为涉及电脑的缓存问题,所以我们最后还是选择了数组。
电脑数据缓存:就是电脑在访问内存时会缓存连续的空间,这样我们就不用频繁的来访问内存,但是链表的物理逻辑是不一定满足连续空间的,所以相比下来数组就会更好一些,因为对缓存这方面了解较少,所以就不做太多介绍,有兴趣可以去问问度娘哈。
栈的代码实现(stack)
头文件(stack.h)
以下是我们本次要编写的一些函数代码:
#pragma once
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>
typedef int STDataType;
typedef struct Stack
{
STDataType* st;//数组
int top;//栈顶
int capacity;//栈大小
}ST;
void STInit(ST* ps);//栈的初始化
void STDestroy(ST* ps);//栈销毁
void StackPush(ST* ps, STDataType x);//入栈
void StackPop(ST* ps);//出栈
STDataType StackTop(ST* ps);//获取栈顶数据
int STSize(ST* ps);//获取栈的大小
栈的初始化
思路:
给结构体赋一些初值,让指针指向NULL,刚开始还没有数据,所以栈顶以及栈的有效数据都为0。
代码:
void STInit(ST* ps)
{
assert(ps);
ps->st = NULL;
ps->capacity = ps->top = 0;
}
栈的销毁
思路:
因为我们经常会入栈出栈,所以空间是不定的,所以我们就会用realloc来创建连续空间(realloc函数不了解的可以自行了解一下哈,之前已经介绍过了),当我们销毁时就是用free函数释放该空间,然后让结构体成员赋初值即可。
代码:
void STDestroy(ST* ps)
{
assert(ps);
if(ps->st)//判断指针是否位NULL
free(ps->st);
ps->st = NULL;
ps->top = ps->capacity = 0;
}
入栈
思路:
因为我们有个top是指向栈顶的,当我们初始化后,top位0,这个时候我们入栈就直接将数据放入top指向的位置即可,当入栈完后,我们将top++,一直让他指向栈顶的位置。
入栈的空间管理:
因为我们是动态申请空间,所以避免不了数组空间不够的情况。
我们使用三目操作符判断数据空间大小capacity,
当capacity为0,我们就给他赋值为4,当不为0,我们就申请二倍的动态空间。
代码:
void StackPush(ST* ps, STDataType x)
{
assert(ps);
if (ps->top == ps->capacity)
{
ps->capacity = ps->capacity == 0 ? 4 : 2 * (ps->capacity);
STDataType* tmp = (STDataType*)realloc(ps->st, sizeof(int) * ps->capacity);//创建临时指针来暂存新空间地址,防止开辟失败导致原数据丢失
if (tmp == NULL)
{
perror("relloc fail!\n");
exit(1);
}
ps->st = tmp;
}
ps->st[ps->top++] = x;
}
出栈
思路:
结构体的top是一直指向栈顶,我们只需要将指向栈顶的这个数据向下挪动一位就可以了。
但是这里要注意,当栈为NULL时,就不能再出栈了。
//空栈,就是跟初始化结束状态相似,top一定是为0,栈大小不一定为0,
//因为我们一直出栈直到数据清空的话,栈就为空栈,但是出栈时我们不会修改栈空间大小
bool STEmpty(ST* ps)
{
assert(ps);
return ps->top != 0;
}
void StackPop(ST* ps)
{
assert(ps);
assert(STEmpty(ps));//判断是否为空栈
--ps->top;
}
获取栈顶数据
思路:
top是一直指向新栈顶的,我们要获取栈顶的数据就是获取top-1的数据。
具体如图:
代码:
STDataType StackTop(ST* ps)
{
assert(ps);
assert(STEmpty(ps));
return ps->st[ps->top-1];
}
获取栈大小
思路:
就是获取结构体capacity的值。
代码:
int STSize(ST* ps)
{
assert(ps);
return ps->capacity;
}
当然获取有效数据个数也很简单,就是返回top的值。
代码的测试
#define _CRT_SECURE_NO_WARNINGS 1
#include"stack.h"
void test()
{
ST s;
STInit(&s);
StackPush(&s,1);
StackPush(&s, 2);
StackPush(&s, 3);
StackPush(&s, 4);
StackPush(&s, 4);
while (s.top)
{
int tmp=StackTop(&s);
printf("%d ", tmp);
StackPop(&s);
}
STDestroy(&s);
}
int main()
{
test();
return 0;
}
入栈出栈:
销毁:
----------------------------------------------------------------分隔符
感谢观看,制作不易,看官老爷们给个三连吧,谢谢!