
книги / Технологии разработки объектно-ориентированных программ на язык C++. Основы структурного программирования на алгоритмическом языке C++
.pdfp->data = |
rand() % 10 + 1; |
|
p->next = |
NULL; |
/* Обнуляем его указатель на |
|
|
следующий элемент */ |
p->prev = q; |
|
// Указываем на прошлый элемент |
}
return first;
}
Подробнее: в строках 4–5 присваивается первый элемент. Затем в цикле от 2 до n (начальное количество элементов в списке) присваиваются остальные элементы. В строке 10 выделяется память для нового элемента. Затем в строке 12 указателю первого элемента присваивается указатель на только что созданный элемент, в строке 13 переходим к следующему элементу и для указателя нового элемента присваиваем указатель прошлого элемента (строка 16). Указателю только введенного элемента присваиваем NULL, так как он пока что является последним. В строке 18 возвращаем указатель на первый элемент.
Вывод списка на экран:
void print_list(List*first) { |
|
if (first == NULL) { |
// Если список пуст |
cout << "Список пустой\n"; |
|
} |
|
List* p = first; |
|
while (p != NULL) { |
|
cout << p->data << " "; |
// Вывод значения |
|
элемента списка |
p = p->next; |
// Переход к следующему |
|
элементу |
} |
|
cout << endl;
}
Подробнее: параметром передается указатель на первый элемент однонаправленного списка. В строке 2 выполняется проверка, пустой список или нет. В строке 6 указателю p присваивается указатель на первый элемент списка. В цикле (строки 7–10) выводятся значения каждого элемента списка, пока p не указывает на NULL.
131
Поиск элемента в списке:
List* find(List* first, int val) { |
|
if (first == NULL) return NULL; |
// Если список пуст |
List* p = first; |
|
List* n = nullptr; |
// Хранение |
|
найденного элемента |
while(p != NULL) { |
// Выполняется, пока |
|
есть элементы |
if (p->data == val) { |
// Если нашло равный |
|
элемент |
return p;
// Возвращается ссылка на найденный элемент
}
p = p->next; // Переход к следующему элементу
}
return 0;
}
Подробнее: параметром передается указатель на первый элемент двунаправленного списка и значение элемента, который необходимо найти. В цикле (строки 5–10) находим нужный элемент и возвращаем указатель на него, а если элемент не найден, то возвращаем 0.
Удаление элемента из списка:
void delete_element(int val, list* &first) {
if (first == NULL) return; |
// Если список пуст |
List* p = first; |
|
List* e = nullptr; |
|
bool f = false; |
// Для проверки |
while (p != NULL) {
// Проверка, входит элемент в список или нет if (p->data == val)
f = true; p = p->next;
}
132
if (!f) {
// Если не входит, то функция заканчивается cout << "Такого элемента нет. Удаление не произведено\n";
return;
}
p = first; // Обнуление р List* w = first;
while (w != NULL) { /*
Удаление первого элемента и, если все последующие за ним такого же значения
*/
if (first->data == find(first, val)->data) {
//Если удаляем первый элемент p = p->next;
//Переходим ко второму элементу first = p;
if (p != NULL) p->prev = NULL;
} |
|
w = w->next; |
|
} |
|
p = first; |
// Обнуление р |
int kol = 0; |
// Количество удаляемых |
|
элементов |
while (p != NULL) { |
// Подсчет количества удаляемых |
|
элементов |
if (p->data == val) |
|
kol++; |
// Количество удаляемых |
|
элементов |
p = p->next; |
|
} |
|
if (first |
== |
NULL) return; |
||
for (int j = |
0; |
j < kol; |
j++) { |
|
list* t |
= |
nullptr; |
|
|
p = first; |
|
|
||
int k = |
0; |
|
// Индекс удаляемого |
133
элемента while (p->data != val) {
// Находим индекс элемента p = p->next;
k++;
if (p->data == val) { t = p->next;
// Сохраняем указатели удаляемого элемента
e = p->prev;
}
}
p = first; // Обнуление р
for (int i = 0; p != NULL; i++) { if (i == k – 1)
p->next = t; /*
указателю на элемент, который находится перед удаляемым, присваиваем указатель удаляемого элемента
*/ if (i == k)
p->prev = e; p = p->next;
}
}
}
Подробнее: параметром передается указатель на первый элемент двунаправленного списка, значение элемента, который хотим удалить. В цикле (строки 6–10) проверяется, входит ли такой элемент в список. Если такого элемента нет, то функция прекращается (строка 13). В строках 17–25 происходит удаление первых повторяющихся элементов, если они равны переданному параметру. Если получилось так, что все элементы были удалены, то выходим из функции (строка 33). В цикле (строки 30–34) происходит подсчет количества удаляемых чисел, и это число сохраняется в перемен-
134
ную kol. Находится индекс удаляемого элемента в списке (строки 40–47), и число удаляется (строки 49–55).
Добавление элементов в конец списка:
void add_to_end(int kol, List* &first) {
if (first == NULL) return; |
// Если список пуст |
|
List* p |
= first; |
|
int k = |
0; |
// Счетчик |
while (p != NULL) { |
// Подсчитываем кол-во |
|
|
|
элементов |
p = p->next; k++;
}
p = first;
for (int i = 1; i < k; i++)
// Переходим к последнему элементу списка p = p->next;
for (int i = 1; i <= kol; i++) {
//Добавление kol элементов по принципу создания списка
List* h = new list;
// Выделение памяти для следующего элемента
List* q = p;
//Для того чтобы записать два указателя p->next = h;
//Настраиваем указатель предшествующего элемента
p = p->next; |
// Переходим к следующему |
|
|
|
элементу |
p->data = |
rand()%10 + 1; |
|
p->next = |
NULL; |
|
// Обнуляем его указатель на следующий элемент |
||
p->prev = q; |
|
// Указываем на прошлый |
|
|
элемент |
}
}
Подробнее: параметром передается указатель на первый элемент двунаправленного списка и количество добавляемых элемен-
135
тов. В строках 5–8 подсчитываем количество элементов в списке. В строках 10–11 переходим к последнему элементу списка. Добавляем элементы по принципу создания двунаправленного списка
(строки 12–20);
Главная функция:
int main() { |
|
setlocale(LC_ALL, "Rus"); |
|
srand(time(0)); |
|
List* list = make_list(10); |
// Создание списка |
print_list(list); |
// Печать списка |
int n; |
// Удаляемое число |
cout << "Введите число, которое хотите удалить из |
|
списка: "; |
|
cin >> n; |
|
delete_element(n, list); |
// Удаление чисел |
cout << "Новый список: "; |
|
print_list(list); |
// Печать списка |
int p; |
// Количество добавляемых |
чисел |
|
|
|
|
cout << "Введите количество добавляемых чисел: "; |
||
|
cin >> p; |
|
|
|
add_to_end(p, list); |
// Добавление случайных |
|
чисел |
|
|
|
|
|
|
в конец списка |
|
cout << "Новый список: "; |
|
|
|
print_list(list); |
// Печать списка |
|
|
return 0; |
|
|
} |
|
|
|
Пример выполнения программы: |
|
||
1 6 6 |
1 4 1 4 5 |
2 1 |
|
Введите число, которое хотите удалить из списка: 4 Новый список: 1 6 6 1 1 5 2 1 Введите количество добавляемых чисел: 7
Новый список: 1 6 6 1 1 5 2 1 2 1 1 7 7 6 3
136
14.5. Реализация двунаправленного списка через класс
Рассмотрим решение задачи в подразд. 14.3, реализованное через класс.
Первым шагом создается класс list со всеми необходимыми методами.
class List { |
// Класс список |
|
private: |
|
|
class Node { |
// Класс для ячеек |
|
public: |
|
|
node* next, *prev; |
// Указатель на следующий |
|
|
элемент |
|
int data; |
|
|
Node(int data1 = 0, node* next1 = nullptr, node* |
||
prev1 == nullptr) { |
// Конструктор |
|
data = data1; |
|
|
next = next1; |
|
|
prev = prev1; |
|
|
} |
|
|
}; |
|
|
Node* head; |
// Указатель на первый эле- |
|
мент |
|
|
int size; |
// Кол-во элементов списка |
|
public: |
|
|
List(); |
|
|
~List(); |
|
|
void push_back(int); |
// Вставка в конец списка |
|
void print(); |
// Печать списка |
|
bool delete_element_by_number(int); |
// Удаление |
|
|
|
элемента по |
|
|
индексу |
bool delete_element_by_value(int); |
// Удаление |
|
|
|
элемента по |
значению
};
137
Описывается дополнительный класс node внутри класса списка (строка 3), который отвечает за создание ячейки в списке, а класс list (строка 1) работает с этими ячейками. Также к приватным данным относится указатель на первый элемент списка и количество элементов списка. Со строки 17 по строку 26 описаны прототипы методов, которые работают со списком (добавление, удаление и т.д.).
Конструктор и деструктор:
List::List() { |
// Конструктор |
|
size = 0; |
// Пустой список |
|
head = nullptr; |
// Первого элемента пока нет |
|
} |
|
|
List::~List() { |
// Деструктор |
|
delete head; |
|
|
} |
|
|
Печать двунаправленного списка: |
|
|
void List::print() { |
|
|
if (head == nullptr) |
// Если список пуст |
|
cout << "Список пустой\n"; |
|
|
Node* t = head; |
|
// t указывает на |
|
|
первый элемент |
while (t) { |
|
|
cout << t->data << " "; |
// Вывод элементов |
|
t = t->next; |
|
// Переход к следующему |
|
|
элементу |
} |
|
|
cout << endl; |
|
|
} |
|
|
Подробнее: если происходит попытка напечатать список, в который еще не добавили элементы, то программа выведет, что список пустой (строка 3). Переменной t присваивается указатель на первый элемент списка (строка 4), и затем в цикле (строки 5–8) выводится значение элемента в списке и осуществляется переход к следующему элементу списка.
138
Добавление элемента в конец списка:
void List::push_back(int data) { |
|
if (head == nullptr) |
// Если вставляется |
|
первый элемент |
head = new Node(data); |
// Присвоение нового |
|
элемента |
else { |
|
Node* t; |
|
t = head; |
|
// t указывает на первый элемент |
|
while (t->next) |
// Переходим к последнему |
|
элементу |
t = t->next; |
|
t->next = new Node(data);
//Присваиваем новый элемент t->next->prev = t;
//Новый элемент указывает на прошлый
} |
|
size++; |
// Увеличиваем размер |
}
Подробнее: если добавляется первый элемент, то выделяется память для нового элемента, и указателю head присваивается указатель на только что выделенную память (строка 3). Если же добавляется не первый элемент, то переменной t присваивается указатель на первый элемент, и затем в цикле доходит до последнего элемента (строки 8–9). Затем выделяется память для нового объекта, и указателю последнего элемента присваивается указатель на новый элемент (строка 10), а указателю на прошлый элемент нового элемента присваивается указатель t (строка 11); size увеличивается на 1 (строка 13), так как только что добавили элемент.
Удаление элемента по индексу:
bool List::delete_element_by_number(int p) { if (head == nullptr) return 0;
else {
if (p < 0 || p > size) { // Если такого индекса нет
139
cout << "Неверно введен индекс\n"; return 0;
}
else {
if (p == 0) { // Если удаляем первый элемент
Node* del = head;
if (head->next != NULL) { /* Если остался не один элемент */
head = head->next; |
// |
|
Переприсваиваем |
|
head |
head->prev = NULL; |
/* Новый первый |
|
элемент |
|
указывает на |
|
0 */ |
} |
|
if (head->next == NULL) |
/* Если остался |
|
один элемент |
|
*/ |
head = NULL; |
|
delete del; |
|
}
else {
Node* t = head;
for (int i = 0; i < p; i++)
//Переходим к удаляемому элементу
t = t->next; Node* e = t->next;
//Указатель на элемент, стоящий после удаляемого
Node* y = t->prev;
// Указатель на элемент, стоящий до
удаляемого |
|
delete t; |
// Удаление |
|
элемента |
140