栈与队列

栈定义

  • 后进先出 (Last In First Out)
    • 一种限制访问端口的线性表
  • 主要操作
    • 进栈 (push) 出栈 (pop)
  • 应用
    • 表达式求值
    • 消除递归
    • 深度优先搜索

栈的抽象数据类型

template <class T>
class Stack {
public: // 栈的运算集
void clear(); // 变为空栈
bool push(const T item);
// item入栈,成功返回真,否则假
bool pop(T& item); // 返回栈顶内容并弹出,成功返回真,否则假
bool top(T& item); // 返回栈顶但不弹出,成功返回真,否则假
bool isEmpty(); // 若栈已空返回真
bool isFull(); // 若栈已满返回真
};

复制代码

栈主要有两种方式实现

  • 顺序栈,线性表的简化版,由于有大小,存在上溢和下溢问题
  • 链式栈,采用单链表,从顶端向下链接

顺序栈模板

Link(const T info, Link* nextValue) {// 具有两个参数的Link构造函数 
    data = info;
    next = nextValue;
 }
template <class T> class arrStack : public Stack <T> {
private: // 栈的顺序存储
int mSize; // 栈中最多可存放的元素个数
int top; // 栈顶位置,应小于mSize
T *st; // 存放栈元素的数组
public: // 栈的运算的顺序实现
arrStack(int size) { // 创建一个给定长度的顺序栈实例
mSize = size; top = -1; st = new T[mSize];
}
arrStack() { // 创建一个顺序栈的实例
top = -1;
}
~arrStack() { delete [] st; }
void clear() { top = -1; } // 清空栈
}
复制代码

顺序栈和链式栈的比较

  • 时间效率

    • 所有操作都只需常数时间
    • 顺序栈和链式栈在时间效率上难分伯仲
  • 空间效率

    • 顺序栈须说明一个固定的长度
    • 链式栈的长度可变,但增加结构性开销
  • 实际应用中,顺序栈比链式栈用得更广泛

    • 顺序栈容易根据栈顶位置,进行相对位移,快速定位并读取栈的内部元素
    • 顺序栈读取内部元素的时间为O(1),而链式栈则需要沿着指针链游走,显然慢些,读取第?个元素需要 时间为?(?)
  • 一般来说,栈不允许“读取内部元素”,只能在栈顶操作

思考问题:

top函数表示取栈顶元素,将结果返回给用户,pop函数表示将栈顶元素弹出(如果栈不空) ,pop函数仅仅是一个操作,并不将结果返回。

– pointer = aStack.ptop()? 可不可行

STL为什么这两个操作分开? 为什么不提供 ptop?

主要是有两方面原因,参考: 1.安全原因,肯能导致悬浮指针 2.考虑到效率,若是ptop(),则必须返回值而不是引用(因为原数据空间已释放),所以效率会变低。

队列的定义

  • 先进先出 (First In First Out)
    • 限制访问点的线性表
      • 按照到达的顺序来释放元素
      • 所有的插入在表的一端进行,所有的删除都在表 的另一端进行
  • 主要元素
    • 队头 (front) – 队尾 (rear)

队列的主要操作

- 入队(enQueue)
- 出队(deQueue)
- 取首元素getFront
- 判断是否为空isEmpty
复制代码

队列的抽象数据类型

template <class T> class Queue { public: // 队列的运算集
    void clear(); // 变为空队列 
    bool enQueue(const T item);// 将item插入队尾,成功则返回真,否则返回假
    bool deQueue(T & item) ;// 返回队头元素并将其从队列中删除,成功则返回真 bool getFront(T & item);
    bool isEmpty(); //
    bool isFull();  //
 };
复制代码

队列的实现方式

  • 顺序队列
    • 关键在于防止假溢出
  • 链式队列
    • 用单链表方式存储,队列中每个元素对于链 表中的一个结点

顺序队列的类定义

class arrQueue: public Queue<T> {
private:
     int mSize;// 存放队列的数组的大小

    int front;// 表示队头所在位置的下标

    int rear;// 表示队尾所在位置的下标

    T * qu;// 存放类型为T的队列元素的数组 // 队列的运算集

public:
    arrQueue(int size);// 创建队列的实例

    ~arrQueue();// 消除该实例,并释放其空间

}
复制代码

链式队列的类定义

template <class T>
class lnkQueue: public Queue<T> {
    private:
     int size; // 队列中当前元素的个数 
     Link<T>* front;// 表示队头的指针
     Link<T>* rear;// 表示队尾的指针
    public:// 队列的运算集
        lnkQueue(int size);// 创建队列的实例
        ~lnkQueue();// 消除该实例,并释放其空间

}
复制代码

顺序队列与链式队列的比较

  • 顺序队列
    • 固定的存储空间
  • 链式队列
    • 可以满足大小无法估计的情况

都不允许访问队列内部元素

队列的应用

  • 只要满足先来先服务特性的应用均可采用队列 作为其数据组织方式或中间数据结构
  • 调度或缓冲
    • 消息缓冲器
    • 邮件缓冲器
    • 计算机硬设备之间的通信也需要队列作为数据缓冲
    • 操作系统的资源管理
  • 宽度优先搜索

问题:

  • 只用front rear,长度为n的队列,最大可容纳多少元素?

    • 最大容纳n-1个。因为当队列空时front=rear,当容纳n个元素时也有front=rear。因此当容纳n个元素时无法判断队列的空与满,所以最大容纳n-1个元素。
  • 若采用虚指方法实现队尾指针(rear指向队尾元素后一个元素,和实指相比后移一位),在具体实现上有何异同?哪一种更好?

    • 具体实现差别在于一个是判断空与满的不同,另一个是插入新节点与rear后移语句顺序的不同。实指针先后移,然后添加新节点,虚指针反之。实指针的判断空与满的判断分别为((rear+1)%n==front)与((rear+2)%n==front),虚指针的判断分别为(rear==front)与((rear+1)%n==front)。两者相比较之下,一方面虚指针的代码更为简洁,另一方面,少一个模运算,能够节省算法时间。(模运算耗时较大),但是就理解上,实指针更为直观。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值