栈的相关知识以及递归

一、栈

1.栈的抽象数据类型的类型定义

1.1定义

ADT Stack{
数据对象:
    D={ai|ai∈ElemSet,i=1,2,...,n,n>=0}
数据关系:
    R1={<ai-1,ai>|ai-1,ai∈D,i=2,...,n}
    约定an端为栈顶,a1端为栈底。
基本操作;初始化、进栈、出栈、取栈顶元素等
}ADT Stack

1.2基本操作

InitStack(&S) 初始化操作

操作结果:构建一个空栈S。

DestroyStack(&S) 销毁栈操作

初始条件:栈S已存在。

操作结果:栈S被销毁。

StackEmpty(S) 判定S是否为空栈

初始条件:栈S已存在。

操作结果:若栈为空栈,则返回TRUE,否则FALSE。

StackLength(S) 求栈的长度

初始条件:栈S已存在。

操作结果:返回S的元素个数,即栈的长度。

GetTop(S,&e) 取栈顶元素

初始条件:栈S已存在且非空。

操作结果:用e返回S的栈顶元素。

ClearStack(&S) 栈置空操作

初始条件:栈S已存在。

操作结果:将S清为空栈。

Push(&S,e) 入栈操作

初始条件:栈S已存在。

操作结果:插入元素e为新的栈顶元素。

Pop(&S,&e) 出栈操作

初始条件:栈S已存在且非空。

操作结果:删除S的栈顶元素an,并用e返回其值。

2.栈的表示和实现

由于栈本身就是线性表,于是栈也有顺序存储和链式存储两种实现方式。

  • 栈的顺序存储——顺序栈

  • 栈的链式存储——链栈

3.顺序栈的表示和实现

3.1顺序栈

存储方式:同一般线性表的顺序存储结构完全相同

利用一组地址连续的存储单元依次存放自栈底到栈顶的数据元素。栈底一般在低地址端。

  • 附设top指针,指示栈顶元素在顺序栈中的位置。

  • 另设base指针,指示栈底元素在顺序栈中的位置。

但是,为了方便操作,通常top指示真正的栈顶元素之上的下标地址

另外,用stacksize表示栈可使用的最大容量。

3.2空栈与栈满

空栈:base==top是栈空标志

栈满:top-base==stacksize

栈满是的处理方法:

  1. 报错,返回操作系统。

  1. 分配更大的空间,作为栈的存储空间,将原栈的内容移入新栈。

使用数组作为顺序栈存储方式的特点:

简单、方便、但易产生溢出(数组大小固定)

  • 上溢:栈已经满,又要压入元素

  • 下溢:栈已经空,还要弹出元素

:上溢是一种错误,使问题的处理无法进行;而下溢一般认为是一种结束条件,即问题处理结束。

3.3顺序栈的表示

(1)判断是否会栈空,若空则会出错(下溢);

(2)获取栈顶元素e;

(3)栈顶指针减1。

Status Pop(Sqstatus &S,SElemType &e){
//若栈不空,则删除S的栈顶元素,用e返回其值,并返回OK;
否则返回ERROR
    if(S.top==S.base)   //等价于if(StackEmpty(S))
        return ERROR;
    e=*--S.top;        //--S.top;   e=*S.top;
    return OK;
}

栈空,(top==base)

4.链栈的表示和实现

4.1链栈的表示

链栈是运算受限的单链表,只能在链表头部进行操作

链表的特点:

  • 链表的头指针就是栈顶

  • 不需要头结点

  • 基本不存在栈满的情况

  • 空栈相当于头指针指向空

  • 插入和删除仅在栈顶处执行

注意:链栈中指针的方向

4.2链栈的入栈

Status Push(LinkStack &S,SElemType e){
    p=new StackNode;           //生成新结点p
    p->data=e;                 //将新结点数据域置为e
    p->next=S;                 //将新结点插入栈顶   
    S=p;                       //修改栈顶指针
    return OK;
}

4.3链栈的出栈

Status Pop(LinkStack &S,SElemType &e){
    if(S==NULL)return ERROR;
    e=S->data;
    p=S;
    S=S->next;
    delete p;
    return OK;
}

4.4取栈顶元素

SElemType GetTop(LinkStack S){
    if(S!=NULL)
    return S->data;
}

二、栈和递归

    • 递归的定义

若一个对象部分地包含它自己,或用它自己给自己定义,则称这个对象是递归的;

若一个过程直接地或间接地调用自己,则称这个过程是递归的过程。

例如:递归求n的阶乘

long Fact(long n){
    if(n==0)return 1;
    else return n*Fact(n-1);
}

以下三种情况常常用到递归方法:

  • 递归定义的数学函数

  • 具有递归特性的数据结构

  • 可递归求解的问题

    • 递归问题——用分治法求解

2.1分治法

分治法:对于一个较为复杂的问题,能够分解成几个相对简单的且解法相同或类似的子问题来求解

2.2必备的三个条件

  1. 能将一个问题转变成一个新问题,而新问题与原问题的解法相同或类同,不同的仅是处理的对象,且这些处理对象是变化有规律的;

  1. 可以通过上述转化而使问题简化;

  1. 必须一个明确的递归出口,或称递归的边界。

2.3分治法求解递归问题算法的一般形式

void p(参数表){
    if (递归结束条件)可直接求解步骤;——基本项
    else p(较小的参数);——归纳项

例如:

long Fact(long n){
    if(n==0)return 1;        //基本项
    else return n*Fact(n-1)  //归纳项
}

2.4函数调用过程

调用前,系统完成:

  1. 实参返回地址等传递给被调用函数;

  1. 为被调用函数的局部变量分配存储区;

  1. 将控制转移到被调用函数的入口

调用后,系统完成:

  1. 保存被调用函数简单计算结果

  1. 释放被调用函数的数据区

  1. 依照被调用函数保存的返回地址将控制转移到调用函数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值