数据结构~栈的应用介绍

数据结构~栈

栈结构是先进后出,压入栈的操作(如果是以链表来解释的话)也可以类比单相表的头插入,即插入顺序与输出顺序刚好相反。

栈的定义:

typedef struct myStack* SPro;
typedef struct myStack{
    int * base;//底指针
    int * top;//顶指针
    int stackSize; //栈的容量
}S;

这里提一点:
例如top这个顶部指针,top代表的是他指向的地址对应的内存,是可以被赋予元素值的,但是top是一个指针,储存的就是一个地址,指针top指向top储存的地址,这点一定要搞明白。下面是栈的操作,代码中的注释里给出了每个函数的介绍。

栈的操作:

/**
 * 创建一个空栈,初始化储存空间为 n,并返回此栈
 * @param n 储存空间
 * @return 返回初始化好的空栈
 */
SPro createStack(int n){
    SPro stack=(SPro)malloc(sizeof(S));
    stack->base=(int *)malloc(n*sizeof(int));
    if (!stack->base){
        cout<<"内存不足,初始化失败"<<endl;
        return NULL;
    }
    stack->top=stack->base;
    stack->stackSize=n;
    return stack;
}



/**
 * 压栈
 * @param stack 被压入的栈
 * @param newElem 将这个元素压入stack
 */
void push(S &stack,int newElem){
    if (stack.top-stack.base==stack.stackSize){
        cout<<"储存已满,不能再继续往栈内放新元素"<<endl;
    }
    *stack.top++=newElem;
}



/**
 * 栈顶元素出栈
 * @param stack 栈
 * @param e 赋给参数 e
 */
void pop(S &stack,int & e){
    if (stack.top==stack.base){
        cout<<"这是一个空栈"<<endl;
        return;
    }
    e=*--stack.top;
}



/**
 * 获取栈顶元素,不弹出
 * @param stack 栈
 * @return 返回栈顶元素
 */
int getTop(S &stack){
    if (stack.top==stack.base){
        cout<<"这是一个空栈"<<endl;
    } else
    return *(stack.top-1);
}
int main() {
    SPro stack=NULL;
    stack=createStack(5);//初始化栈
    for (int i = 1; i <= 5; ++i) {//入栈
        push(*stack,i);
    }
    cout<<"top: "<<getTop(*stack)<<endl;
    int * top=(int *)malloc(sizeof(int));
    for (int i = 1; i <= 5; ++i) {
        pop(*stack,*top);
        cout<<"be popped elem is: " <<*top<<", ";
        if (stack->top!=stack->base)
            cout<<"top elem is: "<<getTop(*stack)<<endl;
    }
    return 0;
}

栈的应用:

汉诺塔,具体问题描述在这里就不再赘述了,下面给出两种解题思路
分别为普通的递归解法,和栈的解法

1:递归解法

void move(int i,char a,char b){
    cout<<i<<": "<<a<<"--->"<<b<<endl;
}
void hnt(int n,char a,char b,char c){
    if (n==1){//如果a柱上剩下一个盘子,就把这个盘子从a柱移到c柱
        move(n,a,c);
        return;
    } else{
        //把第n个盘子上面的n-1个盘子从a柱利用c柱移动到b柱
        hnt(n-1,a,c,b);
        //此时可以把第n个盘子从a柱移动到c柱
        move(n,a,c);
        //然后再把这n-1个盘子利用空着的a柱,从b柱移动到c柱
        hnt(n-1,b,a,c);
    }
}
int main(){
    hnt(3,'a','b','c');
}

可以看出,递归是很方便读懂的,而且很简短,具体思路就是一直把所有盘子看成n和n-1两部分,只有把n-1个盘子利用一个柱子做过渡,全部移到另一个柱子后,最大的盘子才能移到目的柱上,往后依次分两部分进行移动。

递归虽然很好,但是它的空间占用相对较大,一旦数据过大,程序就会崩溃,所以有了递归算法,也要想着把递归算法改为非递归,优化空间占用,防止程序崩溃,下面给出非递归的解法

2:栈解法

现在要解决的问题分三块,依次为:
一:需要把n-1个盘子先从A通过C全部移动到B
二:需要把A上剩下的那一个盘子移到C上
三:需要把B上的n-1个盘子通过A移动到C上

