Двухсторонняя очередь

В этом руководстве вы познакомитесь с двухсторонними очередями (их еще называют двусвязными очередями) и узнаете, как их реализовать в Python, Java, C и C++. 

Deque или двухсторонняя очередь — тип очереди, в которой вставка и удаление элементов возможна и с начала, и с конца очереди. Это значит, что этот тип очереди не подчиняется принципу FIFO («первый пришел — первый вышел»). 

Типы двухсторонних очередей

1. Двухсторонняя очередь с ограниченной вставкой

В этой двухсторонней очереди вставка элементов осуществляется лишь с одной стороны очереди. Удаление все так же доступно с обеих сторон.

2. Двухсторонняя очередь с ограниченным удалением

В этой двухсторонней очереди удаление элементов осуществляется лишь с одной стороны очереди. Вставка так же доступна с обеих сторон.

Операции с двухсторонними очередями

Ниже представлена реализация двухсторонней очереди с помощью круговой очереди. Одна из особенностей круговой очереди заключается в том, что в случае заполнения массива мы все начинаем сначала. 

Но в случае с линейным массивом все не так — когда он заполняется, мы не можем добавлять новые элементы. 

Перед выполнением следующих операций выполните следующие действия:

  1. Объявите массив (двухстороннюю очередь) длины n.
  2. Объявите два указателя и поместите их в начало очереди. После этого присвойте front = -1, а rear = 0.

1. Вставка в начало очереди

С помощью этой операции мы можем вставить элемент в начало очереди.

• Проверьте позицию указателя front.

• Если front < 1, то объявите его повторно — front = n-1 (последний индекс).

• Иначе увеличьте значение front на 1.
• Добавьте новое значение 5 в array[front].

2. Вставка в конец очереди

С помощью этой операции мы можем вставить элемент в конец очереди.

• Проверьте, не заполнена ли очередь.

• Если очередь заполнена, повторно объявите указатель rear — rear = 0.
• Иначе увеличьте значение rear на 1.

• Добавьте новое значение 5 в array[5].

3. Удаление элемента из начала очереди

С помощью этой операции мы можем удалить элемент из начала очереди.

• Проверьте, не пуста ли очередь.

• Если двухсторонняя очередь пуста (front = -1), удаление произвести не удастся — возникнет антипереполнение.
• Если в очереди один элемент (front = rear), присвойте front = -1 и rear = -1.
• Если же front находится в конце очереди (front = n - 1), присвойте front = 0.
• Иначе присвойте front = front + 1.

4. Удаление из конца очереди

С помощью этой операции мы можем удалить элемент из конца очереди.

• Проверьте, не пуста ли очередь.

• Если двухсторонняя очередь пуста (front = -1), удаление произвести не удастся — возникнет антипереполнение.
• Если в очереди один элемент (front = rear), присвойте front = -1, а rear = -1. Иначе выполняйте следующие шаги.
• Если же rear находится в конце очереди (rear = 0), присвойте rear = n - 1.
• Иначе присвойте rear = rear - 1.

5. Проверка на пустоту 

С помощью этой операции можно проверить, не пуста ли двухсторонняя очередь. Если front = -1, она пуста.

6. Проверка на заполненность

С помощью этой операции можно проверить, не заполнена ли двухсторонняя очередь. Если front = 0 и rear = n - 1 ИЛИ front = rear + 1, она заполнена.

Реализация двухсторонних очередей

Python

# Реализация двухсторонней очереди в Python

class Deque:
    def __init__(self):
        self.items = []

    def isEmpty(self):
        return self.items == []

    def addRear(self, item):
        self.items.append(item)

    def addFront(self, item):
        self.items.insert(0, item)

    def removeFront(self):
        return self.items.pop()

    def removeRear(self):
        return self.items.pop(0)

    def size(self):
        return len(self.items)


d = Deque()
print(d.isEmpty())
d.addRear(8)
d.addRear(5)
d.addFront(7)
d.addFront(10)
print(d.size())
print(d.isEmpty())
d.addRear(11)
print(d.removeRear())
print(d.removeFront())
d.addFront(55)
d.addRear(45)
print(d.items)

Java

// Реализация круговых двухсторонних очередей в  Java

class Deque {
  static final int MAX = 100;
  int arr[];
  int front;
  int rear;
  int size;

  public Deque(int size) {
    arr = new int[MAX];
    front = -1;
    rear = 0;
    this.size = size;
  }

  boolean isFull() {
    return ((front == 0 && rear == size - 1) || front == rear + 1);
  }

  boolean isEmpty() {
    return (front == -1);
  }

