数据结构02——栈(知识点+代码版)

本文详细介绍了栈的基本概念,包括栈的定义、操作、常考题型,以及顺序存储(如顺序栈和共享栈)和链式存储(如链栈)的实现。此外,文中还展示了栈在括号匹配、表达式求值和递归中的应用,以及斐波那契数列的非递归计算。
摘要由CSDN通过智能技术生成

一. 栈的基本概念

1. 栈(Stack)的定义

栈是只允许在一端进行插入或删除操作的线性表 后进先出。LIFO

在这里插入图片描述

2.栈的基本操作

  • InitStack(&S):初始化栈。构造一个空栈S,分配内存空间。
  • DestoryStack(&L):销毁栈。销毁并释放栈S所占用的内存空间。
  • Push(&S,x):进栈,若栈S未满,则将x加入使之成为新栈顶
  • Pop(&S,&x):出栈,若栈S非空,则弹出栈顶元素,并用x返回。
  • GetTop(S,&x):读栈顶元素。若栈S费控,则用x返回栈顶元素。
  • StackEmpty(S):判断一个栈S是否为空。若S为空,则返回true,否则返回false。

3.栈的常考题型

​ n个不同元素进栈,出栈元素不同排列的个数为: 1 n + 1 C 2 n n \frac1{n+1}C^n_{2n} n+11C2nn

二. 栈的顺序存储实现

1. 顺序栈的定义

#define MaxSize 10   		//栈中元素的最大个数
typedef struct{
    Elemtype data[MaxSize]; //静态数组存放栈中元素
    int top;     			//栈顶指针
}SqStack;

2.初始化操作

//初始化栈
void InitStack(SqStack &S){
    S.top=-1;
}

//判断栈空
bool StackEmpty(SqStack S){
	if(S.top==-1)     //栈空
		return true;
	else
		return false;
}

3.进出栈操作

//新元素进栈
bool Push(SqStack &S,ElemType x){
    if(S.top==MaxSize-1)	//栈满,报错
        return false;
    S.top=S.top+1;			//指针先加1
    S.data[S.top]=x;		//新元素入栈
    /*等价于
    	S.data[++S.top]=x;
    */
    return true;
}

//出栈操作
bool Pop(SqStack &S,ElemType &x){
    if(S.top==-1)		//栈空,报错
        return false;
    x=S.data[S.top];	//栈顶元素先出栈
    S.top=S.top-1;		//指针再减1
    /*等价于
    	S.data[S.top--]=x;
    */
    return true;
}

[入栈]
入栈
[出栈]
在这里插入图片描述

4.读栈顶元素操作

//读栈顶元素
bool GetTop(SqStack S,ElemType &x){
    if(S.top==-1)
        return false;
    x=S.data[S.top];   //x记录栈顶元素 
    return true;
}

审题时要注意top指针指向的是栈顶,还是栈顶的下一个位置。

指向栈顶下一个位置时,栈满的条件是:top==MaxSize;进栈出栈位置相反,即:

进栈:S.data[S.top++]=x;

出栈:x=S.data[–S.top];

5.共享栈

两个栈共享同一片空间。

///共享栈
typedef struct{
    ElemType data[MaxSize];    //静态数组存放栈中元素 
    int top0;           //0号栈栈顶指针 
    int top1;           //1号栈栈顶指针 
}ShStack;

//初始化共享栈
void InitStack(ShStack &S){
    S.top0=-1;
    S.top1=MaxSize;
}

栈满的条件:top0+1==top1

三. 栈的链式存储

1. 链栈的定义

在这里插入图片描述

typedef struct Linknode{
    ElemType data;       //数据域 
    struct Linknode *next; //指针域 
} *LiStack;				//栈类型定义

2.链栈的基本操作

创,增,删,查,判空,判满。

//创,增,删,查,判空,判满。

//带头结点链栈 
//创(初始化) 
    void  InitLiStack(LiStack &S){
        LiStack node=(LiStack)malloc(sizeof(Linknode));
        S=node;
        S->next=NULL;
    }

//判空
    bool isEmpty(LiStack &S){
        if(S->next==NULL)
            return true;
        return false;
    }

//判满:链表没有判满。 

//增(进栈) 
    void Push(LiStack &S,ElemType x){
        S->data=x;
        LiStack newNode=(Linknode*)malloc(sizeof(Linknode));
        newNode->next=S;
        S=newNode;
    }

//删 (出栈)
    ElemType Pop(LiStack &S){
        ElemType x=S->next->data;//被删除元素带回 
        LiStack top=S->next;
        S->next=top->next;
        free(top);
        return x;
    }

//查(获取栈顶元素)
    ElemType GetTop(LiStack &S){
        return S->next->data;
    }

测试链栈:

//链栈
printf("创建。。。\n");
LiStack S;
printf("创建成功。\n");

printf("初始化。。。\n");
InitLiStack(S);
printf("初始化成功。");

printf("链表是否为空:%d\n",isEmpty(S));

