第十章 基本数据结构——栈和队列

摘要

  本章介绍了几种基本的数据结构,包括栈、队列、链表以及有根树,讨论了使用指针的简单数据结构来表示动态集合。本章的内容对于学过数据结构的人来说,没有什么难处,简单的总结一下。

1、栈和队列

  栈和队列都是动态集合,元素的出入是规定好的。栈规定元素是先进后出(FILO),队列规定元素是先进先出(FIFO)。栈和队列的实现可以采用数组和链表进行实现。在标准模块库STL中有具体的应用,可以参考http://www.cplusplus.com/reference/

  栈的基本操作包括入栈push和出栈pop,栈有一个栈顶指针top,指向最新如栈的元素,入栈和出栈操作操作都是从栈顶端进行的。

  队列的基本操作包括入队enqueue和出队dequeue,队列有队头head和队尾tail指针。元素总是从队头出,从队尾入。采用数组实现队列时候,为了合理利用空间,可以采用循环实现队列空间的有效利用。

  关于栈和队列的基本操作如下图所示:

采用数组简单实现一下栈和队列,实现队列时候,长度为n的数组最多可以含有n-1个元素,循环利用,这样方便判断队列是空还是满。

栈的C++程序如下所示:

#include<iostream>
#include<cstdlib>
using namespace std;

typedef struct stack
{
    int *s;
    int stacksize;
    int top;
}stack;

void init_stack(stack *s,int n)
{
    s->stacksize=n;
    s->s=(int*)malloc(sizeof(int)*s->stacksize);
    s->top=0;
}

bool stack_empty(stack *s)
{
    if(s->top==0)
        return true;
    else
        return false;
}

bool stack_full(stack *s)
{
    if(s->top==s->stacksize)
        return true;
    else
        return false;
}

void push(stack *s,int x)
{
    if(stack_full(s))
    {
        cout<<"overflow"<<endl;
        exit(1);
    }
    s->top=s->top+1;
    s->s[s->top]=x;
}

int pop(stack *s)
{
    int x;
    if(stack_empty(s))
    {
        cout<<"underflow"<<endl;
        exit(1);
    }
    x=s->s[s->top];
    s->top--;
    return x;
}
int top(stack *s)
{
    return s->s[s->top];
}
int main()
{
    stack s;
    init_stack(&s,100);
    push(&s,19);
    push(&s,23);
    push(&s,34);
    push(&s,76);
    push(&s,65);
    cout<<"top is "<<top(&s)<<endl;
    pop(&s);
    cout<<"top is "<<top(&s)<<endl;
}

队列的C++代码实现:

#include<iostream>
#include<cstdlib>
using namespace std;

typedef struct queue
{
    int *q;
    int head,tail;
    int queuesize;
}queue;

void init_queue(queue *q,int n)
{
    q->queuesize=n;
    q->q=(int*)malloc(sizeof(int)*q->queuesize);
    q->tail=q->head=0;
}

bool queue_empty(queue *q)
{
    if(q->head==q->tail)
        return true;
    else
        return false;
}

bool queue_full(queue *q)
{
    if(q->tail+1==q->head)
        return true;
    else
        return false;
}

int queue_length(queue *q)
{
    return (q->tail-q->head+q->queuesize)%q->queuesize;
}

void enqueue(queue *q,int x)
{
    if(queue_full(q))
    {
        cout<<"queue overflow"<<endl;
        exit(1);
    }
    q->q[q->tail]=x;
    q->tail=(q->tail+1)%q->queuesize;
}

int dequeue(queue *q)
{
    int x;
    if(queue_empty(q))
    {
        cout<<"queue underflow"<<endl;
        exit(1);
    }
    x=q->q[q->head];
    q->head=(q->head+1)%q->queuesize;
    return x;
}

int main()
{
    queue q;
    init_queue(&q,100);
    enqueue(&q,10);
    enqueue(&q,30);
    cout<<"head: "<<q.head<<" tail: "<<q.tail<<endl;
    cout<<"value="<<dequeue(&q)<<endl;
    cout<<"value="<<dequeue(&q)<<endl;
    cout<<"head: "<<q.head<<" tail: "<<q.tail<<endl;
    enqueue(&q,10);
    exit(0);
}

问题:

(1)说明如何用两个栈实现一个队列,并分析有关队列操作的运行时间。(始终用一个栈做为出,一个栈作为入队)

