栈 Stack
- 栈顶(Top):表尾(即 a n a_n an端)
- 栈底(Base):表头(即 a 1 a_1 a1端)
- 入栈(PUSH):插入元素到栈顶的操作
- 出栈(POP):从栈顶删除最后一个元素的操作
插入只能在表尾,删除只能在表尾
后进先出(LIFO)结构
顺序栈的表示和实现
#define MAXSIZE 100
typedef struct{
SElemTypeb *base;//栈底指针
SElemTypeb *top;//栈顶指针
int stacksize;//栈可用最大容量
}SqStack;
存储方式:同一般线性表的顺序存储结构完全相同,利用一组地址连续的存储单元依次存放自栈底到栈顶的数据元素。栈底一般在低地址端。
- 附设top指针,指示栈顶元素在顺序栈中的位置
- 另设base指针,指示栈底元素在顺序栈中的位置
但是,为了方便操作,通常top指示真正的栈顶元素之上的下标地址
另外,用stacksize表示栈可使用的最大容量
- 空栈:base==top
- 栈满:top-base==stacksize
栈满时的处理方法:
- 报错,返回操作系统
- 分配更大的空间,作为栈的存储空间,将原栈的内容移入新栈
使用数组作为顺序栈存储方式的特点:简单、方便、但易产生溢出(数组大小固定)
- 上溢(overflow):栈已经满,仍要压入元素
- 下溢(underflow):栈已经空,仍要弹出元素
顺序栈操作的函数
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
using namespace std;
#define Status int
#define SElemType int
#define MaxSize 100
//栈数据结构
typedef struct Stack
{
SElemType *base;//栈底指针 不变
SElemType *top;//栈顶指针 一直在栈顶元素上一个位置
int stacksize;//栈可用的最大容量
}SqStack;
//**************************************基本操作函数************************************//
//初始化函数
Status InitStack(SqStack &s)
{
s.base=new SElemType[MaxSize];//动态分配最大容量
if(!s.base)
{
printf("分配失败\n");
return 0;
}
s.top=s.base;//栈顶指针与栈底相同
s.stacksize=MaxSize;
return 1;
}
//入栈
Status Push(SqStack &s,SElemType e)
{
if(s.top-s.base==s.stacksize) return 0;//栈满
*(s.top++)=e;//先入栈,栈顶指针再上移
/*
*s.top=e;
s.top++;
*/
return 1;
}
//出栈 用e返回值
Status Pop(SqStack &s,SElemType &e)
{
if(s.top==s.base) return 0;//栈空
e=*--s.top;//先减减 指向栈顶元素,再给e
/*
--S.top;
e=*S.top;
*/
return 1;
}
//得到栈顶元素,不修改指针
bool GetTop(SqStack s,SElemType &e)
{
if(s.top==s.base) return false;//栈空
else e=*--s.top;
return true;
}
//顺序栈长度
Status StackLength(SqStack S){
return S.top-S.base;
}
//销毁顺序栈(未使用)
Status DestroyStack(SqStack &S){
if(S.base){
delete S.base;
S.stacksize=0;
S.base=S.top=NULL;
}
return 1;
}
//********************************功能实现函数**************************************//
//菜单
void menu()
{
printf("********1.入栈 2.出栈*********\n");
printf("********3.取栈顶 4.是否为空*****\n");
printf("********5.栈长 6.清空*********\n");
printf("********7.遍历 8.查找*********\n");
printf("**************0.退出***************\n");
}
//入栈功能函数 调用Push函数
void PushToStack(SqStack &s)
{
int n;SElemType e;int flag;
printf("请输入入栈元素个数(>=1):\n");
scanf("%d",&n);
for(int i=0;i<n;i++)
{
printf("请输入第%d个元素的值:",i+1);
scanf("%d",&e);
flag=Push(s,e);
if(flag)printf("%d已入栈\n",e);
else {printf("栈已满!!!\n");break;}
}
}
//出栈功能函数 调用Pop函数
void PopFromStack(SqStack &s)
{
int n;SElemType e;int flag;
printf("请输入出栈元素个数(>=1):\n");
scanf("%d",&n);
for(int i=0;i<n;i++)
{
flag=Pop(s,e);
if(flag)printf("%d已出栈\n",e);
else {printf("栈已空!!!\n");break;}
}
}
//取栈顶功能函数 调用GetTop函数
void GetTopOfStack(SqStack &s)
{
SElemType e;bool flag;
flag=GetTop(s,e);
if(flag)printf("栈顶元素为:%d\n",e);
else printf("栈已空!!!\n");
}
//判断栈是否为空
void IsStackEmpty(SqStack S){
if(S.top==S.base)
cout<<"栈为空"<<endl;
else
cout<<"栈不为空"<<endl;
}
//获取栈长功能函数 调用StackLength函数
void GetStackLength(SqStack S){
int length=StackLength(S);
printf("栈的长度为%d\n",length);
}
//清空顺序栈
void ClearStack(SqStack &S){
if(S.base){
S.top=S.base;
}
cout<<"栈已清空!"<<endl;
}
//遍历整个栈
void Show(SqStack &S){
if (S.top==S.base)
cout <<"栈为空!"<<endl;
int* temp=S.top; //定义一个临时指针
while (temp!=S.base){
SElemType e=*--temp;
cout<<e<<" ";
}
cout<<endl;
}
//查找栈中的元素是否存在(存在则返回对应下标)
void find(SqStack S){
int key,index=0,flag=0;
cout<<"请输入您要查找的元素:"<<endl;
cin>>key;
if(S.top==S.base){
cout<<"栈为空!"<<endl;
}
else{
int* temp=S.top;
while (temp!=S.base){
SElemType e=*--temp;
index++;
if(e==key){
flag=1;
cout<<"此元素的位置为(自顶向下):"<<index<<endl;
break;
}
}
if(flag==0){
cout<<"查无此元素!"<<endl;
}
}
}
//主函数
int main(){
SqStack s;int choice;
InitStack(s);
while(1)
{
menu();
printf("请输入菜单序号:\n");
scanf("%d",&choice);
if(choice==0) break;
switch(choice)
{
case 1:PushToStack(s);break;
case 2:PopFromStack(s);break;
case 3:GetTopOfStack(s);break;
case 4:IsStackEmpty(s);break;
case 5:GetStackLength(s);break;
case 6:ClearStack(s);break;
case 7:Show(s);break;
case 8:find(s);break;
default:printf("输入错误!!!\n");
}
}
return 0;
}
链栈的表示和实现
typedef struct StackNode{
SElemType data;
struct StackNode *next;
}StackNode,*LinkStack;
LinkStack S;
链栈是运算受限的单链表,只能在链表头部进行操作
注意链栈中指针的方向
- 链表的头指针就是栈顶
- 不需要头结点
- 基本不存在栈满的情况
- 空栈相当于头指针指向空
- 插入和删除仅在栈顶处执行
链栈操作的函数
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
using namespace std;
#define Status int
#define SElemType int
//链栈结点数据结构
typedef struct StackNode
{
SElemType data;//数据域
struct StackNode *next;//指针域
}StackNode,*LinkStack;
//**************************基本操作函数***************************//
//初始化函数
Status InitStack(LinkStack &S)
{
S = NULL;//生成空栈 以单链表表头为栈顶 注意,链栈没有像链表似的头结点
return 1;
}
//入栈函数 将e压入栈
Status Push(LinkStack &S,SElemType e)
{
StackNode *p;
p=new StackNode;//生成新节点
p->data=e; //赋值
p->next=S; //压入栈顶
S=p;
return 1;
}
//出栈函数 栈顶出栈用e返回 注意释放空间
bool Pop(LinkStack &S,SElemType &e)
{
LinkStack p;
if(S==NULL)return false;//栈空
e=S->data;
p=S;
S=S->next;
free(p);
return true;
}
//取栈顶函数 用e返回
bool GetTop(LinkStack &S,SElemType &e) //严蔚敏版(人民邮电出版社) 61页有误
{
if(S==NULL) return false;//栈顶为空
e=S->data;
return true;
}
//栈长
int lengthStack(LinkStack S){
StackNode* p=S;
int i=0;
while(p){
i++;
p=p->next;
}
return i;
}
//按值查找-根据指定数据获取该数据所在的位置序号
Status LocateElem(LinkStack S,SElemType e) {
StackNode* p=S;int j=1;
while(p&&p->data!=e){
p=p->next;
j++;
}
if(p)
return j;
else
return 0;
}
//**************************功能实现函数***************************//
//菜单
void menu()
{
printf("********1.入栈 2.出栈*********\n");
printf("********3.取栈顶 4.是否为空*****\n");
printf("********5.栈长 6.清空*********\n");
printf("********7.遍历 8.查找*********\n");
printf("**************0.退出***************\n");
}
//入栈功能函数 调用Push函数
void PushToStack(LinkStack &S)
{
int n;SElemType e;int flag;
printf("请输入入栈元素个数(>=1):\n");
scanf("%d",&n);
for(int i=0;i<n;i++)
{
printf("请输入第%d个元素的值:",i+1);
scanf("%d",&e);
flag=Push(S,e);
if(flag)printf("%d已入栈\n",e);
}
}
//出栈功能函数 调用Pop函数
void PopFromStack(LinkStack &S)
{
int n;SElemType e;int flag;
printf("请输入出栈元素个数(>=1):\n");
scanf("%d",&n);
for(int i=0;i<n;i++)
{
flag=Pop(S,e);
if(flag)printf("%d已出栈\n",e);
else {printf("栈已空!!!\n");break;}
}
}
//取栈顶功能函数 调用GetTop函数
void GetTopOfStack(LinkStack S)
{
SElemType e;bool flag;
flag=GetTop(S,e);
if(flag)printf("栈顶元素为:%d\n",e);
else printf("栈已空!!!\n");
}
//判断栈是否为空
void IsStackEmpty(LinkStack S){
if(S==NULL) cout<<"栈为空!"<<endl;
else cout<<"栈不为空!"<<endl;
}
//取栈长功能函数
void LengthStack(LinkStack S){
int len=lengthStack(S);
if(len) cout<<"栈长为"<<len<<endl;
else cout<<"栈为空!"<<endl;
}
//遍历栈
void PrintStack(LinkStack S)
{
LinkStack p=S;
if(lengthStack(S))
{
cout<<"当前栈所有元素(自顶向下):"<<endl;
while(p)
{
cout<<p->data<<" ";
p=p->next;
}
cout<<endl;
}
else
{
cout<<"栈已空!"<<endl;
}
}
//清空栈
void ClearStack(LinkStack &S){
StackNode *p,*q;
p=S->next;
while(p){
q=p->next;
delete p;
p=q;
}
S=NULL;
PrintStack(S);
}
//查找
void Search(LinkStack S)
{
SElemType e;int q;
cout<<"请输入要查找的值:"<<endl;
cin>>e;
q=LocateElem(S,e);
if(q)
{
cout<<"找到该元素!该元素的位置(自顶向下)是"<<q<<endl;
}
else
cout<<"未找到该元素!"<<endl;
}
//主函数
int main()
{
LinkStack S;int choice;
InitStack(S);
while(1)
{
menu();
printf("请输入菜单序号:\n");
scanf("%d",&choice);
if(choice==0) break;
switch(choice)
{
case 1:PushToStack(S);break;
case 2:PopFromStack(S);break;
case 3:GetTopOfStack(S);break;
case 4:IsStackEmpty(S);break;
case 5:LengthStack(S);break;
case 6:ClearStack(S);break;
case 7:PrintStack(S);break;
case 8:Search(S);break;
default:printf("输入错误!!!\n");
}
}
return 0;
}
栈与递归
函数调用过程
调用前,系统完成:
- 将实参,返回地址等传递给被调用函数
- 为被调用函数的局部变量分配存储区
- 将控制转移到被调用函数的入口
调用后,系统完成:
- 保存被调用函数的计算结果
- 释放被调用函数的数据区
- 依照被调用函数保存的返回地址将控制转移到调用函数
#include<stdio.h>
void fun1(int n){
if(n!=0){
printf("%d\n",n);
fun1(n-1);
}
}
void fun2(int n){
if(n!=0){
fun2(n-1);
printf("%d\n",n);
}
}
int main(){
fun1(6);
printf("\n");
fun2(6);
return 0;
}