Московский государственный индустриальный университет
Опубликован: 27.09.2006 | Доступ: свободный | Студентов: 3332 / 380 | Оценка: 4.17 / 3.79 | Длительность: 24:17:00
Специальности: Программист
Лекция 10:

Основы объектно-ориентированного программирования

Реализации контейнеров на базе вектора

Для того чтобы можно было использовать описанные в предыдущей секции контейнеры, необходимо реализовать определенные там интерфейсы. С практической точки зрения особый интерес представляют реализации на базе вектора, так как вектор (напомним, что это синоним массива) — структура данных, существующая в большинстве языков программирования.

Легко понять, однако, что невозможно реализовать потенциально бесконечные структуры данных, рассмотренные выше, на базе конечного вектора. По этой причине все наши реализации будут реализациями ограниченных контейнеров. При попытке добавить элемент в уже заполненный контейнер должна возникать исключительная ситуация ( Exception ). Фактически это означает необходимость внесения изменений в приведенные выше интерфейсы контейнерных классов, хотя иногда мы будем позволять себе игнорировать данную проблему и не заботиться об обеспечении генерации исключительной ситуации.

В нашем курсе предполагается только начальное знакомство с вопросами реализации контейнеров — гораздо более глубоко эта тема будет рассмотрена в дальнейшем.

Определение 10.9. Реализация на базе вектора называется непрерывной, если все элементы контейнера хранятся в векторе непрерывным куском и порядок элементов в имитируемой структуре (если он существует) соответствует порядку элементов в векторе.

В качестве первого примера рассмотрим следующую задачу.

Задача 10.2. Создайте непрерывную реализацию ограниченного стека целых чисел на базе вектора и оцените эффективность полученной программы.

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

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

Реализация ограниченного стека

Рис. 10.5. Реализация ограниченного стека

Идея реализации стека чрезвычайно проста — его дно размещается в начале вектора, а при добавлении элементов они помещаются в последовательные ячейки массива (см. рис. 10.5). Кроме вектора array, содержащего элементы стека, реализация использует переменную head (количество элементов стека). При этом вершина стека имеет индекс head-1, а значения элементов массива с большими индексами не играют никакой роли.

Текст программы

interface Stack {
    boolean empty();
    void    clear();
    void    push(int val) throws Exception;
    int     pop() throws Exception;
    int     top() throws Exception;
}
class StackTest implements Stack {
    private static final int DEFSIZE = 5;
    private int[] array;
    private int   head;

    public StackTest(int size) {
        array = new int[size];
        head = 0;
    }
    public StackTest() {
        this(DEFSIZE);
    }
    public boolean empty() {
        return head == 0;
    }
    public void clear() {
        head = 0;
    }
    public void push(int val) throws Exception {
        array[head++] = val;
    }
    public int pop() throws Exception {
        return array[--head];
    }
    public int top() throws Exception {
        return array[head-1];
    }
    public static void main(String[] args) throws Exception {
        StackTest s = new StackTest();
        while (true) {
            switch (Xterm.inputInt("Action [01234] -> ")) {
              case 0:	
                System.out.println("Stack is " + (s.empty() ?
                                                 "empty" :
                                                 "not empty"));
                break;
              case 1:
                s.clear(); break;
              case 2:
                s.push(Xterm.inputInt("Push int: ")); break;
              case 3:
                System.out.println("Pop int " + s.pop()); break;
              case 4:
                System.out.println("Top = " + s.top()); break;
              default:
                System.out.println("Wrong action, ignore");
            }
        }
    }
}

Эта программа позволяет производить со стеком все возможные операции. Рекомендуется поработать с ней — это поможет лучше понять, что же такое стек. Обратите также внимание на тот факт, что все операции со стеком в данной реализации имеют сложность \Theta(1) (их сложность не зависит от количества элементов в стеке).

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

Задача 10.3. Создайте непрерывную реализацию ограниченной очереди целых чисел на базе вектора и оцените эффективность полученной программы.

Текст программы

interface Queue {
    boolean empty();
    void    clear();
    void    push(int val) throws Exception;
    int     pop() throws Exception;
    int     front() throws Exception;
}
class QueueTest implements Queue {
    private int[] array;
    private int size, head, tail;
    private int forward(int index) {
        return ++index < array.length ? index : 0;
    }

    public QueueTest(int size) {
        array = new int[size];
        clear();
    }
    public boolean empty() {
        return size == 0;
    }
    public void clear() {
        size = head = 0;
        tail = array.length - 1;
    }
    public void push(int val) throws Exception {
        if(++size >= array.length) throw new Exception();
        array[tail=forward(tail)] = val;
    }
    public int pop() throws Exception {
        int val = front();
        head = forward(head);
        size -= 1;
        return val;
    }
    public int front() throws Exception {
        if(empty()) throw new Exception();
        return array[head];
    }
    public static void main(String[] args) throws Exception {
        QueueTest q = new QueueTest(5);
        while (true) {
            switch (Xterm.inputInt("Action [01234] -> ")) {
              case 0:	
                System.out.println("Queue is " + (q.empty() ?
                                                 "empty" :
                                                 "not empty"));
                break;
              case 1:
                q.clear(); break;
              case 2:
                q.push(Xterm.inputInt("Push int: ")); break;
              case 3:
                System.out.println("Pop int " + q.pop()); break;
              case 4:
                System.out.println("Front = " + q.front()); break;
              default:
                System.out.println("Wrong action, ignore");
            }
        }
    }
}
Анастасия Халудорова
Анастасия Халудорова
екатерина яковлева
екатерина яковлева