解答:栈中的元素是先进后出,而队列中的元素是先进先出。现有栈s1和s2,s1中存放队列中的结果,s2辅助转换s1为队列。

入队时,将元素压入s1。

出队时,判断s2是否为空,如不为空,则直接弹出顶元素;如为空,则将s1的元素逐个“倒入”s2,把最后一个元素弹出并出队。

(如果s1满了,s2既没有装满也不是非空,此时就不能继续入队了;如果s1满了,但s2是空的,则可以先将s1中的元素压人s2中,然后再进队。)

#include<iostream>
#include<stack>
#include<cstdlib>
using namespace std;

template <typename T>
class StackToQueue
{
public:
    T dequeue();
    void enqueue(const T &node);
    StackToQueue(){}
    ~StackToQueue(){}
private:
    stack<T> stack1;
    stack<T> stack2;
};

template <typename T>
void StackToQueue<T>::enqueue(const T &node)
{
    stack1.push(node);
}

template <typename T>
T StackToQueue<T>::dequeue()
{
    if(stack2.empty()&&stack1.empty())
    {
        cout<<"underflow"<<endl;
        exit(1);
    }
    if(stack2.empty()&&!stack1.empty())
    {
        while(!stack1.empty())
        {
            T temp=stack1.top();
            stack1.pop();
            stack2.push(temp);
        }
    }
    T data=stack2.top();
    stack2.pop();
    return data;
}

int main()
{
    StackToQueue<int> sq;
    sq.enqueue(1);
    sq.enqueue(2);
    sq.enqueue(3);
    cout<<sq.dequeue()<<endl;
    cout<<sq.dequeue()<<endl;
    cout<<sq.dequeue()<<endl;
    cout<<sq.dequeue()<<endl;
}

(2)说明如何用两个队列实现一个栈,并分析有关栈操作的运行时间。(始终保证有一个队列是空的)

需要注意的一点是,如果每次入栈都选则将元素插入到第一个队列中,出队时先将前n-1个元素移交到第二个队列中,然后返回队列一中剩余的唯一一个元素,再将队列二中的元素又依次移交回队列一中,这样做未免效率过于低下。

事实上这样的思路我们可以看作是一直选用队列一作为栈元素的存储容器,而使用队列二作为一个出队时的辅助工具,这样每次出栈时来回复制元素容器元素的操作,效率确实低下。因为两个队列完全一样的,所以我们完全有理由让两个队列轮流作为栈元素的存储容器,这样每次出栈时,只需将所有前n-1个元素从一个队列移交到另一个队列中,然后返回最后一个元素作为出栈结果,这样始终至少有一个队列是空的,而每次进栈时,只需将进占元素放在那个非空队列的末尾(如果两个队列均为空,则随便放在那个里面都行)。这样减少了一半的复制操作。

#include<iostream>
#include<queue>
#include<cstdlib>
using namespace std;

template <typename T>
class QueueToStack
{
public:
    QueueToStack(){}
    ~QueueToStack(){}
    T pop();
    void push(const T &node);
private:
    queue<T> queue1;
    queue<T> queue2;
};

template <typename T>
T QueueToStack<T>::pop()
{
    T data;
    if(queue1.empty()&&queue2.empty())
    {
        cout<<"underflow"<<endl;
        exit(1);
    }
    if(!queue1.empty())//对工作的队列进行操作
    {     //出队到队列中只剩下一个元素,就可以出栈了
        while(queue1.size()>1)
        {
            T temp=queue1.front();
            queue1.pop();
            queue2.push(temp);
        }
        data=queue1.front();
        queue1.pop();
    }
    else if(!queue2.empty())
    {
        while(queue2.size()>1)
        {
            T temp=queue2.front();
            queue2.pop();
            queue1.push(temp);
        }
        data=queue2.front();
        queue2.pop();
    }
    return data;
}

template <typename T>
void QueueToStack<T>::push(const T &node)
{
    if(!queue1.empty())//每次都压入到工作的队列中
        queue1.push(node);
    else
        queue2.push(node);
}

int main()
{
    QueueToStack<int> queue;
    queue.push(1);
    queue.push(2);
    queue.push(3);
    cout<<queue.pop()<<endl;
    cout<<queue.pop()<<endl;
    cout<<queue.pop()<<endl;
}

 上面的top没有写

