顺序栈链栈双端栈

Posted tianzeng

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了顺序栈链栈双端栈相关的知识,希望对你有一定的参考价值。

定义

  栈(Stack)又称堆栈,它是一种运算受限的线性表,其限制是仅允许在表的一端进行插入和删除运算。 由于栈的插入和删除运算仅在栈顶一端进行,后进栈的元素必定先出栈,所以又把栈称为后进先出表(Last In First Out, 简称LIFO)。

栈的存储结构

一:顺序存储

    栈的顺序存储结构同样需要使用一个数组和一个整型变量来实现,利用数组来顺序存储栈中的所有元素,利用整型变量来存储栈顶元素的下标位置

typedef struct Node
{
    int data[max];
    int top;
}SeqStack;

max为一个整型全局常量,需事先通过const语句定义,由它确定顺序栈(即顺序存储的栈)的最大深度,又称为长度,即栈最多能够存储的元素个数;由于top用来指示栈顶元素的位置,所以把它称为栈顶指针。

 在顺序存储的栈中,top的值为-1表示栈空,每次向栈中压入一个元素时,首先使top增1,用以指示新的栈顶位置,然后再把元素赋值到这个位置上,每次从栈中弹出一个元素时,首先取出栈顶元素,然后使top减1,指示前一个元素成为新的栈顶元素。由此可知,对顺序栈的插入和删除运算相当于是在顺序表(即顺序存储的线性表)的表尾进行的,其时间复杂度为O(1)。

  1.初始化栈

void InitStack(Stack& S) // 初始化栈S,即把它置为空
{
    S.top=-1;
}

   2.把一个栈清除为空   

void ClearStack(Stack& S)// 清除栈S中的所有元素,使之成为一个空栈,在顺序存储方式下,同初始化栈的算法相同。       
{
    S.top=-1;
}

  3.检查一个栈是否为空

int StackEmpty(Stack& S) // 判断S是否为空,若是则返回1,否则返回0           
{
    return S.top==-1;
}

  4. 读取栈顶元素

ElemType Peek(StackType& S)// 返回S的栈顶元素,但不移动栈顶指针
{
    //若栈为空则终止程序运行
    if(S.top==-1) 
    {
        cerr<<"Stack is empty!"<<endl;
        exit(1);
    }
    //返回栈顶元素的值
    return S.stack[S.top];
}

   5.进栈

void Push(Stack& S, const ElemType& item)// 元素item进栈,即插入到栈顶           
{
    //若栈已满则终止程序运行
    if(S.top==StackMaxSize-1) 
    {
        cerr<<"Stack overflow!"<<endl;
        exit(1);
    }
    //将栈顶指针后移一个位置
    S.top++;
    //将item的值赋给新的栈顶位置
    S.stack[S.top]=item;
}

  6.出栈

ElemType Pop(StackType& S)// 删除栈顶元素并返回之             
{
    // 若栈为空则终止程序运行
    if(S.top==-1) 
    {
        cerr<<"Stack is empty!"<<endl;
        exit(1);
    }
    // 暂存栈顶元素以便返回
    ElemType temp=S.stack[S.top];
    // 栈顶指针前移一个位置
    S.top--;
    // 返回原栈顶元素的值
    return temp;
}

    从出栈算法可以看出,原栈顶元素的值没有被改变,所以可以不使用临时变量保存它,返回语句中返回S.stack[S.top+1]的值即可

  7.检查栈是否已满       

int StackFull(Stack& S)// 若栈已满则返回1,否则返回0,此函数为顺序栈所特有
{
    return S.top==StackMaxSize-1; 
}

完整代码

#include<stdio.h>
#define max 3 

typedef struct Node
{
    int data[max];
    int top;
}SeqStack;

void InitStack(SeqStack *s)
{
    s->top=-1;
}

