Операции над односвязными списками. 


Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Операции над односвязными списками.



Основных операций над списками - три.

 

1) Проход по списку, или переход от элемента к следующему.

Как мы уже рассмотрели, это осуществляется с помощью присвоения типа q = q -> next;

 

2) Включение в список.

 

Пусть q, r - переменные типа elem*.

 

Предположи, что необходимо включить новый элемент в список после некоторого элемента, на который указывает q. Создадим этот новый элемент с помощью указателя r и занесем в его информационную часть число 19.

 

Такое включение осуществляется следующими операторами:

 

r = new elem; r->data = 19;

r->next = q->next; q->next = r;

 

Проиллюстрируем это на рис. 5.

Рис. 5. Включение элемента в список

3) Исключение из списка.

 

Пусть теперь значением переменной q является указатель на некоторый, не последний, элемент списка и требуется исключить из списка элемент, следующий за ним. Это можно сделать так:

 

r = q->next;

q->next = q->next->next;

r->next = NULL;

 

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

 

Третье присваивание выполняется для того, чтобы сделать исключение окончательным, т.е. чтобы из исключенного элемента нельзя было бы попасть в список, из которого он исключен.

 


Реализация односвязных списков.

Реализуем понятие списка через механизм классов.

 

// Файл "list.cpp"

#include < iostream.h >

#include < stdlib.h >

typedef int ETYPE;

struct elem {

ETYPE data;

elem * next;

elem (ETYPE e, elem * n) { data = e; next = n;}

};

class list {

elem *h; // Адрес начала списка.

public:

list () { h = NULL; }

~list () {release (); }

void create (ETYPE); // Добавляет элемент в начало списка.

void release (); // Удаляет список.

void insert (elem *q, ETYPE c); // Вставляет в список после с.

void del0 () { // Удаляет первый элемент.

elem *t = h; h = h->next; delete t;}

void del (elem * q); // Удаляет элемент после q.

void print (); // Распечатывает список.

friend class iter;

elem *first () { return h; }

};

class iter {

elem * current;

public:

iter (list & l) { current = l.h; }

elem * operator ++ (); // Продвижение по списку.

};

void list::create (ETYPE c){ h = new elem (с, h); }

void list::insert (elem *q, ETYPE c){

q->next = new elem (c, q->next);}

void list::del (elem *q){ if (q->next = = NULL){

cout << "Конец списка! "<<

"Удаление следующего элемента невозможно!\n"; exit (1); }

elem * r = q->next; q-> next = q->next->next;

r->next = NULL;

delete r;}

elem* iter::operator ++ (){

/* Возвращает указатель на текущий элемент списка. Осуществляет

продвижение по списку. Запоминает новый текущий элемент списка.

*/

if (current) { elem * tmp = current;

current = current->next;

return tmp; }

return NULL;

}

void list::release () {

iter t (*this);

elem *p;

while ((p = ++t)!= NULL) {h = h->next; delete p;}

}

void list::print () {

iter t (*this);

elem *p;

while ((p = ++t)!= NULL)

cout << p->data << " ";

cout << '\n';

}

// Конец файла list.cpp

 

Здесь реализован односвязный список. Это одна из простых моделей структур управления данным. Класс list, реализующий эту модель - представитель так называемых содержательных, или контейнерных типов. Класс iter создан специально для перебора элементов произвольного списка типа list. Объекты, предназначенные для перебора элементов внутри некоторого набора данных, обычно называют итераторами.

 

Приведем пример использования односвязного списка.

 

В файле int.dat расположена непустая последовательность целых чисел A1, A2,,..., An. Определить количество этих чисел n и вывести их в порядке возрастания.

 

Для решения этой задачи будем строить список, элементы которого упорядочены по возрастанию содержащихся в них целых чисел. Построение выполняется за n шагов. Первый шаг - это создание списка, состоящего из одного элемента, который содержит А1. Очевидно, этот список упорядочен. На i-м шаге (i = 2, 3, …, n) переходим от упорядоченного списка, элементы которого содержат числа А1,..., Ai-1, к упорядоченному списку, элементы которого содержат A1,…, Ai-1, Ai. Для выполнения такого перехода достаточно включить в список новый элемент, содержащий Аi. Его надо вставить непосредственно за последним по порядку элементом, содержащим число, меньшее, чем Аi.

 

Если же все элементы исходного списка содержат числа, не меньшие, чем Ai, то новый элемент нужно вставить в начало списка.

 

#include "list.cpp"

#include < fstream.h >

 

void main () {

ifstream file ("int.dat");

list lst; int i, n;

file >> i; n = 1;

lst.create (i);

while (fil.peek ()!= EOF)

{ file >> i; n ++;

iter tmp (lst); // Создаем объект-итератор для перебора

// элементов списка lst.

elem *p, *q;

while ((p = ++tmp)!= NULL)

if (p->data < i) q = p;

else break;

if (p = = lst.first ()) lst.create (i);

else lst.insert (q, i);

}

cout <<" В файле чисел: " << n << "\n";

cout <<" Упорядоченный список:\n";

lst.print ();

}

 

В последнем операторе if - else делается проверка p = = lst.first (). Это необходимо из-за того, что механизм вставки звена в начало списка и в список после указателя p различен. Различие возникает из-за того, что у первого элемента нет предыдущего. Иногда для единообразия в начало списка помещают так называемый заглавный элемент, который никогда не удаляют, и перед которым никогда не вставляют элемент. Его информационная часть обычно не используется.



Поделиться:


Последнее изменение этой страницы: 2016-08-15; просмотров: 542; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 18.216.94.152 (0.014 с.)