赞
踩
栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。
- 进行数据插入和删除操作的一端 称为栈顶,另一端称为栈底。
- 栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。
- 压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。
- 出栈:栈的删除操作叫做出栈。出数据也在栈顶。
栈也是一种线性表,是一种特殊的数据结构,我们可以用我们前面所学的知识来实现栈
我们之前学习的顺序表和链表只是单纯的存储数据,并可以做到增删查改的作用,而栈除了存储数据还要达到后进先出的性质
- 如图我们假设,数据入栈完成后再出,即先存储1 2 3 4后再出数据,按照后进先出原则,我们的出栈顺序就是4 3 2 1
- 倘若不要求入栈完成再出,就有很多种,如1 2 3 4,即入一个出一个。3 2 4 1即入把3 2出了后再入
- 但没有3 1 2 4这样的顺序,2没有可能比1先出。
助力理解:
1.一个栈的初始状态为空。现将元素1、2、3、4、5、A、B、C、D、E依次入栈,然后再依次出栈,则元素出
栈的顺序是( B)。
A 12345ABCDE
B EDCBA54321
C ABCDE12345
D 54321EDCBA
2.若进栈序列为 1,2,3,4 ,进栈过程中可以出栈,则下列不可能的一个出栈序列是(C)
A 1,4,3,2
B 2,3,4,1
C 3,1,4,2
D 3,4,2,1
接下来就是实现栈了,有2种方式实现
这里我们用数组来实现栈会更方便一点,数组的高数缓存命中更高,并且结构很符合栈的需求,尾部就可以当栈顶,尾插尾删即可
如果选链表实现的话,我们的的尾删就要遍历链表找到前一个太慢,只能将头当成栈顶,进行头删头插
typedef int DataType;
typedef struct stack
{
DataType* p;
int top;
int num;
}st;
void stinit(st* pst)
{
assert(pst);
pst->p = NULL;
pst->top = 0; //这里top也可以赋值-1
pst->num = 0;
}
这是我们的一种初始化方式,即先不开辟空间,这里我们重点讲top的赋值
我们的top是指向数据的下一个位置,因为我们是先插入数据,再让top++
如图,top先指向0,插入数据后top++,即top指向的是栈顶元素的下一个位置
当 top = -1 的时候
与上面相似,不够我们是让top++,再插入数据,这个时候top就指向栈顶元素
我们一定要分清楚自己想让top指向栈顶元素还是栈顶元素的下一个,不然你想指向栈顶元素,却赋值为0,那这就说明这个时候栈里面就已经有数据呢,但你还没插入。
要重视top的赋值,不然后面的所以操作都会收到影响,这里使用 top = 0 的情况
void stdestroy(st* pst)
{
assert(pst);
free(pst->p);
pst->p == NULL;
pst->num = 0;
pst->top = 0;
}
值得注意的是,这里的free是free全部开辟的空间,数组空间连续,并不是你的链表一个一个释放
我们的栈没有头和尾的概念,所以我们的命名就是stpush
void stpush(st* pst, DataType x) { assert(pst); if (pst->top == pst->num) { int newnum = pst->top == 0 ? 4 : pst->num * 2; DataType* newp = (DataType*)realloc(pst->p, sizeof(DataType) * newnum); if (!newp) { perror("realloc"); return; } pst->p = newp; pst->num = newnum; } pst->p[pst->top] = x; pst->top++; }
值得注意的是,我们这里的初始化不开辟空间和这里呼应上了
int newnum = pst->top == 0 ? 4 : pst->num * 2;
DataType* newp = (DataType*)realloc(pst->p, sizeof(DataType) * newnum);
我们的newp是赋值给pst->p的不要把类型写出st*了
我们的
这是一种写法,推荐学习
这里的memblock指针为NULL的时候,realloc表现如同malloc一样
我们并不需要单独写一个函数去扩容,因为复用的函数很少,这个push函数把扩容也包括了
我们只需要让top–即可
void stpop(st* pst)
{
assert(pst);
assert(!stempty(pst));
pst->top--;
}
注意,为了代码的可读性,我们推荐写上一个判空的函数STEmpty
我们为什么不在主函数中直接用下标访问呢?
如: void test() { st st; st.p[st.top]; }
- 1
- 2
- 3
- 4
- 5
- 6
因为这样对使用者的需求过大,使用者不知道你的top是指向哪的,当top为0的时候,使用者可能会当成下标来访问了,打印出随机值来,当top为-1的时候,使用者会感到疑惑
我们用函数来实现获取栈顶元素
DataType sttop(st* pst)
{
assert(pst);
return pst->p[pst->top - 1];
}
bool stempty(st* pst)
{
assert(pst);
/*if (!pst->top)
{
return true;
}
else
{
return false;
}*/
return pst->top == 0;
}
我们要学习一下,这里是一个比较操作符,等于0就是真,反之为假
DataType stsize(st* pst)
{
assert(pst);
return pst->top;
}
while (!stempty(&st))
{
printf("%d ", sttop(&st));
stpop(&st);
}
真,反之为假
DataType stsize(st* pst)
{
assert(pst);
return pst->top;
}
while (!stempty(&st))
{
printf("%d ", sttop(&st));
stpop(&st);
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。