数据结构看书笔记(四)--栈与队列

栈与队列
 栈是限定尽在表尾进行插入和删除操作的线性表 
 队列是只允许在一端进行插入操作、而在另一端进行删除操作的线性表
栈的定义:

栈(Stack)是限定仅在表尾进行插入和删除操作的线性表
 其中允许插入的一端称为栈顶(top),另一端称为栈底(bottom),不含任何数据元素的栈称为空栈。栈又称为先进后出(Last In First Out)的线性表,简称为LIFO结构。
 特殊之处在与限制了这个线性表的插入和删除位置只能是在栈顶。
 
 栈的插入操作,叫做进栈,也称为压栈、入栈。
 栈的删除操作,叫做出栈,也有的叫做弹栈。

栈的抽象数据类型:

ADT 栈(Stack)
    Data
        同线性表。元素具有相同的类型,相邻元素具有前驱和后继关系。
    Operation
        InitStatic(*S):初始操作,建立一个空栈S
        DestroyStack(*S):若栈存在则销毁它。
        ClearStack(*S):将栈清空
        StackEmpty(S):若栈为空,则返回true,否则返回false
        GetTop(S,*s):若栈存在且非空,用e返回S的栈顶元素
        Push(*S,e):若栈S存在,插入新的元素e到
        Pop(*S,*e):删除栈S中栈顶元素,并用e返回其值
        StackLength(S):返回栈S的元素个数。
    endADT

栈的顺序存储结构及实现:
 栈的顺序存储结构
 定义:

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

    Status Push(SqStack *s,SElemType e)
    {
        if(S->top==MAXSIZE-1)
            return ERROR;
        S->top++;
        s->data[S->top] = e;
        return OK;
    }

栈的顺序存储结构--进栈操作

    Status Pop(SqStack *S,SElemType *e)
    {
        if(S->top==-1)
            return ERROR;
        *e = S->data[S->top];
        S->top--;
        return OK;
    }

栈的顺序存储结构--出栈操作

两栈共享空间
 两栈共享空间的结构的代码如下:

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

    Status Push(SqDoubleStack *S,SElemType e,stackNumber)
    {
        if(S->top+=S->top2)//栈满
            return ERROR;
        if(stackNumber==1)
            S->data[++S->top1] = e;
        else if(stackNumber==2)
            S->data[--S->top2] = e;
        return OK;
    }

    Status Pop(SqDoubleStack *S,SElemType *e,int stackNumber)
    {
        if(stackNumber==1)
        {
            if(S->top1==-1)
                return ERROR;
            *e=S->data[S->top1--];
        }
        else if(stackNumber==2)
        {
            if(S-top2==MAXSIZE)
                return ERROR;
            *e=S->data[S->top2++];
        }
        return OK;
    }

栈的链式存储结构及实现
 对于链栈来说基本不存在栈满的情况,除非内存已经没有可以使用的空间。
 链栈的结构代码如下:

typedef struct StackNode
    {
        SElemType data;
        struct StackNode *next;
    }StackNode,*LinkStackPtr;

    typedef struct LinkStack
    {
        LinkStackPtr top;
        int count;
    }LinkStack;

    Status Push(LinkStack *S,SElemType e)
    {
        LinkStackPtr s = (LinkStackPtr)malloc(sizeof(StackNode));
        s->data = e;
        s->next = S->top;
        s->top = s;
        S->count++;
        return OK;
    }

栈的链式存储结构--进栈操作

    Status Pop(LinkStack *S,SElemType *e)
    {
        LinkStackPtr p;
        if(StackEmpty(*S))
            return ERROR;
        *e=S->top->data;
        p = S->top;
        S->top = S->top->next;
        free(p);
        S->count--;
        return OK;
    }

栈的链式存储结构--出栈操作

注:关于顺序栈和链栈的讨论
   如果栈的使用过程中元素变化不可预料,有时很小,有时非常大,那么最好是使用链栈,反之,如果它的变化在可控的范围内,建议使用顺序栈会更好一些。
   栈的作用:栈的引入简化了程序设计的问题,划分了不同关注层次,使得思考范围缩小,更加聚焦于我们要解决的问题核心。反之,像数组等,因为要分散精力去考虑数组的下标增减等细节问题,反而掩盖了问题的本质。

栈的应用--递归
 斐波那契数列(Fibonacci)

    //直接计算
    int main()
    {
        int i;
        int a[40];
        a[0] = 0;
        a[1] = 1;
        printf("%d",a[0]);
        printf("%d",a[1]);
        for(i=2;i<40;i++)
        {
            a[i] = a[i-1]+a[i-2];
            printf("%d\n",a[i]);
        }
        return 0;
    }
