大话数据结构——栈

栈 Stack

栈的定义

栈是仅限在表尾进行插入和删除操作的线性表,又被称为后进先出的线性表,简称LIFO结构

栈可以被视为一种受限制的数组或链表

我们把允许插入和删除的一段称为栈顶,另一端称为栈底,不含任何数据元素的栈称为空栈。

栈是一个线性表,栈底是固定的,最先进的栈只能在栈底。

栈的插入操作,叫做进栈,也称压栈、入栈,英文push

栈的删除操作,叫做出栈,也称弹栈,英文pop

进出栈的时间没有限制,请看这个例子:

3个整形数1、2、3依次进栈,可以有5种出栈次序

1进、1出、2进、2出、3进、3出

1进、2进、3进、1出、2出、3出

......

栈的抽象数据类型

ADT 栈(stack)

Data

同线性表。元素都具有相同的类型,相邻元素具有前驱和后继的关系

Operation

InitStack(*S):建立一个空栈S

DestroyStack(*S):若栈S存在,则销毁

ClearStack(*S): 将栈清空

StackEmpty(S):检查栈S是否为空

GetTop(S,*e):若栈存在且非空,用e返回S的栈顶元素

Push(*S,e):若栈S存在,插入e

Pop(S,e):删除栈顶的元素,并用e返回其值

StackLength(S):返回栈S的元素个数

endADT

栈的顺序存储(顺序栈)

栈的顺序存储是线性表顺序存储的简化,我们简称为顺序栈。

顺序表是用数组来实现的,下标为0的元素作为栈底,定义一个top变量作为栈顶(记录最后一个元素所在的下标)

当栈存在一个元素时,top = 0

空栈,top = -1

typedef struct
{
        int data[MAXSIZE];
        int top;
}SqStack;

进栈操作 O(1)

int Push(SqStack *S,int e)
{
    if(S->top == MAXSIZE - 1)                                  //栈满
        {
            return 0;
        }
    //S->top++;
    S->data[++S->top] = e;
    return 1;
}

出栈操作O(1)

int Pop(SqStack *S,int *e){
    if(s->top = -1)
        return 0;
    *e = S->data[S->top--];           //将要删除的栈顶元素赋给e
    //S->top--;                     //栈顶指针-1
    return 1;
}

两栈共享空间(一般用在两栈需求有相反关系时)

用一个数组存储2个栈,一个栈的栈底在下标为0处,另一个在下标为n-1处

栈1为空栈:top1=-1,栈1满了:top1 = n-1

栈2为空栈:top2=n,栈2满了:top2=0

两个栈都有东西,栈满了:top1+1=top2

typedef struct
{
        int data[MAXSIZE];
        int top1;
        int top2;
}SqDoubleStack;

进栈操作

int push(SqDoubleStack *S,int e,int stackNumber)
{
    if(S->top1 + 1 == S->top2)
        return 0 ;
    if(stackNumber == 1)
        S->data[++S->top1] = e;
    elseif(stackNumber == 2)
        S->data[--S->top2] = e;
    return 1;    
}

出栈操作

int pop(SqDoubleStack *S,int e,int stackNumber)
{
    if(stackNumber == 1)
    {
        if(S->top1 == -1)
            return 1;
        *e=S->data[S->top1--];
    }
    else if(stackNumber == 2)
    {
        if(S->top2 == n)
            return 1;
        *e=S->data[S->top2++];
    }
    return 0;
}

栈的链式存储

栈的链式存储结构,也称为链栈(把链表用栈的规则进行束缚)

链栈不需要头结点,也不需要考虑上限

空栈的情况:top = NULL;

typedef struct Node
{
    int data;
    Node* next;
}Node;
typedef struct StackList
{
    Node* top;
    int size;
}StackList;

链栈的初始化

StackList* NewStackList()
{
    StackList* p = malloc(sizeof(StackList));
    p->top = NULL;
    p->size = 0;
    return p;
}

链栈的析构

void DestroyStackList(StackList* L)
{
     while(L->size){
     Pop(L);
     }
   free(L);
}

进栈操作

Status Push(StackList* L,int e)
{
    Node* p = malloc(sizeof(Node));
    p->data = e;                       //给新结点p添加数据域
    p->next = L->top;                  //给新结点p添加指针域
    L->top = p;                        //更新栈顶指针
    L->size ++;                        //更新栈大小
    return OK;
}

出栈操作

Status Pop(StackList* L)
{
    if(StackList->top = NULL)
        printf("This Stack is empty.\n");
        return 0;
    //*e = L->top->data;
    p = L->top;                        
    L->top = L->top->next;            //更新栈顶指针
    free(p);                          //释放原栈顶指向的结点
    L->size--;                        //更新栈大小
    return OK;
}

栈的应用——递归

斐波那契数列

1,1,2,3,5,8,13,21,34,......

int Fbi(int i)
{
    if(i<2)
       return i == 0 ? 0 : 1;                      //i = 0为真则返回0,否则返回1
    return Fbi(i-1)+Fbi(i-2);
}
​
int main()
{
    int i;
    printf("递归显示斐波那契数列\n");
    for(i=0;i<40;i++)
        printf("%d ",Fbi(i));
    return 0;    
}
​

递归的定义

一个可以直接调用/通过一系列调用语句间接调用自己的函数被称为递归函数

栈的应用——逆波兰表示法

栈的实现方式的对比

支持操作

栈的顺序结构额外支持随机访问,但一般不会用到。栈可以被视为一种受限制的数组或链表

时间效率

基于数组实现的栈:入栈和出栈都是在预先分配好的内存中进行,具有很好的缓存本地性,因此效率较高。

查找

顺序存储结构 O(1) //随机存取的特性:可以直接访问某个元素

单链表O(n) //顺序存取的特性:必须挨个访问元素

插入和删除

顺序存储结构 O(n) //找到要改动的位置之后后边的元素全都要动

单链表在找到位置后O(1)

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值