@栈

特殊的线性表——栈、队列
从数据结构角度看,栈和队列是操作受限的线 性表,他们的逻辑结构相同。
栈的逻辑结构
空栈:不含任何数据元素的栈。
(a1, a2, ……, an)
栈:限定仅在表尾进行插入和删除操作的线性表。
栈顶

示意图
栈的操作特性:后进先出(LIFO)
注意:栈只是对表插入和删除操作的位置进行了限 制,并没有限定插入和删除操作进行的时间。
栈的抽象数据类型定义 特殊线性表——栈 特殊线性表——栈 ADT Stack Data
栈中元素具有相同类型及后进先出特性, 相邻元素具有前驱和后继关系 Operation InitStack
DestroyStack 前置条件:栈已存在 输入:无 功能:销毁栈 输出:无 后置条件:释放栈所占用的存储空间
Push 前置条件:栈已存在 输入:元素值x 功能:在栈顶插入一个元素x 输出:如果插入不成功,抛出异常 后置条件:如果插入成功,栈顶增加了一个元素 前置条件:栈不存在 输入:无 功能:栈的初始化 输出:无 后置条件:构造一个空栈
Pop 前置条件:栈已存在 输入:无 功能:删除栈顶元素 输出:如果删除成功,返回被删元素值,否则,抛出异常
后置条件:如果删除成功,栈减少了一个元素
GetTop 前置条件:栈已存在 输入:无 功能:读取当前的栈顶元素 输出:若栈不空,返回当前的栈顶元素值 后置条件:栈不变
Empty 前置条件:栈已存在 输入:无 功能:判断栈是否为空 输出:如果栈为空,返回1,否则,返回0 后置条件:栈不变 endADT
栈的顺序存储结构——顺序栈
确定用数组的哪一端表示栈底。
特殊线性表——栈 特殊线性表——栈
如何改造数组实现栈的顺序存储?

  1. 确定用数组的哪一端表示栈底。
  2. 附设指针top指示栈顶元素在数组中的位置。附设指针top指示栈顶元素在数组中的位置。
    进栈:top加1 出栈:top减1
    栈空:top= -1 栈满:top= StackSize-1
    顺序栈类的声明

const intStackSize=100;
template
class SeqStack
{ public:
SeqStack( ){top=-1;} ;
~SeqStack( );
void Push ( T x );
T Pop ( );
T GetTop( ) {if (top!=-1) return data[top];}
bool Empty ( ) { if(top==-1) return 1;else return 0;}
private: T data[StackSize]; int top; } ;
顺序栈的实现——入栈
template
void SeqStack::Push( T x)
{ if (topStackSize-1)
throw “溢出”; top++;
data[top]=x; }
顺序栈的实现——出栈
template
T SeqStack:: Pop ( )
{ if (top
-1) throw “溢出”;
x=data[top–]; return x; }
两栈共享空间
解决方案1: 直接解决:为每个栈开辟一个数组空间。
在一个程序中需要同时使用具有相同数据类型的 两个栈,如何顺序存储这两个栈?
解决方案2: 顺序栈单向延伸——使用一个数组来存储两个栈
特殊线性表——栈 特殊线性表——栈
会出现什么问题?如何解决?
**两栈共享空间:**使用一个数组来存储两个栈,让一个 栈的栈底为该数组的始端,另一个栈的栈底为该数组 的末端,两个栈从各自的端点向中间延伸。
栈1的底固定在下标为0的一端; 栈2的底固定在下标为StackSize-1的一端。 top1和top2分别为栈1和栈2的栈顶指针; StackSize为整个数组空间的大小
什么时候栈 1为空?top1= -1
栈2为空?top2= StackSize
什么时候栈满?top2= top1+1