//使用递归计算,测试后知道,使用递归会急剧增加运算时间
    int Fbi(int i)
    {
        if(i<0)
            return 0;
        else if(i==0)
            return 0;
        else if(i==1)
            return 1;
        else
            return Fbi(i-1)+Fbi(i-2);
    }
    int main()
    {
        int i;
        for(i=0;i<40;i++)
            printf("%d\n",Fbi(i));
        return 0;
    }

对递归的定义
    把一个直接调用自己活通过一系列的调用语句间接的调用自己的函数,称做递归函数。
    每个递归定义至少必须有一个条件,满足递归时不再进行,即不再引用自身而是返回值退出。

栈的应用--四则运算表达式求值:
 后缀(逆波兰)表示法定义:不需要括号的后缀表示法,我们也把它称为逆波兰(Reverse Polish Notation,RPN)表示。

后缀表达式计算结果:
 此处为了表达方便,应有配图
 
 中缀表达式转后缀表达式:
 此处为了表达方便,应有配图

队列的定义:
   队列是只允许在一端进行插入操作、而在另一端进行删除操作的线性表。
   队列是一种先进先出(First in First Out)的线性表,简称为FIFO.允许插入的一端称为队尾,允许删除的一端称为队头。

队列的抽象数据类型:

    ADT 队列(Queue)
    Data
        同线性表。元素具有相同的类型,相邻元素具有前驱和后继关系。
    Operation
        InitQueue(*Q):初始化操作,建立一个空队列Q.
        DestroyQueue(*Q):若队列Q存在则销毁它。
        ClearQueue(*Q):将队列清空
        QueueEmpty(Q):若队列为空则返回true,否则返回false
        GetHead(Q,*e):若队列存在且非空,用e返回队列Q的队头元素。
        EnQueue(*Q,e):若队列Q存在,插入新元素e到队列Q中并成为队尾元素
        DeQueue(*Q,*e):删除队列Q的队头元素,并且用e返回其值
        QueueLength(Q):返回队列Q的元素个数
    endADT

循环队列
   队列也存在顺序存储和链式存储
 
   队列顺序存储的不足:队头指针和队尾指针的问题,如果不循环,那么就可能造成位置空缺的问题。
 
   循环队列的定义:队列的头尾相接的顺序存储结构称为循环队列。
   队列满的条件是:

(rear+1)%QueueSize == front

    (取模“%”的目的就是为了整合rear与front大小为一个问题)

  通用的计算队列长度的公式:

  (rear-front+QueueSize)%QueueSize

  定义:

    typedef int QElemType;
    typedef struct
    {
        QElemType data[MAXSIZE];
        int front;
        int rear;
    }SqQueue;

  操作:

    //初始化空队列Q
    Status InitQueue(SqQueue *Q)
    {
        Q->front = 0;
        Q->rear = 0;
        return OK;
    }

    //返回Q的元素个数
    int QueueLength(SqQueue Q)
    {
        return (Q.rear-Q.front+MAXSIZE)%MAXSIZE;
    }

    //若队列未满,则插入元素e为Q新的队尾元素
    Status EnQueue(SqQueue *Q,QElemType e)
    {
        if((Q->rear+1)%MAXSIZE==Q->front)
            return ERROR;
        Q->data[Q->rear] = e;
        Q->rear = (Q->rear+1)%MAXSIZE;

        return OK;
    }

    //出队列
    Status DeQueue(SqQueue *Q,QElemType *e)
    {
        if(Q->front = Q->rear) //队列为空
            return ERROR;
        *e=Q->data[Q->front];
        Q->front = (Q->front+1)%MAXSIZE;
        return OK;
    }

队列的链式存储结构及实现

    队列的链式存储结构,其实就是线性表的单链表,只不过它只能尾进头出而已,我们把它简称为链队列。

    链队列的结构为:

    typedef int QElemType;
    typedef struct QNode
    {
        QElemType data;
        struct QNode *next;
    }QNode,*QueuePtr;

    typedef struct
    {
        QueuePtr front,rear;
    }LinkQueue;    

    //入队操作
    Status EnQueue(LinkQueue *Q,QElemType e)
    {
        QueuePtr s = (QueuePtr)malloc(sizeof(QNode));
        if(!s)    //存储分配失败
            exit(OVERFLOW);
        s->data = e;
        s->next = NULL;
        Q->rear->next = s;

        Q->rear = s;
        return OK;
    }

入队操作

    //出队操作
    Status DeQueue(LinkQueue *Q,QElemType *e)
    {
        QueuePtr p;
        if(Q->front==Q->rear)
            return ERROR;
        p=Q->front->next;
        *e = p->data;
        Q->front->next = p->next;

        if(Q->rear==p)
            Q->rear = Q->front;
        free(p);
        return OK;
    }