printf("进栈\n");
Push(S,6);
printf("链表是否为空:%d\n",isEmpty(S));
printf("查询栈顶元素为:%d\n",GetTop(S));

printf("出栈\n");
Pop(S);
printf("链表是否为空:%d\n",isEmpty(S));
测试结果

四. 栈的应用

1.栈在括号匹配中的应用

在这里插入图片描述在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

算法思路

//括号匹配
bool bracketCheck(char str[],int length){
    SqStack S;
    InitStack(S);
    for(int i=0;i<length;i++){
        if(str[i]=='('||str[i]=='['||str[i]=='{')
            Push(S,str[i]);
        else{//扫描到右括号 
            if(StackEmpty(S))// 栈为空
                return false;//匹配失败 

            char topElem;
            Pop(S,topElem);
            if(str[i]==')'&&topElem=='(')
                continue;
            else if(str[i]==']'&&topElem=='[')
                continue;
            else if(str[i]=='}'&&topElem=='{')
                continue;
            else
                return false;
        }
    }
    return StackEmpty(S);//检索完全部括号后,栈空说明匹配成功 
}

//测试括号匹配
void testbracketCheck()
{
    char str[]={'{','[','(',')',']','}'};
    for(int i=0;i<6;i++){
        printf("%c",str[i]);
    }
    printf("\n");
    if(bracketCheck(str,6))
        printf("匹配成功!");
    else
        printf("匹配失败。");
}

2.栈在表达式求值中的应用

算术表达式由三个部分组成:操作数,运算符,界限符

波兰表达式:前缀表达式; 逆波兰表达式:后缀表达式。

中缀表达式后缀表达式前缀表达式
a + ba b ++ a b
a + b - ca b + c -- + a b c

中缀表达式转后缀表达式(机算):

​ (1). 初始化一个栈,用于保存暂时还不能确定运算顺序的运算符

​ (2). 从左到右处理各个元素,直到末尾。可能遇到三种情况:

​ ①遇到操作数。直接加入后缀表达式。

​ ②遇到界限符。遇到“(”直接入栈:遇到“)”则依次弹出栈内运算符并加入后缀表达式,直到弹出“(”为止。注意:“(”不加入后缀表达式。

​ ③遇到运算符。依次弹出栈中优先级高于或等于当前运算符的所有运算符,并加入后缀表达式,若碰到“(”或栈空则停止。之后再把当前运算符入栈。

​ (3). 按上述方法处理完所有字符后,将栈中剩余运算符依次弹出,并加入后缀表达式。

用栈实现后缀表达式的计算(机算):

①. 从左往右扫描下一个元素,直到处理完所有元素;

②. 若扫描到操作数则压入栈,并回到①;否则执行③;

③. 若扫描到运算符,则弹出两个栈顶元素,执行相应运算,运算结果压回栈顶,回到①。注意:先出栈的是右操作数

用栈实现中缀表达式的计算(机算):(中缀转后缀+后缀计算)

初始化两个栈,操作数栈和运算符栈

​ ①. 若扫描到操作数,压入操作数栈

​ ②. 若扫描到运算符或界限符,则按照“中缀转后缀”相同的逻辑压入运算符栈(期间也会弹出
运算符,每当弹出一个运算符时,就需要再弹出两个操作数栈的栈顶元素并执行相应运算,
运算结果再压回操作数栈)

用栈实现前缀表达式的计算(机算):

①. 从右往左扫描下一个元素,直到处理完所有元素;

②. 若扫描到操作数则压入栈,并回到①;否则执行③;

③. 若扫描到运算符,则弹出两个栈顶元素,执行相应运算,运算结果压回栈顶,回到①。注意:先出栈的是左操作数

3.栈在递归中的应用

函数调用的特点:最后被调用的函数最先执行结束(FIFO)

函数调用时,需要用一个栈存储:
①调用返回地址
②函数
③局部变量

斐波那契数列

#include <stdio.h>
#include <stdlib.h>

///递归
int Fibon1(int n){
	if(n==1||n==2)
		return 1;
	else
		return Fibon1(n-1)+Fibon1(n-2);
} 

int Fibon2(int n){
	if(n==0)
		return 0;
	else if(n==1)
		return 1;
	else
		return Fibon2(n-1)+Fibon2(n-2);
}


///非递归
int Fibon3(int n){
	int f0=0,f1=1,f2=1;
	for(int i=2;i<=n;i++){
		f2=f0+f1;
		f0=f1;
		f1=f2;
	}
	if(n==0)
		return 0;
	else if(n==1)
		return 1;
	else
		return f2;
}
 

int main(){
	printf("斐波那契数列值:"); 
	printf("\n");
	printf("递归1: ");
	for(int i=1;i<15;i++){
		printf("%d ",Fibon1(i));
	}
	printf("\n");
	printf("递归2: ");
	for(int i=1;i<15;i++){
		printf("%d ",Fibon2(i));
	}
	printf("\n");
	printf("非递归:");
	for(int i=1;i<15;i++){
		printf("%d ",Fibon3(i));
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值