#include <cstdlib>
#include <iostream>
#include <assert.h>
#include <deque>
using namespace std;
/*两个队列模拟一个堆栈*/
/*队列A、B
入栈:将元素依次压入到非空的队列,第一个元素压倒对列A
出栈:把队列A的前n-1个元素倒到队列B,把第n个元素去掉。此时数据在B中,下次操作,则对B操作。
栈顶:把队列A的前n-1个元素倒到队列B,把第n个元素作为栈顶*/
template <typename T>
class MyStack
{
public:
    //入栈,第一个元素进到队列deque1,以后每个元素进到非空的队列
    void  push(T element)
    {
        if (deque1.empty() && deque2.empty())
        {
            deque1.push_back(element);
        }
        else if (!deque1.empty() && deque2.empty())
        {
            deque1.push_back(element);
        }
        else if (deque1.empty() && !deque2.empty())
        {
            deque2.push_back(element);
        }
    }
    //出栈,将非空队列的前n-1个元素转移到另一个空的队列,删除非空队列的第n个元素
    void pop()
    {
        if (!deque1.empty())
        {
            int size = deque1.size();
            for (int i=0; i<size-1; i++)
            {
                deque2.push_back(deque1.front());
                deque1.pop_front();
            }
            deque1.pop_front();
        }
        else
        {
            int size = deque2.size();
            for (int i=0; i<size-1; i++)
            {
                deque1.push_back(deque2.front());
                deque2.pop_front();
            }
            deque2.pop_front();
        }
    }
    //栈顶元素,将非空队列的前n-1个元素转移到另一个空的队列,将非空队列的第n个元素返回
    T top()
    {
        if (!deque1.empty())
        {
            int size = deque1.size();
            for (int i=0; i<size-1; i++)
            {
                deque2.push_back(deque1.front());
                deque1.pop_front();
            }
            T temp = deque1.front();
            deque1.pop_front();
            deque2.push_back(temp);
            return temp;
        }
        else
        {
            int size = deque2.size();
            for (int i=0; i<size-1; i++)
            {
                deque1.push_back(deque2.front());
                deque2.pop_front();
            }
            T temp = deque2.front();
            deque2.pop_front();
            deque1.push_back(temp);
            return temp;
        }
    }
    //栈是否为空
    bool empty()
    {
        return (deque1.empty()&&deque2.empty());
    }
private:
    deque<T> deque1;
    deque<T> deque2;
};
int main(int argc, char *argv[])
{
    MyStack<int> my;
    for (int i=0; i<10; i++)
    {
        my.push(i);
    }
    while (!my.empty())
    {
        cout<<my.top()<<" ";
        my.pop();
    }
    cout<<endl;
}

 

时间: 2024-08-03 09:45:09

第十章 基本数据结构——栈和队列的相关文章

数据结构――栈、队列和树(Java)

数据|数据结构 数据结构――栈.队列和树 开发者可以使用数组与链表的变体来建立更为复杂的数据结构.本节探究三种这样的数据结构:栈.队列与树.当给出算法时,出于简练,直接用Java代码. 栈 栈是这样一个数据结构,其数据项的插入和删除(获取)都只能在称为栈顶的一端完成.因为最后插入的数据项就是最先要删除的数据项,开发者往往将栈称为LILO(last-in, first-out)数据结构. 数据项压入(插入)或者弹出(删除或取得)栈顶.图13示例了一个有三个String数据项的栈,每个数据项压入栈顶

浅谈算法和数据结构 一 栈和队列

最近晚上在家里看Algorithems,4th Edition,我买的英文版,觉得这本书写的比较浅显易懂,而且"图码并茂",趁着这次机会打算好好学习做做笔记,这样也会印象深刻,这也是写这一系列文章的原因.另外普林斯顿大学在Coursera 上也有这本书同步的公开课,还有另外一门算法分析课,这门课程的作者也是这本书的作者,两门课都挺不错的. 计算机程序离不开算法和数据结构,本文简单介绍栈(Stack)和队列(Queue)的实现,.NET中与之相关的数据结构,典型应用等,希望能加深自己对这

c c++-数据结构,栈和队列,问题