void Push(SeqStack *s,int a[],int k)
{
    if(s->top==max-1)
    {
        printf("The stack is full!
");
        return;
    }
    else
    {
        for(int i=0;i<=k;i++)
        {
            s->top++;
            s->data[s->top]=a[i];
        }
        printf("Successful entry into the stack!
");
        return;
    }
}
void Print(SeqStack *s)
{
    printf("Data in the Stack:
");
    for(int i=0;i<=s->top;i++)
        printf("%-3d",s->data[i]);
    printf("
");
}

int Pop(SeqStack *s)
{
    if(s->top==-1)
    {
        printf("Stack empty!
");
        return -1;
    }
    else
    {
        s->top--;
        printf("Successful leave into the stack!
");
    }
}

void P_rint(SeqStack *s)
{
    printf("Now data in the Stack is:
");
    for(int i=0;i<=s->top;i++)
        printf("%-3d",s->data[i]);
    printf("
");
}
int main()
{
    int a[max],k=-1,x,result; 
    SeqStack s;
    InitStack(&s);
    printf("Please input the data:
");
    for(int i=0;i<max;i++)
    {
        scanf("%d",&x);
        if(x==0)
            break;
        
        a[i]=x;
        k++;
            
    }
    Push(&s,a,k);
    Print(&s);
    result=Pop(&s);
    if(result==-1)
        return 0;
    P_rint(&s);
    return 0;
}

二:链式存储

   栈的链接存储结构与线性表的链接存储结构相同,是通过由结点构成的单链表实现的,此时表头指针被称为栈顶指针,由栈顶指针指向的表头结点被称为栈顶结点,整个单链表被称为链栈,即链接存储的栈。当向一个链栈插入元素时,是把该元素插入到栈顶,即使该元素结点的指针域指向原来的栈顶结点,而栈顶指针则修改为指向该元素结点,使该结点成为新的栈顶结点。当从一个链栈中删除元素时,是把栈顶元素结点删除掉,即取出栈顶元素后,使栈顶指针指向原栈顶结点的后继结点。由此可知,对链栈的插入和删除操作是在单链表的表头进行的,其时间复杂度为O(1)。

  1.初始化链栈       

void InitStack(LNode*& HS)
{
    HS=NULL;  // 将链栈置空。
}

  2.清除链栈为空     

void ClearStack(LNode*& HS)
{
    LNode *cp, *np;  
    // 用cp作为指向待删除的结点,np指向cp的后继结点。
    cp=HS;  // 给cp指针赋初值,使之指向栈顶结点。
    while(cp!=NULL) 
    { // 从栈顶到栈底依次删除每个结点
        np=cp->next; 
        delete cp;  
        cp=np;  
    }
    HS=NULL;  // 置链栈为空
}

  3.检查链栈是否为空       

int StackEmpty(LNode* HS)// HS为值参或引用形参均可    
{
    return HS==NULL;
}

   4.读取栈顶元素       

ElemType Peek(LNode* HS)  // HS为值参或引用形参均可
{
    if(HS==NULL)  
    {
        cerr<<"Linked stack is empty!"<<endl;
        exit(1);
    }
    return HS->data;
}

  5.进栈

void Push(LNode*& HS, const ElemType& item)
{
    // 为插入元素获取动态结点
    LNode* newptr= new LNode;
    if(newptr==NULL)  
    {
        cerr<<"Memory allocation failare!"<<endl;
        exit(1);
    }
    // 给新分配的结点赋值
    newptr->data=item;
    // 向栈顶插入新结点
    newptr->next=HS;
    HS=newptr;
}

  6.出栈

ElemType Pop(LNode*& HS)
{
    if(HS==NULL) 
    {
        cerr<<"Linked stack is empty!"<<endl;
        exit(1);
    }
    LNode* p=HS;  // 暂存栈顶结点
    HS=HS->next;  // 使栈顶指针指向其后继结点
    ElemType temp=p->data;  // 暂存原栈顶元素
    delete p;  // 回收原栈顶结点
    return temp;  // 返回原栈顶元素
}

三:双端顺序栈

概念

  在程序中经常需要同时使用多个栈的情况,若使用顺序栈会对栈的大小难以准确估计,从而产生有栈的溢出,为了解决这个问题,让多个栈共享一个足够大的数组空间,利用栈的动态性使存储空间相互补充。

  在顺序栈的共享技术中,最常用的就是两个栈共享,即双端栈。利用栈低位置不变,栈顶变化的特性。首先申请一个一维数组空间,S[M],将两个栈的栈低分别放在数组的两端,分别是0和M-1,由于是栈顶动态变化的,这样可以形成互补,使得每个栈可用的最大空间与需求有关,由此可见,两个共享栈比两个栈分别申请M/2哥空间利用率高。

typedef struct Stack
{
    int data[max];
    int top[2];
}Dqstack;

进栈及出栈代码

#include<stdio.h>
#define max 5

typedef struct Stack
{
    int data[max];
    int top[2];
}Dqstack;

void InitDqstack(Dqstack *s)
{
    s->top[0]=-1;
    s->top[1]=max;
}

void Push(Dqstack *s,int a[],int k,int i)
{
    if(s->top[0]+1==s->top[1])
    {
        printf("The stack is full!
");
        return;
    }
    else
    {
        switch(i)
        {
            case 0:
            {
                for(int i=0;i<=k;i++)
                {
                    s->top[0]++;
                    s->data[s->top[0]]=a[i];
                }
                break;
            }
            case 1:
            {
                for(int i=0;i<=k;i++)
                {
                    s->top[1]--;
                    s->data[s->top[1]]=a[i];
                }
                break;    
            }
            default:
                return;
        }
        printf("Successful entry into the stack!
");
    }
}

int Pop(Dqstack *s,int _i)
{
    switch(_i)
    {
        case 0:
            {
                if(s->top[0]==-1)
                {
                    printf("Stack empty!
");
                    return -1;
                }
                else
                {
                    s->top[0]--;
                    printf("Successful leave into the stack!
");
                }
                break;
            }
        case 1:
            {
                if(s->top[1]==max-1)
                {
                    printf("Stack empty!
");
                    return -1;
                }
                else
                {
                    s->top[1]++;
                    printf("Successful leave into the stack!
");
                }
                break;
            }
        default:
            return -1;
    }
}
void P_rint(Dqstack *s,int _i,int k)
{
    printf("Now data in the Stack is:
");
    switch(_i)
    {
        case 0:
            {
                for(int i=s->top[0];i>=0;i--)
                    printf("%-3d",s->data[i]);
                break;
            }
        case 1:
            {
                for(int i=s->top[1];i<max;i++)
                    printf("%-3d",s->data[i]);
                break;
            }
        default:
            return;
    }
    
    printf("
");
}

int main()
{
    int i,_i,a[max],x,k=-1,result;
    Dqstack s;
    InitDqstack(&s);
    printf("Please input the data (x):
");
    for(int i=0;i<max;i++)
    {
        scanf("%d",&x);
        if(x==0)
            break;
        
        a[i]=x;
        k++;        
    }
    
    printf("Please input enter the (i) push top[0] or top[1]:
");    
        scanf("%d",&i);
    Push(&s,a,k,i);

    printf("Please input out the (_i) pop top[0] or top[1]:
");    
        scanf("%d",&_i);
    result=Pop(&s,_i);
    if(result==-1)
        return 0;
    P_rint(&s,_i,k);

    return 0;
}

 

以上是关于顺序栈链栈双端栈的主要内容,如果未能解决你的问题,请参考以下文章

(王道408考研数据结构)第三章栈和队列-第二节:队列基本概念顺序栈和链栈基本操作

栈和队列

[NEFU 数据结构]阶段一复习

Python数据结构系列❤️《栈(顺序栈与链栈)》——❤️知识点讲解+代码实现

数据结构之栈和队列熬夜暴肝,有亿点详细

数据结构于算法总览