两栈共享空间类的声明
const intStackSize=100;
template
classBothStack
{ public: BothStack( )
{top1= -1; top2=StackSize;} ~BothStack( );
void Push(inti, T x);
T GetTop(inti);
bool Empty(inti);
private: T data[StackSize]; inttop1, top2; };

  1. 如果栈满,则抛出上溢常; 2. 判断是插在栈1还是栈2; 2.1 若在栈1插入,则 2.1.1 top1加1; 2.1.2 在top1处填入x; 2.2 若在栈2插入,则 2.2.1 top2减1; 2.2.2 在top2处填入x;
    template
    void BothStack::Push(inti, T x )
    { if (top1top2-1) throw “上溢”;
    if (i
    1) data[++top1]=x;
    if (i2) data[–top2]=x; }
    两栈共享空间的实现——删除 操作接口:T Pop(inti);
    若是在栈1删除,则 1.1 若栈1为空栈,抛出下溢异常; 1.2 删除并返回栈1的栈顶元素; 2. 若是在栈2删除,则 2.1 若栈2为空栈,抛出下溢异常; 2.2 删除并返回栈2的栈顶元素;
    T BothStack::Pop(inti)
    { if (i
    1) //将栈1的栈顶元素出栈
    { if (top1== -1) throw “下溢”;
    return data[top1–];} if (i2) //将栈2的栈顶元素出栈
    {if (top2
    StackSize) throw “下溢”; return data[top2++]; } }
    两栈共享空间的实现——读取栈i当前的栈顶元素 操作接口:T GetTop(inti);
    template
    T BothStack::GetTop(inti)
    { if(i1) { if (top1!=-1) return data[top1];}
    if(i
    2) { if(top2!=StackSize) return data[top2]; } }
    两栈共享空间的实现——判断栈i是否为空栈 操作接口:boolEmpty(inti);
    template
    boolBothStack::Empty(inti)
    { if(i1) { if(top1-1) return 1;
    else return 0;} if(i2) {
    if(top2
    StackSize) return 1;
    else return 0;} }
    栈的链接存储结构及实现 栈的链接存储结构——链栈
    shiyi
    链 栈 的 类 声 明
    template
    class LinkStack
    { public: LinkStack(){top=NULL;} ~LinkStack( ); void Push(T x); T Pop( ); T GetTop( ){if(top!=NULL)returntop->data;} boolEmpty ( ){return(topNULL? 1: 0);} private: Node *top; }
    template
    void LinkStack::Push(Tx)
    template
    T LinkStack::Pop( )
    { if (top
    NULL) throw “下溢”;
    x=top->data;
    p=top; top=top->next;
    delete p; return x; }
    { s=new Node;
    s->data=x; s->next=top; top=s; }
    顺序栈和链栈的比较
    时间性能:相同,都是常数时间O(1)。 空间性能: ¾顺序栈:有元素个数的限制和空间浪费的问题。 ¾链栈:没有栈满的问题,只有当内存没有可用空 间时才会出现栈满,但是每个元素都需要一个指针 域,从而产生了结构性开销。
    总之,当栈的使用过程中元素个数变化较大时,用 链栈是适宜的,反之,应该采用顺序栈。
    1 递归的定义
    子程序(或函数)直接调用自己或通过一系列调 用语句间接调用自己,是一种描述问题和解决问 题的基本方法。
    2 递归的基本思想
    问题分解:把一个不能或不好解决的大问题转化 为一个或几个小问题,再把这些小问题进一步分 解成更小的小问题,直至每个小问题都可以直接 解决。
    3 递归的要素
    ⑴递归边界条件:确定递归到何时终止,也称 为递归出口;
    ⑵递归模式:大问题是如何分解为小问题的, 也称为递归体。
    队列的逻辑结构
    空队列:不含任何数据元素的队列。
    队列:只允许在一端进行插入操作,而另一端进行 删除操作的线性表。
    允许插入(也称入队、进队)的一端称为队尾,允 许删除(也称出队)的一端称为队头。
    (a1, a2, ……, an)
    队列的抽象数据类型定义
    ADT Queue Data
    队列中元素具有相同类型及先进先出特性, 相邻元素具有前驱和后继关系
    Operation InitQueue 前置条件:队列不存在 输入:无 功能:初始化队列 输出:无 后置条件:创建一个空队列
    DestroyQueue 前置条件:队列已存在 输入:无 功能:销毁队列 输出:无 后置条件:释放队列所占用的存储空间 EnQueue 前置条件:队列已存在 输入:元素值x 功能:在队尾插入一个元素 输出:如果插入不成功,抛出异常 后置条件:如果插入成功,队尾增加了一个元素
    DeQueue 前置条件:队列已存在 输入:无 功能:删除队头元素 输出:如果删除成功,返回被删元素值 后置条件:如果删除成功,队头减少了一个元素 GetQueue 前置条件:队列已存在 输入:无 功能:读取队头元素 输出:若队列不空,返回队头元素 后置条件:队列不变
    Empty 前置条件:队列已存在 输入:无 功能:判断队列是否为空 输出:如果队列为空,返回1,否则,返回0 后置条件:队列不变 endADT
    Empty 前置条件:队列已存在 输入:无 功能:判断队列是否为空 输出:如果队列为空,返回1,否则,返回0 后置条件:队列不变 endADT
    如何确定不同的队空、队满的判定条件? 为什么要将队空和队满的判定条件分开?
    方法一:附设一个存储队列中元素个数的变量num, 当num=0时队空,当num=QueueSize时为队满; 方法二:修改队满条件,浪费一个元素空间,队满时 数组中只有一个空闲单元; 方法三:设置标志flag,当front=rear且flag=0时为队 空,当front=rear且flag=1时为队满。
    const intQueueSize=100;
    template
    class CirQueue
    { public: CirQueue( )
    {front=rear=QueueSize-1;}
    ~ CirQueue( );
    void EnQueue(Tx);
    T DeQueue( );
    T GetQueue( );
    bool Empty(
    ){return(frontrear? 1: 0);}
    private: T data[QueueSize];
    int front, rear; };
    template
    void CirQueue::EnQueue(Tx )
    { if ((rear+1)% QueueSize
    front)
    throw “上溢”;
    rear=(rear+1)% QueueSize;
    data[rear]=x; }
    template
    T CirQueue::DeQueue( )
    { if (rearfront)throw “下溢”;
    front=(front+1)% QueueSize;
    return data[front]; }
    template
    T CirQueue::GetQueue( )
    { if (rear
    front)throw “下溢”;
    i=(front+1)% QueueSize;
    return data[i]; }
    链 队 列 类 的 声 明
    template
    class LinkQueue
    { public: LinkQueue( );
    ~LinkQueue( );
    void EnQueue(Tx);
    T DeQueue( );
    T GetQueue( );
    intEmpty( )
    {return(frontrear?1:0);}
    private: Node *front, *rear; };
    LinkQueue( );
    template
    LinkQueue::LinkQueue( )
    { front=new Node;
    front->next=NULL; rear=front; }
    链队列的实现——入队
    template
    void LinkQueue::EnQueue(Tx )
    { s=new Node; s->data=x; s->next=NULL; rear->next=s; rear=s; }
    链队列的实现——出队
    template
    T LinkQueue::DeQueue( )
    { if (rear
    front) throw “下溢”;
    p=front->next;
    x=p->data;
    front->next=p->next;
    if (p->nextNULL) rear=front;
    delete p; return x; }
    时间性能: 循环队列和链队列的基本操作都需要常数时间O (1)。
    空间性能:
    循环队列:必须预先确定一个固定的长度,所以有 存储元素个数的限制和空间浪费的问题。
    链队列:没有队列满的问题,只有当内存没有可用 空间时才会出现队列满,但是每个元素都需要一个 指针域,从而产生了结构性开销。
    括号匹配的检验
    分析可能出现的不匹配的情况: ™到来的右括号并非是所“期待”的; [ ( ]) ¾到来的是“不速之客”; [ ( ) ] ) 或) ƒ直到结束,也没有到来所“期待”的括号。 ( ( 这三种情况对应到栈的操作即为: ™和栈顶的左括号不相匹配; ¾栈中并没有左括号等在哪里; ƒ栈中还有左括号没有等到和它相匹配的右括号。
    算法思想:
    1)凡出现左括号,则进栈;
    2)凡出现右括号,首先检查栈是否空 若栈空,则表明该“右括号”多余, 否则和栈顶元素比较, 若相匹配,则“左括号出栈”, 否则表明不匹配。
    3)表达式检验结束时, 若栈空,则表明表达式中匹配正确, 否则表明“左括号”有余。
    boolmatching(charexp[])
    { // 检验表达式中所含括号是否正确嵌套,
    //若是,则返回TRUE,否则返回FALSE. ‘#’ 为表达式的结束符 intstate = 1,i=0; char ch,e; ch= exp[i++]; SeqStack S; // 构造空栈 while (ch!=’\0’ && state) { if(ch
    ’(’ || ch==’[’) S.Push(ch); // 凡左括号一律入栈 else if(ch== ‘)’) if (!S.Empty() && S.GetTop()’(’) e=S.Pop(); else state = 0; else if(ch’]’) if (!S.Empty() && S.GetTop()==’[’) e=S.Pop(); else state = 0; ch= exp[i++]; } // while if ( state && S.Empty() ) return 1; else return 0; }//matching
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值