出队操作

本章内容

  栈
      顺序栈
      两栈共享空间

  链栈
  队列

    顺序队列

    循环队列
    链队列

时间: 2024-12-25 05:00:35

数据结构看书笔记(四)--栈与队列的相关文章

4、蛤蟆的数据结构笔记之四栈和队列定义

4.蛤蟆的数据结构笔记之四栈和队列定义 本篇名言:"人生应该如蜡烛一样,从顶燃到底,一直都是光明的." 今天学习栈和队列了.从第二篇学习时候我们知道,其实也是线性表的一种. 我们先来看下定义. 欢迎转载,转载请标明出处: 1.  栈 栈(stack)又名堆栈,它是一种运算受限的线性表.其限制是仅允许在表的一端进行插入和删除运算.这一端被称为栈顶,相对地,把另一端称为栈底.向一个栈插入新元素又称作进栈.入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素:从一个栈删除元素又称

浅谈算法和数据结构(1):栈和队列

浅谈算法和数据结构(1):栈和队列 2014/11/03 ·  IT技术                                         · 2 评论                                      ·  数据结构, 栈, 算法, 队列 分享到: 60 SegmentFault D-Day 2015 北京:iOS 站 JDBC之“对岸的女孩走过来” CSS深入理解之relative HTML5+CSS3实现春节贺卡 原文出处: 寒江独钓   欢迎分享原创

小猪的数据结构辅助教程——3.1 栈与队列中的顺序栈

小猪的数据结构辅助教程--3.1 栈与队列中的顺序栈 标签(空格分隔): 数据结构 本节学习路线图与学习要点 学习要点 1.栈与队列的介绍,栈顶,栈底,入栈,出栈的概念 2.熟悉顺序栈的特点以及存储结构 3.掌握顺序栈的基本操作的实现逻辑 4.掌握顺序栈的经典例子:进制变换的实现逻辑 1.栈与队列的概念: 嗯,本节要进行讲解的就是栈 + 顺序结构 = 顺序栈! 可能大家对栈的概念还是很模糊,我们找个常见的东西来拟物化~ 不知道大家喜欢吃零食不--"桶装薯片"就可以用来演示栈! 生产的时

易学设计模式看书笔记(4) - 抽象工厂模式

 本文内容来自书上,不懂设计模式,只求混个眼熟. 三.抽象工厂模式 1.动物管理系统的例子 public interface Animal{ public void eat(); } public class Tiger implements Animal { public void eat(){ sysout.out.println("老虎会吃"); }; public void run(){ sysout.out.println("老虎会跑"); }; } pu

易学设计模式看书笔记(2) - 简单工厂模式

本文摘自易学设计模式一书 一.简单工厂模式 1.动物管理系统的例子 public interface Animal{ public void eat(); } public class Tiger implements Animal { public void eat(){ sysout.out.println("老虎会吃"); }; public void run(){ sysout.out.println("老虎会跑"); }; } public class D

小猪的数据结构辅助教程——3.2 栈与队列中的链栈

小猪的数据结构辅助教程--3.2 栈与队列中的链栈 标签(空格分隔): 数据结构 1.本节引言: 嗯,本节没有学习路线图哈,因为栈我们一般都用的是顺序栈,链栈还是顺带提一提吧, 栈因为只是栈顶来做插入和删除操作,所以较好的方法是将栈顶放在单链表的头部,栈顶 指针与单链表的头指针合二为一~所以本节只是讲下链栈的存储结构和基本操作! 2.链栈的存储结构与示意图 存储结构: typedef struct StackNode { SElemType data; //存放的数据 struct StackN

设计模式看书笔记(5) - 三种工厂模式比较

先看三种工厂模式部分主要代码(完整代码在前三篇博客): 简单工厂模式: public class SampleFactory { public static Animal createAnimal(String animalName){ if("Tiger".equals(animalName))){ return new Triger(); }else if("Dolphin".equals(animalName)){ return new Dolphin();

易学设计模式看书笔记(7) - 代理模式

代理模式 1.系统日志记录的例子:给系统中的业务逻辑加上日志 (1):最简单直接的做法 public class Test { private Logger logger = Loger.getLogger(this.getClass().getName()); public void doLgic(String name){ logger.log(name + "开始业务逻辑处理..."); //业务逻辑处理相关程序 System.out.println("业务逻辑处理相关

易学设计模式看书笔记(3) - 工厂方法模式

二.工厂方法模式 1.动物管理系统的例子 首先,抽象的动物类和具体的动物实现类: public interface Animal{ public void eat(); } public class Tiger implements Animal { public void eat(){ sysout.out.println("老虎会吃"); }; public void run(){ sysout.out.println("老虎会跑"); }; } public