01栈的基础概念
栈(Stack)作为一种线性存储结构,呈现出以下独特特性:栈遵循“先进后出”原则,即先入栈的数据元素后出栈。在栈中,只能对栈顶元素进行插入和删除操作,有其独特的操作方式。
压栈(push)操作是将元素添加到栈中的过程,而弹栈(pop)操作则相反,遵循后进先出的原则。在栈中,允许进行元素插入与删除的一端被称作栈顶,而另一端则被称为栈底。
在压栈操作中,栈顶的位置会持续“向上”移动,而栈底则保持静止不动。
若要移除栈中的元素,则需进行弹栈操作。出栈的顺序遵循“先进后出”的原则,即后入栈的元素先出栈。在弹栈操作中,栈顶元素被移除,导致栈顶位置不断“向下”移动,而栈底始终保持不变。
02栈的操作与实现
?常见操作栈的常见操作包括:弹栈(也称为pop操作)、压栈(即push操作)、求栈的大小、判断栈是否为空以及获取栈顶元素的值。
?基于数组的实现由于栈是一种线性结构,它可以使用数组来存储。通常选择数组来实现栈,通过栈底和栈顶指针来维护栈的元素。我们将探讨以数组为基础的栈实现方式。
在基于数组的栈实现中,我们通常将数组的头部设为栈底,而数组的尾部则作为栈顶的生长方向。此外,我们还需要维护一些私有成员,如栈的元素数量、栈的容量以及底层数组等,以确保栈的正确性和高效性。
栈的元素数量由count表示,而栈的容量则由capacity表示。在任意时刻,count的值都不会超过capacity。当栈满时,count等于capacity。
本实现不支持栈的动态扩容,这意味着当栈满时无法再插入新元素。而栈的容量必须在定义时指定,默认情况下,栈的容量为10。
以上是几个关键操作的实现代码,例如:
判空操作:用于确认栈中是否有元素。
插入操作:在插入前判断是否已满。
弹栈操作:在弹栈前判断是否为空栈。
?基于链表的实现通过单链表实现栈,将链表头作为栈顶是一种合适的选择。这样的设计使得节点的插入与删除操作变得简单高效。新加入的节点会始终被添加到链表的头部,从而实现了栈的后进先出(LIFO)特性。
在实现基于链表的栈时,我们首先需要定义链表节点结构。一个典型的链表节点包含数据域和指针域,其中数据域用于存储元素值,而指针域则指向下一个节点。我们定义了一个Node结构来表示每个节点,其中包含一个value域用于存储数据,以及一个next指针指向下一个节点。我们还定义了一个LinkStack类来表示整个栈,其中包含一个phead指针指向链表的头部,以及一个count变量用于记录栈中的元素数量。
?代码测试以下提供了一个简单的测试程序来验证栈的实现是否正确。该程序不仅限于测试基于数组和链表实现的栈,还展示了预期的输出结果。
对于数组实现的栈:
```cpp
intmain()
{
ArrayStackintp(5);
for(inti=0;i5;i++)
p.push(i);
cout"栈的大小:"p.size()endl;
cout"栈是否为空:"p.isEmpty()endl;
cout"栈顶元素:"p.top()endl;
cout"依次出栈:"endl;
while(!p.isEmpty())
coutp.pop()endl;
getchar();
turn0;
}
```
而对于基于链表实现的栈:
```cpp
intmain(intargc,_TCHARargv[])
{
LinkStackstringlstack;
lstack.push("hello");
lstack.push("to");
lstack.push("you!");
cout"栈的大小:"lstack.size()endl;
cout"栈顶元素:"lstack.top()endl;
while(!lstack.isEmpty())
lstack.pop();
cout"栈的大小:"lstack.size()endl;
getchar();
turn0;
}
```
这样的测试代码确保了通过数组和链表实现的栈的功能正常,结果符合预期。