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

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

摘要

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

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;
}

 

相关文章
|
前端开发 Java
java实现队列数据结构代码详解
本文详细解析了Java中队列数据结构的实现,包括队列的基本概念、应用场景及代码实现。队列是一种遵循“先进先出”原则的线性结构,支持在队尾插入和队头删除操作。文章介绍了顺序队列与链式队列,并重点分析了循环队列的实现方式以解决溢出问题。通过具体代码示例(如`enqueue`入队和`dequeue`出队),展示了队列的操作逻辑,帮助读者深入理解其工作机制。
774 1
|
存储 C语言 C++
【C++数据结构——栈与队列】顺序栈的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现顺序栈的基本运算。开始你的任务吧,祝你成功!​ 相关知识 初始化栈 销毁栈 判断栈是否为空 进栈 出栈 取栈顶元素 1.初始化栈 概念:初始化栈是为栈的使用做准备,包括分配内存空间(如果是动态分配)和设置栈的初始状态。栈有顺序栈和链式栈两种常见形式。对于顺序栈,通常需要定义一个数组来存储栈元素,并设置一个变量来记录栈顶位置;对于链式栈,需要定义节点结构,包含数据域和指针域,同时初始化栈顶指针。 示例(顺序栈): 以下是一个简单的顺序栈初始化示例,假设用C语言实现,栈中存储
1264 77
|
编译器 C语言 C++
栈区的非法访问导致的死循环(x64)
这段内容主要分析了一段C语言代码在VS2022中形成死循环的原因,涉及栈区内存布局和数组越界问题。代码中`arr[15]`越界访问,修改了变量`i`的值,导致`for`循环条件始终为真,形成死循环。原因是VS2022栈区从低地址到高地址分配内存,`arr`数组与`i`相邻,`arr[15]`恰好覆盖`i`的地址。而在VS2019中,栈区先分配高地址再分配低地址,因此相同代码表现不同。这说明编译器对栈区内存分配顺序的实现差异会导致程序行为不一致,需避免数组越界以确保代码健壮性。
338 0
栈区的非法访问导致的死循环(x64)
232.用栈实现队列,225. 用队列实现栈
在232题中,通过两个栈(`stIn`和`stOut`)模拟队列的先入先出(FIFO)行为。`push`操作将元素压入`stIn`,`pop`和`peek`操作则通过将`stIn`的元素转移到`stOut`来实现队列的顺序访问。 225题则是利用单个队列(`que`)模拟栈的后入先出(LIFO)特性。通过多次调整队列头部元素的位置,确保弹出顺序符合栈的要求。`top`操作直接返回队列尾部元素,`empty`判断队列是否为空。 两题均仅使用基础数据结构操作,展示了栈与队列之间的转换逻辑。
|
存储 C++ 索引
【C++数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】
【数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】初始化队列、销毁队列、判断队列是否为空、进队列、出队列等。本关任务:编写一个程序实现环形队列的基本运算。(6)出队列序列:yzopq2*(5)依次进队列元素:opq2*(6)出队列序列:bcdef。(2)依次进队列元素:abc。(5)依次进队列元素:def。(2)依次进队列元素:xyz。开始你的任务吧,祝你成功!(4)出队一个元素a。(4)出队一个元素x。
635 13
【C++数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】
|
算法 调度 C++
STL——栈和队列和优先队列
通过以上对栈、队列和优先队列的详细解释和示例,希望能帮助读者更好地理解和应用这些重要的数据结构。
430 11
☀☀☀☀☀☀☀有关栈和队列应用的oj题讲解☼☼☼☼☼☼☼
### 简介 本文介绍了三种数据结构的实现方法:用两个队列实现栈、用两个栈实现队列以及设计循环队列。具体思路如下: 1. **用两个队列实现栈**: - 插入元素时,选择非空队列进行插入。 - 移除栈顶元素时,将非空队列中的元素依次转移到另一个队列,直到只剩下一个元素,然后弹出该元素。 - 判空条件为两个队列均为空。 2. **用两个栈实现队列**: - 插入元素时,选择非空栈进行插入。 - 移除队首元素时,将非空栈中的元素依次转移到另一个栈,再将这些元素重新放回原栈以保持顺序。 - 判空条件为两个栈均为空。
|
C++
【C++数据结构——栈和队列】括号配对(头歌实践教学平台习题)【合集】
【数据结构——栈和队列】括号配对(头歌实践教学平台习题)【合集】(1)遇到左括号:进栈Push()(2)遇到右括号:若栈顶元素为左括号,则出栈Pop();否则返回false。(3)当遍历表达式结束,且栈为空时,则返回true,否则返回false。本关任务:编写一个程序利用栈判断左、右圆括号是否配对。为了完成本关任务,你需要掌握:栈对括号的处理。(1)遇到左括号:进栈Push()开始你的任务吧,祝你成功!测试输入:(()))
654 7
|
C语言
【数据结构】栈和队列(c语言实现)(附源码)
本文介绍了栈和队列两种数据结构。栈是一种只能在一端进行插入和删除操作的线性表,遵循“先进后出”原则;队列则在一端插入、另一端删除,遵循“先进先出”原则。文章详细讲解了栈和队列的结构定义、方法声明及实现,并提供了完整的代码示例。栈和队列在实际应用中非常广泛,如二叉树的层序遍历和快速排序的非递归实现等。
1400 10
|
存储 算法
非递归实现后序遍历时,如何避免栈溢出?
后序遍历的递归实现和非递归实现各有优缺点,在实际应用中需要根据具体的问题需求、二叉树的特点以及性能和空间的限制等因素来选择合适的实现方式。
495 59

热门文章

最新文章