问题描述 数据结构,栈和队列,问题 已知Q 是一个非空队列,S 是一个空栈,借助队列和栈的ADT 函数,将队列Q的所有元素逆置 解决方案 数据结构-栈和队列数据结构-栈和队列<数据结构>第三章 栈和队列问题回收站 解决方案二: 将队列元素都出队到栈中,再将栈中的数据入队 解决方案三: http://zhidao.baidu.com/link?url=dOiiECx8cRRi-hWjhDyWkIJoYigGhttDvdFnx28a9RNh35ddFDuTlXCINA7CCIriyrMRlHDUp

数据结构学习(C++)之栈和队列

栈和队列是操作受限的线性表,好像每本讲数据结构的数都是这么说的.有些书按照这个思路给出了定义和实现:但是很遗憾,本文没有这样做,所以,有些书中的做法是重复建设,这或许可以用不是一个人写的这样的理由来开脱. 顺序表示的栈和队列,必须预先分配空间,并且空间大小受限,使用起来限制比较多.而且,由于限定存取位置,顺序表示的随机存取的优点就没有了,所以,链式结构应该是首选. 栈的定义和实现 #ifndef Stack_H #define Stack_H #include "List.h" tem

数据结构实践——停车场模拟(栈和队列综合)

本文是针对数据结构基础系列网络课程(3):栈和队列的实践项目. 设停车场是一个可停放n辆汽车的狭长死胡同,南边封口,汽车只能从北边进出(这样的停车场世间少有).汽车在停车场内按车辆到达时间的先后顺序,最先到达的第一辆车停放在车场的最南端,依次向北排开.若车场内已停满n辆汽车,则后来的汽车只能在门外的候车场上等候,一旦有车开走,则排在候车场上的第一辆车即可开入.当停车场内某辆车要离开时,在它之后进入的车辆必须先退出车场为它让路(假定停车场内设有供车辆进出的便道,所有的司机也必须在车内随时待命),待

数据结构与算法02 之栈与队列

 我们知道,在数组中,若知道数据项的下标,便可立即访问该数据项,或者通过顺序搜索数据项,访问到数组中的各个数据项.但是栈和队列不同,它们的访问是受限制的,即在特定时刻只有一个数据项可以被读取或者被删除.众所周知,栈是先进后出,只能访问栈顶的数据,队列是先进先出,只能访问头部数据.这里不再赘述.     栈的主要机制可以用数组来实现,也可以用链表来实现,下面用数组来实现栈的基本操作: [java] view plain copy   public class ArrayStack {       

C#数据结构与算法揭秘五 栈和队列_C#教程

这节我们讨论了两种好玩的数据结构,栈和队列. 老样子,什么是栈, 所谓的栈是栈(Stack)是操作限定在表的尾端进行的线性表.表尾由于要进行插入.删除等操作,所以,它具有特殊的含义,把表尾称为栈顶(Top) ,另一端是固定的,叫栈底(Bottom) .当栈中没有数据元素时叫空栈(Empty Stack).这个类似于送饭的饭盒子,上层放的是红烧肉,中层放的水煮鱼,下层放的鸡腿.你要把这些菜取出来,这就引出来了栈的特点先进后出(First in last out).   具体叙述,加下图. 栈通常记

数据结构之栈和队列

 我们知道,在数组中,若知道数据项的下标,便可立即访问该数据项,或者通过顺序搜索数据项,访问到数组中的各个数据项.但是栈和队列不同,它们的访问是受限制的,即在特定时刻只有一个数据项可以被读取或者被删除.众所周知,栈是先进后出,只能访问栈顶的数据,队列是先进先出,只能访问头部数据.这里不再赘述.     栈的主要机制可以用数组来实现,也可以用链表来实现,下面用数组来实现栈的基本操作: [java] view plain copy   public class ArrayStack {       

数据结构:栈和队列的定义和操作

一.栈和队列定义 1).栈 定义: 栈(Stack)是一个后进先出(Last in first out,LIFO)的线性表,它要求只在表尾进行删除和插入操作. 图如下: 特点: 一.栈特殊的线性表(顺序表.链表),它在操作上有一些特殊的要求和限制:栈的元素必须"后进先出". 三.栈的表尾称为栈的栈顶(top),相应的表头称为栈底(bottom) 二.栈的操作只能在这个线性表的表尾进行. 2).队列 定义: 队列是限定只能在表的一端进行插入,在表的另一端进行删除的特殊的线性表. 图如下: