精华内容
下载资源
问答
  • 队列的作用

    千次阅读 2008-11-12 16:20:00
    之所以会定义栈和队列这样数据结构 是因为他们有两大特性: 第一: 他们可以保存程序运行路径中各个点信息,以便用于回溯操作或其他需要访问已经访问过节点信息操作。 比如: 栈用于解决迷宫问题,就是...


        之所以会定义栈和队列这样的数据结构
        是因为他们有两大特性:
        第一: 他们可以保存程序运行路径中各个点的信息,以便用于回溯操作或其他需要访问已经访问过的节点信息的操作。
        比如: 栈用于解决迷宫问题,就是用到了若线路不通,需要回溯到已访问过的结点,从那个结点再做一次与这次路径不同的选择。
       
        第二: 先进后出 和 先进先出的 次序

               先进后出次序 其实就是一种将序列反序操作的次序
               先进先出次序 其实就是一种将序列顺序操作的次序
              
        比如: 利用栈的先进后出可以解决进制转化问题 ,即:先将个位余数进栈,再将十位余数进栈,然后百位,千位 等 ,这样出栈的时候顺序就成了反序出栈,即:先千位,百位,然后十位,最后个位。


    总结:

               从保存信息角度来看 定义栈和队列数据结构是为了保存遍历所经过的路径,亦即保存遍历过程中的每一个结点的信息,而不仅仅只是终结点的信息。  而之所以要用栈或队列保存每个遍历节点信息,是因为在确定下一次的遍历节点时,可能需要利用已经遍历过的节点的信息,比如回溯操作

               从次序角度来看,定义栈是为了对序列实行一种反序操作
                              定义队列 是为了对序列实行一种顺序操作



       




     
       
    展开全文
  • 栈和队列的知识

    2013-05-25 18:46:54
    该文档清晰地讲解了有关栈和队列的一些知识,希望能对初学者们起到很好的帮助作用
  • 详细描述了数据结构中栈和队列的思想和使用方法,通过链表的使用也描述了指针的一些使用方法,能够对初学C语言的同学在算法设计和C语言的项目中起到很好的作用
  • 栈和队列

    2017-08-24 22:47:00
    栈和队列都属于限制了插入、删除操作表。栈要求插入、删除操作都只能作用在一端;队列要求插入、删除操作不能作用在同一端。 因此,栈是先进后出一种数据结构,队列是先进先出数据结构。 C++标准库中有栈...

    栈和队列都属于限制了插入、删除操作的表。栈要求插入、删除操作都只能作用在一端;队列要求插入、删除操作不能作用在同一端。

    因此,栈是先进后出的一种数据结构,队列是先进先出的数据结构。

    C++的标准库中有栈模板,它的详细操作介绍请参考这篇博文:http://www.cnblogs.com/yeqluofwupheng/p/6711265.html

    下面从算法和应用等方面介绍栈。

    顺序栈和链栈

    栈的实现可以是建立在数组或链表上的,建立在数组上的顺序栈和建立在链表上的链栈也有一些区别:

    • 在顺序栈中,定义了栈的栈底指针(存储空间首地址base)、栈顶指针top以及顺序存储空间的大小stacksize;而对于链栈来说,它只定义栈顶指针。
    • 顺序栈和链栈的top指针有区别,顺序栈的top指针指向栈定的空元素处,top-1才指向栈顶元素,而链栈的top指针相当于链表的head指针一样,指向实实在在的元素。
    • 在空间上,顺序表是静态分配的,而链表则是动态分配的;就存储密度来说:顺序表等于1,而链式表小于1。
    • 顺序栈由于有栈空间大小,所以一般栈中的空间是有限制的,而链栈是动态分配,它的空间大小等于可用的整个内存空间。

    链栈的简单实现:

    class LinkStack{
    public:
        bool isEmpty(){ return !head; }
        void push(int val){
            ListNode *r = new ListNode(val);
            r->next = head;
            head = r;
        }
        void pop(){
            if (isEmpty())return;
            ListNode *p = head;
            head = head->next;
        }
        int top(){
            if (isEmpty())return -1;//抛出异常
            return head->val;
        }
    private:
        ListNode *head = nullptr;
    };

    Min Stack

    LeetCode中有一道题,设计最小栈Min Stack。支持push、top、pop、getMin这四种操作。

    • push(x) -- 将x入栈.
    • pop() -- 栈顶元素出栈.
    • top() -- 得到栈顶元素的值.
    • getMin() -- 返回栈中最小值的元素.

    使用双栈来实现最小栈

    class MinStack {
    public:
        /** initialize your data structure here. */
        MinStack() {
    
        }
    
        void push(int x) {
            if (s.empty() || s.top().second > x){//需要更新最小值
                s.push(make_pair(x, x));
            }
            else{//上一个的最小值是当前的最小值
                s.push(make_pair(x, s.top().second));
            }
        }
    
        void pop() {
            s.pop();
        }
    
        int top() {
            return s.top().first;
        }
    
        int getMin() {
            return s.top().second;
        }
    private:
        stack<pair<int,int>>s;//first存当前位置的实际值,second存当前位置的最小值
    };

    Implement Stack using Queues

    使用队列来实现栈,要求包含下面的操作:

    • push(x) -- 将x入栈.
    • pop() -- 栈顶元素出栈.
    • top() -- 得到栈顶元素的值.
    • empty() -- 判断栈是否为空.
    class MyStack {
    public:
        /** Initialize your data structure here. */
        MyStack() {
    
        }
    
        /** Push element x onto stack. */
        void push(int x) {
            Q.push(x);
            for (size_t i = 1; i < Q.size(); i++){
                Q.push(Q.front());
                Q.pop();
            }
        }
    
        /** Removes the element on top of the stack and returns that element. */
        int pop() {
            int v = Q.front();
            Q.pop();
            return v;
        }
    
        /** Get the top element. */
        int top() {
            return Q.front();
        }
    
        /** Returns whether the stack is empty. */
        bool empty() {
            return Q.empty();
        }
    private:
        queue<int>Q;
    };

    栈的应用

    数制转换:即将十进制转换为K进制;

    括号匹配:{}、()、[]三种括号的嵌套和匹配。

    表达式求值:给定一个算数表达式字符串,求出它的值。

    中缀与后缀表达式的转换:将一个中缀表达式转换成后缀表达式。

    队列

    Implement Queue using Stacks

    使用栈实现队列,要求包含下面操作:

    • push(x) -- 将x入队.
    • pop() -- 队列的头部的元素出队.
    • peek() -- 查看队列的头部的元素.
    • empty() -- 返回队列是否为空.

    双栈实现队列。

    class MyQueue {
    public:
        /** Initialize your data structure here. */
        MyQueue() {
    
        }
    
        /** Push element x to the back of queue. */
        void push(int x) {
            sin.push(x);
        }
    
        /** Removes the element from in front of queue and returns that element. */
        int pop() {
            int v = 0;
            if (sout.empty()){
                while (!sin.empty()){
                    sout.push(sin.top());
                    sin.pop();
                }
            }
            v = sout.top();
            sout.pop();
            return v;
        }
    
        /** Get the front element. */
        int peek() {
            if (sout.empty()){
                while (!sin.empty()){
                    sout.push(sin.top());
                    sin.pop();
                }
            }
            return sout.top();
        }
    
        /** Returns whether the queue is empty. */
        bool empty() {
            return sin.empty() && sout.empty();
        }
    private:
        stack<int>sin,sout;
    };

    队列的应用

    例如,打印机中的任务队列,售票时每个窗口的队列,系统中的消息队列等,很多场景需要用到队列这一结构。

    双端队列

    双端队列对应C++标准库中的deque容器,双端队列是一种具有队列和栈的性质的数据结构。双端队列中的元素可以从两端弹出,其限定插入和删除操作在表的两端进行。

    deque采用分块的线性存储结构来存储数据,每块的大小一般为512B,将之称为deque块,所有的deque块使用一个map块进行管理,每个map数据项记录各个deque块的首地址。这样的话,deque块在头部和尾部都可以插入和删除,而不需要移动其他元素(使用

    push_back()方法在尾部插入元素,会扩张队列,而使用push_front()方法在首部插入元素和使用insert()方法在中间插入元素,只是将原位置上的元素进行覆盖,不会增加新元素)一般来说,当考虑到容器元素的内存分配策略和操作的性能时deque相当于vector更有优势。

    deque的构造方法

    • deque<type> deq                                        创建一个没有任何元素的双端队列
    • deque<type> deq(otherDeq)                    用另一个类型相同双端队列初始化该双端队列
    • deque<type> deq(size)                              初始化一个固定size的双端队列
    • deque<type> deq(n, element)                  初始化n个相同元素的双端队列
    • deque<type> deq(begin,end)                   初始化双端队列中的某一段元素,从begin 到 end - 1

    deque的操作

    • deq.assign(n,elem)               赋值n个元素的拷贝给双端队列
    • deq.assign(beg,end)            赋值一段迭代器的值给双端队列
    • deq.push_front(elem)           添加一个元素在开头
    • deq.pop_front()                       删除第一个元素
    • deq.at(index)                           取固定位置的元素
    • deq[index]                                取固定位置的元素
    • deq.front()                                返回第一个元素(不检测容器是否为空)
    • deq.back()                                返回最后一个元素(不检测容器是否为空)

    输入受限的双端队列

    即:一端既能输入又能输出,另一端只能输出的双端队列;

    输出受限的双端队列

    即:一端既能输入又能输出,另一端只能输入的双端队列;

    一个经典问题:

    如果以1、2、3、4为一个双端队列的输入序列,则满足下面条件的输出序列是什么?

    1)能够通过一个输入受限的双端队列得到,但不能通过输出受限的双端队列得到;

    2)能够通过一个输出受限的双端队列得到,但不能通过输入受限的双端队列得到;

    3)既不能通过一个输入受限的双端队列得到,又不能通过输出受限的双端队列得到;

    输入受限的双端队列不可以得到:4213、4231

    输出受限的双端队列不可以得到:4132、4231

    因此上面的答案分别是

    1)4132;2)4213;3)4231

    转载于:https://www.cnblogs.com/yeqluofwupheng/p/7420675.html

    展开全文
  • 03 栈和队列

    2020-05-26 23:54:33
    03 栈和队列 栈 定义 顺序存储结构 出栈操作 进栈操作 两栈共享空间 链式存储结构 进栈操作 出栈操作 作用应用 递归 四则运算表达式求值 后缀(逆波兰)表示法 中缀表达式转后缀表达式 后缀表达式...

    03 栈和队列

    在这里插入图片描述

    1. 定义

    栈(Stack)是限定仅在表尾进行插入和删除操作的线性表,栈又称为后进先出(Last In First Out)的线性表,简称LIFO结构

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

       typedef int SElemType; /* SElemType类型根据实际情况而定,这里假设为int */
       
       /* 顺序栈结构 */
       typedef struct
       {
               SElemType data[MAXSIZE];
               int top; /* 用于栈顶指针 */
       }SqStack;
      
      1. 出栈操作:时间复杂度O(1)

         /* 若栈不空,则删除S的栈顶元素,用e返回其值,并返回OK;否则返回ERROR */
         Status Pop(SqStack *S,SElemType *e)
         { 
                 if(S->top==-1)
                         return ERROR;
                 *e=S->data[S->top];	/* 将要删除的栈顶元素赋值给e */
                 S->top--;				/* 栈顶指针减一 */
                 return OK;
         }
        
      2. 进栈操作:时间复杂度O(1)

         /* 插入元素e为新的栈顶元素 */
         Status Push(SqStack *S,SElemType e)
         {
                 if(S->top == MAXSIZE -1) /* 栈满 */
                 {
                         return ERROR;
                 }
                 S->top++;				/* 栈顶指针增加一 */
                 S->data[S->top]=e;  /* 将新插入元素赋值给栈顶空间 */
                 return OK;
         }
        
      3. 两栈共享空间

        1. 结构

           /* 两栈共享空间结构 */
           typedef struct 
           {
                   SElemType data[MAXSIZE];
                   int top1;	/* 栈1栈顶指针 */
                   int top2;	/* 栈2栈顶指针 */
           }SqDoubleStack;
          
        2. push

           /* 插入元素e为新的栈顶元素 */
           Status Push(SqDoubleStack *S,SElemType e,int stackNumber)
           {
                   if (S->top1+1==S->top2)	/* 栈已满,不能再push新元素了 */
                           return ERROR;	
                   if (stackNumber==1)			/* 栈1有元素进栈 */
                           S->data[++S->top1]=e; /* 若是栈1则先top1+1后给数组元素赋值。 */
                   else if (stackNumber==2)	/* 栈2有元素进栈 */
                           S->data[--S->top2]=e; /* 若是栈2则先top2-1后给数组元素赋值。 */
                   return OK;
           }
          
        3. pop

           /* 若栈不空,则删除S的栈顶元素,用e返回其值,并返回OK;否则返回ERROR */
           Status Pop(SqDoubleStack *S,SElemType *e,int stackNumber)
           { 
                   if (stackNumber==1) 
                   {
                           if (S->top1==-1) 
                                   return ERROR; /* 说明栈1已经是空栈,溢出 */
                           *e=S->data[S->top1--]; /* 将栈1的栈顶元素出栈 */
                   }
                   else if (stackNumber==2)
                   { 
                           if (S->top2==MAXSIZE) 
                                   return ERROR; /* 说明栈2已经是空栈,溢出 */
                           *e=S->data[S->top2++]; /* 将栈2的栈顶元素出栈 */
                   }
                   return OK;
           }
          
    2. 链式存储结构

       /* 链栈结构 */
       typedef struct StackNode
       {
               SElemType data;
               struct StackNode *next;
       }StackNode,*LinkStackPtr;
       
       
       typedef struct
       {
               LinkStackPtr top;
               int count;
       }LinkStack;
      
      1. 进栈操作:时间复杂度O(1)

         /* 插入元素e为新的栈顶元素 */
         Status Push(LinkStack *S,SElemType e)
         {
                 LinkStackPtr s=(LinkStackPtr)malloc(sizeof(StackNode)); 
                 s->data=e; 
                 s->next=S->top;	/* 把当前的栈顶元素赋值给新结点的直接后继,见图中① */
                 S->top=s;         /* 将新的结点s赋值给栈顶指针,见图中② */
                 S->count++;
                 return OK;
         }
        
      2. 出栈操作:时间复杂度O(1)

         /* 若栈不空,则删除S的栈顶元素,用e返回其值,并返回OK;否则返回ERROR */
         Status Pop(LinkStack *S,SElemType *e)
         { 
                 LinkStackPtr p;
                 if(StackEmpty(*S))
                         return ERROR;
                 *e=S->top->data;
                 p=S->top;					/* 将栈顶结点赋值给p,见图中③ */
                 S->top=S->top->next;    /* 使得栈顶指针下移一位,指向后一结点,见图中④ */
                 free(p);                    /* 释放结点p */        
                 S->count--;
                 return OK;
         }
        
      3. 总结:对比一下顺序栈和链栈,它们在时间复杂度上是一样的,均为O(1)。对于空间性能,顺序栈需要事先确定一个固定的长度,可能会存在内存空间浪费的问题,但它的优势是存取时定位很方便,而链栈则要求每个元素都有指针域,这同时也增加了一些内存开销,但对于栈的长度无限制。所以它们的区别和线性表中讨论的一样,如果栈的使用过程中元素变化不可预料,有时很小,有时非常大,那么最好是用链栈,反之,如果它的变化在可控范围内,建议使用顺序栈会更好一些。

    3. 作用

      栈的引入简化了程序设计的问题,划分了不同关注层次,使得思考范围缩小,更加聚焦于我们要解决的问题核心。

    4. 栈的应用

      1. 递归:斐波那契数列的实现

        把一个直接调用自己或通过一系列的调用语句间接地调用自己的函数,称做递归函数

         #include "stdio.h"
         
         int Fbi(int i)  /* 斐波那契的递归函数 */
         {
         	if( i < 2 )
         		return i == 0 ? 0 : 1;  
             return Fbi(i - 1) + Fbi(i - 2);  /* 这里Fbi就是函数自己,等于在调用自己 */
         }  
         
         int main()
         {
         	int i;
         	int a[40];  
         	printf("迭代显示斐波那契数列:\n");
         	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 ",a[i]);  
         	} 
         	printf("\n");
         	
         	printf("递归显示斐波那契数列:\n");
         	for(i = 0;i < 40;i++)  
         		printf("%d ", Fbi(i));  
             return 0;
         }
        
      2. 四则运算表达式求值

        9 + ( 3 - 1 ) * 3 + 10 / 2

        1. 后缀(逆波兰)表示法

           9 3 1 - 3 * + 10 2 / +			
          
        2. 中缀表达式转后缀表达式

        3. 后缀表达式计算结果
          中缀表达式转成后缀表达式后,借助栈来计算结果

    队列

    1. 定义

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

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

    2. 循环队列

      我们把队列的这种头尾相接的顺序存储结构称为循环队列

      1. 循环队列的顺序存储结构如下:

         typedef int QElemType; /* QElemType类型根据实际情况而定,这里假设为int */
         
         /* 循环队列的顺序存储结构 */
         typedef struct
         {
         	QElemType data[MAXSIZE];
         	int front;    	/* 头指针 */
         	int rear;		/* 尾指针,若队列不空,指向队列尾元素的下一个位置 */
         }SqQueue;
        
      2. 循环队列初始化:

         /* 初始化一个空队列Q */
         Status InitQueue(SqQueue *Q)
         {
         	Q->front=0;
         	Q->rear=0;
         	return  OK;
         }
        
      3. 循环队列求队列长度代码如下:

         /* 返回Q的元素个数,也就是队列的当前长度 */
         int QueueLength(SqQueue Q)
         {
         	return  (Q.rear-Q.front+MAXSIZE)%MAXSIZE;
         }
        
      4. 循环队列的入队列操作代码:

         /* 若队列未满,则插入元素e为Q新的队尾元素 */
         Status EnQueue(SqQueue *Q,QElemType e)
         {
         	if ((Q->rear+1)%MAXSIZE == Q->front)	/* 队列满的判断 */
         		return ERROR;
         	Q->data[Q->rear]=e;			/* 将元素e赋值给队尾 */
         	Q->rear=(Q->rear+1)%MAXSIZE;/* rear指针向后移一位置, */
         								/* 若到最后则转到数组头部 */
         	return  OK;
         }
        
      5. 循环队列的出队列操作代码

         /* 若队列不空,则删除Q中队头元素,用e返回其值 */
         Status DeQueue(SqQueue *Q,QElemType *e)
         {
         	if (Q->front == Q->rear)			/* 队列空的判断 */
         		return ERROR;
         	*e=Q->data[Q->front];				/* 将队头元素赋值给e */
         	Q->front=(Q->front+1)%MAXSIZE;	/* front指针向后移一位置, */
         									/* 若到最后则转到数组头部 */
         	return  OK;
         }
        
    3. 链式存储结构

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

       typedef int QElemType; /* QElemType类型根据实际情况而定,这里假设为int */
       
       typedef struct QNode	/* 结点结构 */
       {
          QElemType data;
          struct QNode *next;
       }QNode,*QueuePtr;
       
       typedef struct			/* 队列的链表结构 */
       {
          QueuePtr front,rear; /* 队头、队尾指针 */
       }LinkQueue;
      
      1. 入队操作

         /* 插入元素e为Q的新的队尾元素 */
         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;	/* 把拥有元素e的新结点s赋值给原队尾结点的后继,见图中① */
         	Q->rear=s;		/* 把当前的s设置为队尾结点,rear指向s,见图中② */
         	return OK;
         }
        
      2. 出队操作

         /* 若队列不空,删除Q的队头元素,用e返回其值,并返回OK,否则返回ERROR */
         Status DeQueue(LinkQueue *Q,QElemType *e)
         {
         	QueuePtr p;
         	if(Q->front==Q->rear)
         		return ERROR;
         	p=Q->front->next;		/* 将欲删除的队头结点暂存给p,见图中① */
         	*e=p->data;				/* 将欲删除的队头结点的值赋值给e */
         	Q->front->next=p->next;/* 将原队头结点的后继p->next赋值给头结点后继,见图中② */
         	if(Q->rear==p)		/* 若队头就是队尾,则删除后将rear指向头结点,见图中③ */
         		Q->rear=Q->front;
         	free(p);
         	return OK;
         }
        
    4. 总结:

      对于循环队列与链队列的比较,可以从两方面来考虑,从时间上,其实它们的基本操作都是常数时间,即都为O(1),不过循环队列是事先申请好空间,使用期间不释放,而对于链队列,每次申请和释放结点也会存在一些时间开销,如果入队出队频繁,则两者还是有细微差异,对于空间上来说,循环队列必须有一个固定的长度,所以就有了存储元素个数和空间浪费的问题。而链队列不存在这个问题,尽管它需要一个指针域,会产生一些空间上的开销,但也可以接受。所以在空间上,链队列更加灵活。总的来说,在可以确定队列长度最大值的情况下,建议用循环队列,如果你无法预估队列的长度时,则用链队列。

    下载资源

    资源链接

    展开全文
  • 栈和队列的解说

    2019-04-17 16:01:00
    :又名堆栈,是一种运算受限线性表,仅允许在线性表一端进行插入(push)移除(pop)运算,可以进行运算一端称为栈顶,另一端称为底。遵循先进后出原理。先进入数据被压入栈底,后放入数据置于栈顶。桟...

    一、栈

    基本概念

    栈:又名堆栈,是一种运算受限的线性表,仅允许在线性表的一端进行插入(push)和移除(pop)运算,可以进行运算的一端称为栈顶,另一端称为栈底。遵循先进后出原理。先进入的数据被压入栈底,后放入的数据置于栈顶。桟的插入数据、删除数据操作都是实现在栈顶当中:读取数据的时候从栈顶开始弹出数据(最后一个插入的数据被第一个读出来)。栈具有记忆作用,对栈的插入与删除操作中,不需要改变栈底指针。向栈中插入新元素称为进栈、压栈、入栈(PUSH),即把新元素放入栈顶元素的上面,使之成为新的栈顶元素。从栈中删除元素成为出栈、退栈(POP),即将栈顶元素删除,让其相邻的元素成为新的栈顶元素。允许进行插入和移除操作的一端称为栈顶(top),另一端为栈底(bottom);栈底固定,而栈顶浮动;栈中元素个数为零时称为空栈,栈也称为后进先出表。举个栗子:一群人挤电梯时,先进电梯的人想要出来,必须让后进电梯的人出去之后,先进电梯的人才能出去。

     

    两种基本操作

    堆栈常用一维数组或链表来实现。包括推入(压栈,push)和弹出(弹栈,pop):

    • 推入:将数据放入栈顶,栈顶指向这个新放入的数据。
    • 弹出:将栈顶数据移除,栈顶指向这个移除后数据的下一个数据。

     

    基本特点:

    • 先进后出,后进先出

     

     桟的应用场景

    • 内存管理中使用的堆栈;
    • 基于桟实现的二叉树的遍历;
    • 在处理语言的符号对等问题,在语言中,往往很多符号是成对出现的,比如<>,{},[],()等,如何判断符号是否漏了,一种实现方式就是:假设在读入一串字符串以后,如果遇到对称符号的左边部分,则将其压入栈中,当遇到对称符号的右边部分,则弹出栈中的一个对象,如果所有的符号都是平衡的,栈中此时应该就是为空,通过判断栈中是否为空,说明字符串是否是符号平衡的。

     

    代码实现:

    复制代码
    ### 列表实现桟
    class Stack(object):
    
      def __init__(self):
        self._li = list()
    
      def peek(self):
                """获取栈顶元素"""
        if not self._li:
          return None
        else:
          return self._li[-1]
    
      def push(self, item):    
                """推送新元素 """ 
        self._li.append(item)    
        return self._li[-1]
      
      def pop(self):    
                 """ 弹出栈顶元素""" 
        if not self._li:
          return None
        else:
          return self._li.pop()
    
    
    ### 链表实现桟
    class Node(object):
    
        def __init__(self, elem):
            self.elem = elem    # 保存着结点对象的真正元素
            self.next = None    # 保存着当前元素的下一指向
    
    
    class Stack(object):
       
        def __init__(self):    
            self._top = None    # 栈顶元素的指向
    
        def peek(self):
            """返回栈顶元素"""
            if not self._top:    # 如果栈顶元素的指向为空,代表是个空桟
                return None
            else:
                # self._top相当于是一个结点对象; self._top.elem取出结点对象的元素
                return self._top.elem    # 若不是空桟,就返回当前栈顶所指向的元素
    
    
        def push(self, item):
            """推送新元素"""
            node = Node(item)        # 创建新结点对象
            node.next = self._top    # 将新结点对象下一指向原先的栈顶元素,新结点对象变为栈顶元素
            self._top = node       # 将栈顶元素指向新创建的结点对象
            return node.elem     # 返回新结点对象的elem属性中保存的元素
    
    
        def pop(self):
            """弹出栈顶元素"""
            if not self._top:
                return None
            else:
                node = self._top.elem    # 获取栈顶元素
                self._top = self._top.next     # 改变新栈顶元素的指向; 新栈顶元素=原栈顶元素的下一指向
                return node     # 弹出原栈顶元素
    
                  
    
    In [66]: s = Stack()
    
    In [67]: s.push(1)
    Out[67]: 1
    
    In [68]: s.push(2)
    Out[68]: 2
    
    In [69]: s.pop()
    Out[69]: 2
    
    In [70]: s.pop()
    Out[70]: 1
    
    In [72]: print(s.peek())
    None    
    复制代码

     

     

     

    二、队列

    基本概念

     

    队列:是一种特殊的线性表,特殊之处在于它只允许在线性表的前端(front)进行删除操作,在线性表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。遵循先进先出(FIFO, First-In-First-Out)原则进行插入操作的端称为队尾,进行删除操作的端称为队头。队列的数据元素又称为队列元素。在队列中插入一个队列元素称为入队,从队列中删除一个队列元素称为出队;当队列中没有元素时,称为空队列。因为队列只允许在一端插入,在另一端删除,所以只有最早进入队列的元素才能最先从队列中删除,故队列又称为先进先出(FIFO—first in first out)线性表。在具体应用中通常用链表或者数组来实现。举个栗子:现有一只队伍需要过马路,总共有22人,你是最后一个,你为了能够顺利过马路必须等待前面所有人过去之后,你才能过马路,排在第一位的人最先过马路,然后的第二个、第三个 ... 对应着:最先插入线性表的元素,最先被删除/读取。

     

    五种基本操作

    • 创建队列:Queue()  初始条件:队q 不存在。 操作结果:创建一个空队;
    • 入队(队尾)操作:enqueue(item)   初始条件: 队q 存在。  操作结果: 对已存在的队列q,向队尾插入一个元素item;
    • 出队(队头)操作:dequeue()  初始条件: 队q 存在且非空。  操作结果: 删除队首元素,并返回其值;
    • 统计队列元素:length()     初始条件: 队q 存在且非空。 操作结果: 返回队列中元素个数;
    • 队列判空操作:empty()    初始条件: 队q 存在/不存在。 操作结果: 若q 为空队则返回False,否则返回为True。
     

    基本特点:

      先进先出,后进后出

     

    队列的应用场景

    • 异步处理,举个栗子:现有用户注册模块,需要同时完成写入注册数据至数据库、发送激活邮件、发送短信验证码。实现包括:串行方式、并行方式
      • 串行方式:先将注册信息写入数据库成功后,再发送激活邮件,最后发送短信验证码。以上三个任务依次全部完成后,返回给客户。
      • 并行方式:先将注册信息写入数据库成功后,发送注册邮件的同时,一起发送注册短信。以上三个任务完成后,返回给客户端。与串行的差别是,并行的方式可以提高处理的时间。
    • 应用解耦,举个栗子:现有用户下单模块,当用户下单后,订单系统需要通知库存系统。传统的做法是,在订单系统调用库存系统的接口。假如库存系统无法访问,则订单系统减库存将失败,从而导致订单失败,订单系统与库存系统耦合度过高。
      • 订单系统:用户下单后,在订单系统中将调用库存系统接口的操作放入到消息队列,订单系统中不再阻塞等待库存系统的返回结果。并将订单下单成功返回给用户。

      •  

        库存系统:在消息队列中订阅下单的消息,采用拉/推的方式,获取下单信息,库存系统根据下单信息,进行库存操作。

      •  

        假如:在下单时库存系统不能正常使用。也不影响正常下单,因为下单后,订单系统写入消息队列就不再关心其他的后续操作了。实现订单系统与库存系统的应用解耦。

    • 流量削锋,一般在秒杀或团抢活动中使用广泛。举个栗子:现有一个秒杀活动,一般会因为流量过大、暴增而导致应用挂掉。为解决这个问题,一般需要将用户请求加入消息队列,达到控制活动的人数,可以缓解短时间内高流量压垮应用。
      • 服务器接收用户请求后,首先写入消息队列。假如消息队列长度超过最大数量,则直接抛弃用户请求或跳转到错误页面。
      • 秒杀业务根据消息队列中的请求信息,再做后续处理。

    • 消息通讯
      • 点对点通讯:客户端A和客户端B使用同一队列,进行消息通讯;

      • 聊天室通讯:客户端A,客户端B,客户端N订阅同一主题,进行消息发布和接收。实现类似聊天室效果。
     

     

    代码实现

    复制代码
    ### 列表实现队列
    class Queues(object):
        
        def __init__(self):
            self._li = list()
    
        def is_empty(self):
            """队列判空"""
            if len(self._li) == 0:
                return True
            else:
                return False
    
        def length(self):
            """返回队列长度"""
            return len(self._li)
    
        def enqueue(self, item):
            """队尾插入元素"""
            self._li.append(item)
            return self._li[-1]
    
        def dequeue(self):
            """队头删除元素"""
            if not self.is_empty():
                return None
            else:
                return self._li.pop(0)
    
    
    
    ### 链表实现队列

    转载于:https://www.cnblogs.com/xiaozengzeng/p/10724166.html

    展开全文
  • 栈和队列的定义和作用我就不多说了,网上有很多博客说,我这里附上两个链接吧。 各种数据结构的时间复杂度分析 数据结构大梳理 Typescript中的栈和队列 不多说,直接上代码 队列 /** * 队列的实现 * 先进先出 */...
  • 模拟实现栈和队列常用方法栈概念方法模拟实现栈队列概念方法模拟实现队列 栈 概念 栈:是一种特殊线性表,只允许在固定一端进行插入和删除元素操作。进行插入和删除叫做栈顶,另一端叫做栈底。栈遵循先进...
  • 整理一下JAVA中栈和队列的常用方法

    千次阅读 2019-06-19 13:43:55
    整理一下JAVA中栈和队列的常用方法 栈 JAVA中栈类是继承了Vector实现的,基本特征是先进后出,并且只能在一侧进出。 方法 作用 Stack() 定义一个空栈 empty() 栈空返回真,否则返回假 peek() 获取栈顶值...
  • 2和队列称为q1和q2.其中q2是一个中间转化作用。q1为真正有元素的栈。其中出栈和取栈顶元素,是否为空这些操作都是和之前一样。只是入队时候需要处理将整个队列变为即可。首先如果有元素需要入栈,将元素放入...
  • 1.本周学习总结(0--1分) 这两周的时间,学习了栈与...栈和队列的应用对计算机的一些问题的解决起到了很大的作用,。可以有效地节约时间。这两周的学习,主要了解了栈和队列的逻辑结构,存储结构以及各自的特点,在...
  • 数据结构_栈和队列

    2019-07-22 19:33:15
    栈和队列: 栈Stack: 1.线性数据结构 2.相比于数组,栈对应操作是数组子集. 3.只能从一端添加元素,也只能从一端取出元素,这一端我们成为栈顶. 栈是一种后进先出数据结构(Last In First Out(LIFO))...
  • :一种特殊线性表,其只允许在固定一端进行插入删除元素操作。进行数据插入删除操作一端称为栈顶,另一端称为底。数据元素遵守后进先出LIFO(Last In First Out)原则。 一般使用数组实现。...
  • 系列课程包含11个部分,本课为第3部分栈和队列,介绍在系统软件和应用软件开发中大有用途的两种特殊线性表——栈和队列的构组成、存储结构的选择,以及基本运算的实现,通过相关的应用案例介绍了这些数据结构的应用...
  • 第三章 栈和队列 一、栈 1. 栈空条件:S.top == -1;栈满:S.top == MaxSize – 1; 栈长:S.top + 1; 以上当然是顺序栈的情况。 或许之前存储的元素仍然在栈中,但top指针已经指向了新的栈顶,也就起到了删除...
  • 栈和队列章节知识

    2021-02-19 20:19:30
    Catalan列计算可能出栈序列个数(记住这个公式): 特点:LIFO 顺序:顺序存储结构 链栈:链接存储方式//不带头结点单链表 ...递归工作栈作用:将递归调用实参函数返回地址传递给下一层递归函数;
  • 9-栈和队列

    2020-11-07 09:18:41
    其次,仅允许在表一端进行插入删除数据 这一端称为栈顶,另一端称为底 处理数据符合先进后出特点 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Cf5eZ4GR-1604711897508)...
  • 文章目录栈栈的抽象数据类型简单的基于数组的实现适配器模式用Python的list类实现一个分析基于数组的的实现队列队列的抽象数据类型基于数组的队列实现循环使用数组 一系列对象组成的一个集合。这些对象的...
  • 栈和队列是两种很有用数据结构,栈是先进后出(FILO),队列是先进先出(FIFO)。  在迷宫求解,括号匹配,递归这些问题里,栈应用非常广泛,而队列在操作系统进程排队,线程池里很有用。C中没有现成的栈和...
  • 具有记忆作用,对栈的插入与删除操作中,不需要改变底指针 二、 数据结构中与程序中有什么不同? 1.数据结构的栈:管理数据一种手段或者是方法!可以用来存放数据地址; 2.内存中的栈:是确切存在...
  • 栈和队列实现BFS和DFS算法

    千次阅读 2019-05-06 17:02:56
    栈和队列实现BFS和DFS算法 1.BFS(宽度优先遍历) 1、从顶点v出发遍历图G算法描述如下: ①访问V0 ②假设最近一层访问顶点依次为v1v2v3vk则依次访问v1,v2,v3…vk未被访问邻接点 ③重复②知道没有未被访问...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 632
精华内容 252
关键字:

栈和队列的作用