数据结构与算法(Dart)之队列(五)

452 阅读3分钟

队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作。进行插入操作的端称为队尾,进行删除操作的端称为队头。

队列中没有元素时,称为空队列。

队列的数据元素又称为队列元素。在队列中插入一个队列元素称为入队,从队列中删除一个队列元素称为出队。因为队列只允许在一端插入,在另一端删除,所以只有最早进入队列的元素才能最先从队列中删除,故队列又称为先进先出(FIFO—first in first out)

同栈一样,队列也可以用顺序表或者链表实现。

顺序队列

建立顺序队列结构必须为其静态分配或动态申请一片连续的存储空间,并设置两个指针进行管理。一个是队头指针front,它指向队头元素;另一个是队尾指针rear,它指向下一个入队元素的存储位置,如图所示

每次在队尾插入一个元素时,rear增1; 每次在队头删除一个元素时,front增1。随着插入和删除操作的进行,队列元素的个数不断变化,队列所占的存储空间也在为队列结构所分配的连续空间中移动。

当front=rear时,队列中没有任何元素,称为空队列。

当rear增加到指向分配的连续空间之外时,队列无法再插入新元素,但这时往往还有大量可用空间未被占用,这些空间是已经出队的队列元素曾经占用过得存储单元。

实现以下操作:

  • Queue():创建一个空的队列。
  • enQueue(item): 往队列中添加一个元素。
  • deQueue(): 列头部删除一个元素。
  • isEmpty(): 判断一个队列是否为空。
  • size(): 返回队列的大小。
abstract class Queue {
  /// 清空队列
  clear();

  /// 加入队列
  enQueue(item);

  /// 出队列
  deQueue();

  /// 判断队列是否为空
  bool isEmpty();

  /// 判断队列是否已满
  bool isFull();

  /// 队列大小
  int size();

  /// 打印
  display();
}

class SequenceQueue extends Queue {
  int _capacity = 0; // 队列的最大容量
  int _front = 0; // 队首元素指针
  int _rear = -1; // 队尾元素指针
  late List _list; // 存储元素的数组

  SequenceQueue({required int capacity}) {
    _capacity = capacity;
    _list = List.generate(capacity, (index) => null);
  }

  @override
  clear() {
    for (int i = _front; i != _rear; i = (i + 1) & (_list.length - 1)) {
      _list[i] = null;
    }
    _front = 0;
    _rear = -1;
  }

  @override
  enQueue(item) {
    /// 加入队列
    if (isFull()) throw StackOverFlowException();

    _rear = (_rear + 1) % _capacity;
    print('_rear:$_rear');

    _list[_rear] = item;
  }

  @override
  deQueue() {
    /// 出队列
    if (isEmpty()) throw StackOverFlowException();

    /// 取出队首位置元素
    var item = _list[_front];
    print('_front:$_front');

    /// 队首位置元素置null。回收对象
    _list[_front] = null;

    /// 队首元素指针 + 1, 指向下一个元素
    _front = (_front + 1) % _capacity;

    return item;
  }

  @override
  bool isEmpty() {
    /// 判断队列是否为空
    return _front == _rear;
  }

  @override
  bool isFull() {
    /// 判断队列是否已满
    return _rear == (_capacity - 1);
  }

  @override
  int size() {
    return (_capacity + (_rear - _front + 1)) % _capacity;
  }

  @override
  display() {
    /// 打印栈
    print("SequenceQueue: ${_list}\n");
  }
}

class StackOverFlowException implements Exception {
  const StackOverFlowException();
  String toString() => 'StackOverFlowException';
}

class StackEmptyException implements Exception {
  const StackEmptyException();
  String toString() => 'StackEmptyException';
}

void main() {
  SequenceQueue queue = new SequenceQueue(capacity: 6);

  queue.enQueue('1');
  queue.enQueue("2");
  queue.enQueue('3');
  queue.enQueue("4");
  queue.enQueue('5');
  queue.enQueue("6");

  queue.display();

  var popData;
  popData = queue.deQueue();
  print("deQueue $popData from queue\n");
  popData = queue.deQueue();
  print("deQueue $popData from queue\n");

  popData = queue.deQueue();
  print("deQueue $popData from queue\n");
  queue.display();

  final size = queue.size();
  print("deQueue size:$size \n");
}

单链表实现

截屏2024-01-23 下午5.58.38.png

class Node {
  /// 单链表的节点
  var item;
  Node? next;
  Node({this.item, this.next});
}

class LinkedQueue extends Queue {
  Node? _front; // 头节点
  Node? _rear; // 尾节点

  int _size = 0;

  LinkedQueue();

  @override
  enQueue(item) {
    /// 加入队列
    final Node node = Node(item: item);
    if (_rear == null) {
      /// 如果尾节点为空, 将新节点赋值给首节点
      _front = node;
    } else {
      /// 尾节点不为空, 将新节点追加到尾节点后面
      _rear?.next = node;
    }

    /// 修改尾节点为新节点
    _rear = node;

    _size++;
  }

  @override
  deQueue() {
    /// 出队列
    if (isEmpty()) throw StackEmptyException();

    /// 队首出队
    final item = _front?.item;
    _front = _front?.next;

    _size--;
    return item;
  }

  @override
  display() {
    /// 打印
    var cur = _front;
    while (cur != null) {
      print("LinkedQueue: ${cur.item}");
      cur = cur.next;
    }
    print("\n");
  }

  @override
  bool isEmpty() {
    /// 队列是否为空
    return _front == null;
  }

  @override
  int size() {
    return _size;
  }
}

void main() {
  LinkedQueue queue = new LinkedQueue();

  queue.enQueue('1');
  queue.enQueue("2");
  queue.enQueue('3');
  queue.enQueue("4");
  queue.enQueue('5');
  queue.enQueue("6");
  queue.enQueue("7");
  queue.enQueue("8");
  queue.enQueue("9");
  queue.enQueue("10");

  queue.display();

  var popData;
  popData = queue.deQueue();
  print("deQueue $popData from queue\n");
  popData = queue.deQueue();
  print("deQueue $popData from queue\n");

  popData = queue.deQueue();
  print("deQueue $popData from queue\n");

  popData = queue.deQueue();
  print("deQueue $popData from queue\n");

  popData = queue.deQueue();
  print("deQueue $popData from queue\n");

  queue.display();

  final size = queue.size();
  print("deQueue size:$size \n");
}

参考资料

数据结构与算法(三)

堆栈和队列「数据结构和算法5

实现一个简单的队列

What is Priority Queue | Introduction to Priority Queue

Priority Queue using Binary Heap

优先级队列的应用-Applications of Priority Queue