  void insertfront(int key) {
    if (isFull()) {
      System.out.println("Переполнение");
      return;
    }

    if (front == -1) {
      front = 0;
      rear = 0;
    }

    else if (front == 0)
      front = size - 1;

    else
      front = front - 1;

    arr[front] = key;
  }

  void insertrear(int key) {
    if (isFull()) {
      System.out.println(" Переполнение ");
      return;
    }

    if (front == -1) {
      front = 0;
      rear = 0;
    }

    else if (rear == size - 1)
      rear = 0;

    else
      rear = rear + 1;

    arr[rear] = key;
  }

  void deletefront() {
    if (isEmpty()) {
      System.out.println("Антипереполнение\n");
      return;
    }

    // В очереди только один элемент
    if (front == rear) {
      front = -1;
      rear = -1;
    } else if (front == size - 1)
      front = 0;

    else
      front = front + 1;
  }

  void deleterear() {
    if (isEmpty()) {
      System.out.println(" Антипереполнение");
      return;
    }

    if (front == rear) {
      front = -1;
      rear = -1;
    } else if (rear == 0)
      rear = size - 1;
    else
      rear = rear - 1;
  }

  int getFront() {
    if (isEmpty()) {
      System.out.println(" Антипереполнение");
      return -1;
    }
    return arr[front];
  }

  int getRear() {
    if (isEmpty() || rear < 0) {
      System.out.println(" Антипереполнение\n");
      return -1;
    }
    return arr[rear];
  }

  public static void main(String[] args) {

    Deque dq = new Deque(4);

    System.out.println("Вставить элемент в конец очереди : 12 ");
    dq.insertrear(12);

    System.out.println("Вставить элемент в конец очереди : 14 ");
    dq.insertrear(14);

    System.out.println("Получаем элемент из конца : " + dq.getRear());

    dq.deleterear();
    System.out.println("После удаления последнего элемента новый последний элемент : " + dq.getRear());

    System.out.println("Вставляем элемент в начало");
    dq.insertfront(13);

    System.out.println("Получить первый элемент из начала: " + dq.getFront());

    dq.deletefront();

    System.out.println("После удаления первого элемента из начала очереди новый первый элемент : " + +dq.getFront());

  }
}

С

// Реализация круговых двухсторонних очередей в  C

#include <stdio.h>

#define MAX 10

void addFront(int *, int, int *, int *);
void addRear(int *, int, int *, int *);
int delFront(int *, int *, int *);
int delRear(int *, int *, int *);
void display(int *);
int count(int *);

int main() {
  int arr[MAX];
  int front, rear, i, n;

  front = rear = -1;
  for (i = 0; i < MAX; i++)
    arr[i] = 0;

  addRear(arr, 5, &front, &rear);
  addFront(arr, 12, &front, &rear);
  addRear(arr, 11, &front, &rear);
  addFront(arr, 5, &front, &rear);
  addRear(arr, 6, &front, &rear);
  addFront(arr, 8, &front, &rear);

  printf("\nЭлементы в двухсторонней очереди: ");
  display(arr);

  i = delFront(arr, &front, &rear);
  printf("\nудален элемент: %d", i);

  printf("\nОчередь после удаления элемента: ");
  display(arr);

  addRear(arr, 16, &front, &rear);
  addRear(arr, 7, &front, &rear);

  printf("\nОчередь после вставки элемента: ");
  display(arr);

  i = delRear(arr, &front, &rear);
  printf("\nудален элемент: %d", i);

  printf("\nОчередь после удаления элемента: ");
  display(arr);

  n = count(arr);
  printf("\nКоличество элементов в очереди: %d", n);
}

void addFront(int *arr, int item, int *pfront, int *prear) {
  int i, k, c;

  if (*pfront == 0 && *prear == MAX - 1) {
    printf("\nОчередь заполнена.\n");
    return;
  }

  if (*pfront == -1) {
    *pfront = *prear = 0;
    arr[*pfront] = item;
    return;
  }

  if (*prear != MAX - 1) {
    c = count(arr);
    k = *prear + 1;
    for (i = 1; i <= c; i++) {
      arr[k] = arr[k - 1];
      k--;
    }
    arr[k] = item;
    *pfront = k;
    (*prear)++;
  } else {
    (*pfront)--;
    arr[*pfront] = item;
  }
}