所以,通过上面的分析,应该把问题依次反向压入栈中,即先push(问题三),再push(问题二),再push(问题一),然后即可从栈顶开始解决问题,并pop出解决过的问题。具体代码如下(代码是借鉴了北大信息工程学院的数据结构课件后写的):

#include<iostream>
#include "stackDemo.h"
#include<stack>
using namespace std;
struct Problem{
    int n;
    char source,mid,destination;
    
    //Problem:问题描述,即:将num个盘子,从source柱,通过mid柱当中介,移动到destination柱
    Problem(int num, char s, char m, char d): n(num), source(s), mid(m), destination(d){ }
};

stack<Problem> stk;//定义问题栈

int main(){
    int n;
    cin>>n;
    stk.push(Problem(n,'a','b','c'));//初始化问题,并压入栈
    while(!stk.empty()){
        Problem curPrb=stk.top();//每次循环 取 栈顶储存的问题内容 赋值给 curPrb
        stk.pop();//取出后,从栈中弹出此问题
        if(curPrb.n==1) cout << curPrb.source << "---->" << curPrb.destination << endl;//当这个问题里就剩一个盘子需要从source移动到destination时,直接输出即可
        else{//分解问题,三个问题反向入栈
            stk.push(Problem(curPrb.n-1, curPrb.mid, curPrb.source, curPrb.destination));//先将第三个问题压入栈
            stk.push(Problem(1, curPrb.source, curPrb.mid, curPrb.destination));//再将第二个问题压入栈
            stk.push(Problem(curPrb.n-1, curPrb.source, curPrb.destination, curPrb.mid));//最后将第一个问题压入栈
        }
    }
    return 0;
}

栈的拓展:

双栈,就是两个栈共用一段储存空间,两端分别为两个栈的栈底,分别向中间压栈,实现起来,先初始化储存空间的大小,然后,如果是下边的栈需要入栈或者出栈,跟单栈一样,入栈后top++,出栈后top–,内存可别释放,不然还得申请,因为刚开始创建这个双栈的时候,就把空间申请好了,就是入栈的时候需要判断两个栈的顶指针是否相等,如果相等,就别往里边存了。如果是上边的栈进行入栈操作,就是他的top–即可,具体代码如下:(这里给出的是伪代码,不能直接运行)

typedef struct{
int top[2],bot[2];//栈顶和栈底指针 
SElemType *V;//栈数组 
in m;//栈最大可容纳元素的个数(也就是数组长度) 
}DblStack;

//初始化
Status InitStack(Dblstack &S, int m){
    S.V=(SElemType *)malloc(m*sizeof(SElemType));  //为双栈分配一个大小为m的数组空间
    if(!S.V){
        return ERROR;   //存储分配失败
    }
    S.top[0]=-1;
    S.bot[0]=-1;
    S.top[1]=m;
    S.bot[1]=m;
    return OK;
}
//判断是否为空
Status DblstackEmpty(Dblstack &S){
    if(S.top[0]==S.bot[0]&&S.top[1]==S.bot[1]){
        return TRUE;  //栈空
    }else{
        return FALSE;  //栈非空
    }
}

//判断是否满栈
Status DblstackFull(Dblstack &S){
    if(S.top[1]-S.top[0]==1)
        return TRUE;  //栈满
    else
        return FALSE; //栈不满
}
//进栈
Status Push(Dblstack &S,int e,int n){
    if(DblstackFull(S)){  //调用3中的判满函数
        return ERROR;   //栈满,无法入栈
    }
    if(e=0){
        S.V[++S.top[e]]=n;    //n存到0号栈栈顶,栈顶元素右移
    }else if(e==1){
        S.V[--S.top[e]]=n;    //n存到1号栈栈顶,栈顶元素左移
    }
    return OK;
}
//出栈
Status Pop(Dblstack &S,int n1,int n2){
    if(DblstackEmpty(S)){  //调用2中的判空函数
        return ERROR;    //栈为空,无法出栈
    }
    n1=S.V[S.top[0]--];  //将0号栈栈顶元素的值赋值给e1,栈顶元素左移
    n2=S.V[S.top[1]++];  //将1号栈栈顶元素的值赋值给e2,栈顶元素右移
    return OK;
}


  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值