在刚学习完C语言后,我设计了个程序实现10进制向n进制转换,我发现只能将其最终结果逆序输出。想正序输出一直没有实现。但是当我了解到了栈结构的时候豁然开朗。什么是栈结构?我的理解就是它就像是一个没盖子的瓶子,只能从一端进出。所以就有了先进去的后出来,后进去的先出来的说法。
栈 分为 顺序栈 和 链栈:
1. 顺序栈
和顺序表一样,这个线性结构实质上就是一段线型空间。只不过对这段线性空间进行了限制,而且多了些修饰这段空间的指针。
首先来看一个顺序栈应该包含哪些内容
typedef struct SeqStack
{
int top; //记录栈顶下标
int* base;//指向栈底的指针
int capacity;//当前已经分配的存储空间
}SeqStack;
顺序栈的初始化
void SeqStackInit(SeqStack* pst,int sz)//sz是你要申请空间的大小
{
pst->capacity = sz > SeqStack_Dafult_Size ? sz:SeqStack_Dafult_Size;
pst->base = (int*)malloc(sizeof(SeqStack)*pst->capacity);
pst->top = 0;
}
还有种初始化的方法就是可以减少一个参数
SeqStack* SeqStackCreate(int sz)//创建链表方法2
{
SeqStack* pst = (SeqStack*)malloc(sizeof(SeqStack));//先让pst指向一个顺序栈
pst->capacity = sz > SeqStack_Dafult_Size ? sz : SeqStack_Dafult_Size;
pst->base = (int*)malloc(sizeof(pst->capacity)*pst->capacity);
pst->top = 0;
return pst;
这里面有个问题就是top初始值的问题,在人民邮电版本和清华大学的数据结构教材中top初始值均为-1,我们这里采取0;
区别:
在放入元素的时候初始值为0的情况先放入元素,后指针后移,
top初始值为-1的情况先指针后移,再放入元素。
出栈,原来的栈顶元素被删掉,由下一个顶替。 取栈顶元素,只是获取栈顶元素的值,不删除该元素
接下来我们来看栈中最常见的操作,取栈顶元素
int SeqStackTop(SeqStack* pst)
{
if (SeqStackIsEmpty(pst))
{
printf("栈为空,无法取栈顶元素");
return;
}
return pst->base[pst->top - 1];
}
下面再看插入元素
void SeqStackPush(SeqStack *pst, DataType x)
{
if(SeqStackFull(pst))
{
printf("栈已满,%d 不能入栈.\n", x);
return;
}
pst->base[pst->top++] = x;
}
删除元素
void SeqStackPop(SeqStack* pst)
{
if (SeqStackIsEmpty(pst))
printf("栈空间已空,不能继续删除");
pst->top--;
}
展示
void SeqStackShow(SeqStack* pst)
{
for (int i = pst->top - 1; i > 0; i--)
{
printf("%d ", pst->base[i]);
}
}
销毁
void SeqStackDestroy(SeqStack* pst)
{
free(pst);
free(pst->base);
}
需要强调的是销毁栈空间,不但要销毁这个空间,还要销毁指向这个空间的指针,这就是为什么看到2次free。
这样,一个完整的顺序栈就操作完成
接下来我们来看链栈
链栈 实际上就是链表加了些限定条件,但是少了很多限制(比如说链栈不存在栈满问题)反而比起链表还简单了一些,但是链栈也有很多难点,比如说怎么搭建框架。栈的数据结构操作起来相对链表来说比较简单,但是搭建栈实现栈的思想比较复杂(经常会用到二级指针),我们一起来看。
我们在创建链表的时候经常创建指向节点的指针,栈是要用到指向链表的指针,即指向节点指针的指针。也就是二级指针。
这里补充一个小知识点:指针
int a = 5;
int* p;
*p = &a;
通过简单的代码监视有无*的区别,
可以看到p是a的地址,而p有自己的地址,p的地址类型就会变成int**类型,但是* p的类型是int 类型。所以,到底加不加 (星号)取决于你要进行的操作,不要混为一谈。
再补充一点 运算符优先级
这几个运算符优先级别非常高,所以要进行操作时注意什么时候打括号,否则会出现很多问题。
首先来看栈的初始化
void ListStackInit(ListStack *pst)
{
*pst = NULL;//初始化将栈赋空
}
我们来看链栈最复杂的部分 插入
插入的时候由于链栈的限制只能是头插
void ListStackPush(ListStack* pst, int x)
{
StackNode* node = (StackNode*)malloc(sizeof(StackNode));
assert(node);
node->data = x;
node->next = *pst;//结点连接栈
*pst = node;
}
链栈删除栈顶
void ListStackPop(ListStack* pst)
{
ListStack P = (*pst);//
*pst = (*pst)->next;
free(P);
}
链栈取栈顶元素
int ListStackTop(ListStack *pst)
{
assert(*pst == NULL);
return false;
return(*pst)->data;
}
链栈展示函数
void ListStackShow(ListStack pst)
{
StackNode* p = pst;
while (p != NULL)
{
printf("%d ",p->data);
p = p->next;
}
}
以上就是数据结构中栈的基本操作。虽说结构不复杂,但是牵扯到二级指针问题,什么时候用一级指针什么时候用二级指针,所以还是有点苒,所以千万别混淆概念,否则你就要一步一步调试,超级痛苦的,写代码之前先理清楚思路。好了,今天就到这啦,觉得小编写的还不错的别忘了点赞哦!