void addRear(int *arr, int item, int *pfront, int *prear) {
  int i, k;

  if (*pfront == 0 && *prear == MAX - 1) {
    printf("\nОчередь заполнена.\n");
    return;
  }

  if (*pfront == -1) {
    *prear = *pfront = 0;
    arr[*prear] = item;
    return;
  }

  if (*prear == MAX - 1) {
    k = *pfront - 1;
    for (i = *pfront - 1; i < *prear; i++) {
      k = i;
      if (k == MAX - 1)
        arr[k] = 0;
      else
        arr[k] = arr[i + 1];
    }
    (*prear)--;
    (*pfront)--;
  }
  (*prear)++;
  arr[*prear] = item;
}

int delFront(int *arr, int *pfront, int *prear) {
  int item;

  if (*pfront == -1) {
    printf("\nОчередь пуста.\n");
    return 0;
  }

  item = arr[*pfront];
  arr[*pfront] = 0;

  if (*pfront == *prear)
    *pfront = *prear = -1;
  else
    (*pfront)++;

  return item;
}

int delRear(int *arr, int *pfront, int *prear) {
  int item;

  if (*pfront == -1) {
    printf("\nОчередь пуста.\n");
    return 0;
  }

  item = arr[*prear];
  arr[*prear] = 0;
  (*prear)--;
  if (*prear == -1)
    *pfront = -1;
  return item;
}

void display(int *arr) {
  int i;

  printf("\n front:  ");
  for (i = 0; i < MAX; i++)
    printf("  %d", arr[i]);
  printf("  :конец");
}

int count(int *arr) {
  int c = 0, i;

  for (i = 0; i < MAX; i++) {
    if (arr[i] != 0)
      c++;
  }
  return c;
}

C++

// Реализация круговых двухсторонних очередей в  C++

#include <iostream>
using namespace std;

#define MAX 10

class Deque {
  int arr[MAX];
  int front;
  int rear;
  int size;

   public:
  Deque(int size) {
    front = -1;
    rear = 0;
    this->size = size;
  }

  void insertfront(int key);
  void insertrear(int key);
  void deletefront();
  void deleterear();
  bool isFull();
  bool isEmpty();
  int getFront();
  int getRear();
};

bool Deque::isFull() {
  return ((front == 0 && rear == size - 1) ||
      front == rear + 1);
}

bool Deque::isEmpty() {
  return (front == -1);
}

void Deque::insertfront(int key) {
  if (isFull()) {
    cout << "Переполнение\n"
       << endl;
    return;
  }

  if (front == -1) {
    front = 0;
    rear = 0;
  }

  else if (front == 0)
    front = size - 1;

  else
    front = front - 1;

  arr[front] = key;
}

void Deque ::insertrear(int key) {
  if (isFull()) {
    cout << " Переполнение\n " << endl;
    return;
  }

  if (front == -1) {
    front = 0;
    rear = 0;
  }

  else if (rear == size - 1)
    rear = 0;

  else
    rear = rear + 1;

  arr[rear] = key;
}

void Deque ::deletefront() {
  if (isEmpty()) {
    cout << "Антипереполнение\n"
       << endl;
    return;
  }

  if (front == rear) {
    front = -1;
    rear = -1;
  } else if (front == size - 1)
    front = 0;

  else
    front = front + 1;
}

void Deque::deleterear() {
  if (isEmpty()) {
    cout << " Антипереполнение\n"
       << endl;
    return;
  }

  if (front == rear) {
    front = -1;
    rear = -1;
  } else if (rear == 0)
    rear = size - 1;
  else
    rear = rear - 1;
}

int Deque::getFront() {
  if (isEmpty()) {
    cout << " Антипереполнение\n"
       << endl;
    return -1;
  }
  return arr[front];
}

int Deque::getRear() {
  if (isEmpty() || rear < 0) {
    cout << " Антипереполнение\n"
       << endl;
    return -1;
  }
  return arr[rear];
}

int main() {
  Deque dq(4);

  cout << "Вставить элемент в конец очереди \n";
  dq.insertrear(5);
  dq.insertrear(11);

  cout << "Последний элемент: "
     << dq.getRear() << endl;

  dq.deleterear();
  cout << "После удаления последнего элемента новый последний элемент: " << dq.getRear() << endl;

  cout << "Вставка в начало очереди \n";

  dq.insertfront(8);

  cout << "Первый элемент: " << dq.getFront() << endl;

  dq.deletefront();

  cout << "После удаления первого элемента новый первый элемент: " << dq.getFront() << endl;
}

Временная сложность

Временная сложность всех вышеприведенных операций — O(1).

Где применяются двухсторонние списки

  • Операции отмены в ПО.
  • Хранение истории браузера.
  • Реализация очередей и стеков.
codechick

СodeСhick.io - простой и эффективный способ изучения программирования.

2024 ©