栈
来源:互联网 发布:3tier数据 编辑:程序博客网 时间:2024/05/24 05:47
一、什么是栈
栈是限定仅在表头进行插入和删除操作的线性表。要搞清楚这个概念,首先要明白”栈“原来的意思,如此才能把握本质。”栈“者,存储货物或供旅客住宿的地方,可引申为仓库、中转站,所以引入到计算机领域里,就是指数据暂时存储的地方,所以才有进栈、出栈的说法。
二、两种常用的栈
1、顺序栈
使用连续的内存空间模拟栈的空间,一般使用数组来实现,数组索引为0即为栈底,其次再定义一个变量储存栈顶位置即可,这种栈实现起来比较简单容易操作,适合初学者。
2、链式栈
使用零散的内存空间模拟栈的空间,一般使用链表1来实现,链表尾部即为栈底,链表头部即为栈顶。
三、栈的实现
代码节选自网络
#include<stdio.h>#include<stdlib.h>#include<malloc.h>//定义结点结构体typedef struct Node{ int data; //内容 struct Node * pNext; //指向下一结点的指针} NODE, * PNODE; //NODE等价于struct Node, PNODE等价于struct Node *//定义栈的结构体typedef struct Stack{ PNODE pTop; //栈顶结点 PNODE pBottom; //栈底结点} STACK, * PSTACK; //STACK等价于struct Stack, PSTACK等价于struct Stack *//函数声明void initStack(PSTACK pStack); //对栈进行初始化的函数void pushStack(PSTACK pStack, int val); //入栈的函数bool popStack(PSTACK pStack, int * pVal);//出栈的函数,*pVal用来保存出栈的元素内容void traverseStack(PSTACK pStack); //遍历栈的函数bool isEmpty(PSTACK pStack); //判断栈是否为空的函数void clearStack(PSTACK pStack); //清空栈的函数int main(void){ STACK stack; //定义一个栈变量,STACK等价于struct Stack int val; //用来保存出栈的内容 initStack(&stack); //调用栈的初始化函数 pushStack(&stack, 10); //调用入栈的函数 pushStack(&stack, 20); pushStack(&stack, 30); pushStack(&stack, 50); traverseStack(&stack); //调用遍历栈的函数 //调用出栈的函数 if(popStack(&stack, &val)) printf("出栈成功,出栈的元素值为:%d\n", val); else printf(" 出栈失败!"); //调用清空栈的函数 clearStack(&stack); traverseStack(&stack); //调用遍历栈的函数 system("pause"); return 0;}void initStack(PSTACK pStack){ //创建一个空结点,让pTop指向它 pStack->pTop = (PNODE)malloc(sizeof(NODE)); if(NULL != pStack->pTop) { //将pBottom也指向空节点 pStack->pBottom = pStack->pTop; //清空空结点的指针域 pStack->pTop->pNext = NULL; } else //如果内存分配失败 { printf("内存分配失败!程序退出!\n"); exit(-1); } return;}void pushStack(PSTACK pStack, int val){ //动态创建一个新结点 PNODE pNew = (PNODE)malloc(sizeof(NODE)); //设置新结点的数据域的值 pNew->data = val; //将新结点的指针域指向之前建的空节点 pNew->pNext = pStack->pTop; //pStack->pTop不能换成pStack->pBottom //pTop指向新的结点 pStack->pTop = pNew; return;}bool popStack(PSTACK pStack, int * pVal){ if(isEmpty(pStack)) { return false; } else { //先保存栈顶元素的地址,然后将pTop指向下一元素,最后释放之前栈顶元素的内存 PNODE rNode = pStack->pTop; *pVal = rNode->data; pStack->pTop = rNode->pNext; free(rNode); rNode = NULL; return true; }}void traverseStack(PSTACK pStack){ //将栈顶赋给一个临时结点,因为在遍历栈的时候不能销毁栈 PNODE pNode = pStack->pTop; //循环遍历栈,直到栈底 while(pStack->pBottom != pNode ) { printf("%d ", pNode->data); pNode = pNode->pNext; } printf("\n"); return;}bool isEmpty(PSTACK pStack){ if(pStack->pTop == pStack->pBottom) return true; else return false;}void clearStack(PSTACK pStack){ //栈为空,则退出该函数 if(isEmpty(pStack)) { return; } else { //两个结点指针变量用来释放栈中元素的内存 PNODE p = pStack->pTop; PNODE q = NULL; //循环释放内存 while(p != pStack->pBottom) { q = p->pNext; free(p); p = q; } //将栈顶和栈底指向同一个指针域为空的结点 pStack->pTop = pStack->pBottom; return; }}
四、栈的存储
1、栈的顺序存储结构
栈的顺序存储结构需要使用一个数组和一个整型变量来实现。利用数组来顺序存储栈中的所有元素,利用整型变量来存储栈顶元素的下标位置,可这个变量称为栈顶指针。
const int MaxSize = 50;struct Stack{ ElemType stack[MaxSize]; int top;};
若要对存储栈的数组空间采用动态分配,则可定义如下:
struct Stack{ ElemType *stack; int top; int MaxSize;};
top的值为-1表示栈空。
2、栈的链式存储结构
栈的链式存储结构是通过由结点构成的单链表实现的,此时表头指针被称为栈顶指针,由栈顶指针指向的表头结点被称为栈顶结点,整个单链表被称为链栈。对链栈的插入和删除操作是在单链表的表头进行的。
当向一个链栈插入元素时,是把该元素插入到栈顶,即,使该元素结点的指针域指向原来的栈顶结点,而栈顶指针则修改为指向该元素结点,使该结点成为新的栈顶结点。
五、两栈的空间共享
当程序中同时使用两个栈时,可以将两个栈的栈底设在向量空间的两端,让两个栈各自向中间延伸。
当一个栈的元素较多,超过向量空间的一半时,只要另一个栈的元素不多,那么前者就可以占用后者的部分存储空间。只有当整个向量空间被两个栈占满(即两个栈顶相遇)时,才会发生上溢,因此两个栈共享一个长度为m的向量空间
六、栈的抽象数据类型
ADT 栈(stack) Data 同线性表。元素具有相同的类型,相邻元素具有前驱和后堆关系。 Operation InitStack ( *S ):初始化操作.建立一个空栈S。 DestroyStack ( *S ):若栈存在,則销毁它。 ClearStack (*S):将栈清空。 StackEmpty ( S ):若栈为空,返回true,否則返回 false。 GetTop (S,*e):若栈存在且非空,用e返回S的栈顶元素。 Push (*S,e):若栈S存在,插入新元素e到栈S中并成为栈頂元素。 Pop (*S,*e):删除栈S中栈顶元素,并用e返回其值。 StackLength (S):返回回栈S的元素个数。 endADT
七、栈的应用
裴波那契数列的计算
四则运算表达式
后缀表达式的定义
递归
中缀转后缀