- •Введение
- •1. ТИПЫ ДАННЫХ И ОПЕРАТОРЫ
- •1.1. Переменные и базовые типы данных
- •1.2. Операции и выражения
- •1.3. Символические константы
- •1.5. Несколько слов о функции main()
- •2. ВВОД И ВЫВОД В СИ
- •2.2. Форматный ввод-вывод
- •3. ЦИКЛЫ И ОПЕРАТОРЫ СРАВНЕНИЯ
- •3.1. Условный оператор
- •3.2. Оператор выбора switch
- •3.3. Операторы цикла
- •3.4. Операторы break и continue
- •3.5. Примеры
- •3.6. Вычисление значений элементарных функций
- •3.7. Задачи
- •4. ОБРАБОТКА ПОСЛЕДОВАТЕЛЬНОСТЕЙ
- •4.1. Примеры
- •4.2. Задачи
- •5. ОДНОМЕРНЫЕ МАССИВЫ
- •5.1. Начальные сведения о массивах
- •5.2. Примеры работы с массивами
- •5.3. Задачи
- •6. МНОГОМЕРНЫЕ МАССИВЫ
- •6.1. Определение и инициализация двумерных массивов
- •6.2. Примеры с двумерными массивами
- •6.3. Задачи
- •7. УКАЗАТЕЛИ И МАССИВЫ
- •7.1. Указатели и адреса
- •7.2. Указатели и аргументы функций
- •7.3. Указатели и массивы
- •7.4. Операции с указателями
- •7.5. Указатели с типом void
- •7.6. Модификатор const
- •7.7. Массивы переменного размера
- •7.8. Массивы указателей
- •7.9. Двумерные массивы переменного размера
- •8. СИМВОЛЫ И СТРОКИ
- •8.1. Представление символьной информации в ЭВМ
- •8.2. Библиотека обработки символов
- •8.3. Строки в языке Си
- •8.4. Функции обработки строк
- •8.5. Функции преобразования строк
- •8.6. Примеры работы со строками
- •8.7. Разбиение строки на лексемы
- •8.8. Задачи
- •9. СТРУКТУРЫ
- •9.1. Основные сведения о структурах
- •9.2. Объединения
- •10. ДИРЕКТИВЫ ПРЕПРОЦЕССОРА
- •10.1. Директива #include
- •10.2. Директива #define
- •10.3. Директива #undef
- •10.4. Условная компиляция
- •11. ФУНКЦИИ
- •11.1. Основные сведения о функциях
- •11.2. Прототипы функций
- •11.3. Классы памяти
- •11.4. Указатели на функции
- •11.5. Рекурсия
- •11.6. Примеры с использованием рекурсии
- •11.7. Метод «разделяй и властвуй»
- •11.8. Задачи на применение рекурсии
- •12. РАБОТА С БИТАМИ ПАМЯТИ
- •12.1. Битовые операции
- •12.2. Примеры с использованием битовых операций
- •12.3. Задачи
- •13. РАБОТА С ФАЙЛАМИ
- •13.1. Файлы и потоки
- •13.2. Текстовые файлы
- •13.3. Двоичные файлы
- •13.4. Шифрование файлов
- •13.5. Задачи на текстовые файлы
- •13.6. Задачи на двоичные файлы
- •14. СТРУКТУРЫ ДАННЫХ
- •14.1. Односвязные списки
- •14.2. Примеры работы с односвязными списками
- •14.3. Задачи на односвязные списки
- •14.4. Стеки, очереди
- •14.5. Задачи на стеки и очереди
- •14.6. Двусвязные списки
- •14.7. Задачи на двусвязные списки
- •14.8. Бинарные деревья
- •14.9. Примеры с использованием бинарных деревьев
- •14.10. Задачи на бинарные деревья
- •Приложение 1. АЛГОРИТМЫ ПОИСКА
- •1. Линейный поиск
- •2. Поиск с барьером
- •3. Двоичный поиск
- •Приложение 2. АЛГОРИТМЫ СОРТИРОВКИ
- •Несколько слов о сложности алгоритмов
- •1. Метод прямого выбора
- •2. Метод прямого включения
- •3. Пузырьковая сортировка
- •4. Шейкерная сортировка
- •5. Быстрая сортировка
- •6. Сортировка подсчетом
- •Приложение 3. СОРТИРОВКА ИНДЕКСОВ И УКАЗАТЕЛЕЙ
- •1. Сортировка индексов на основе метода прямого выбора
- •2. Сортировка индексов на основе пузырьковой сортировки
- •3. Сортировка индексов на основе быстрой сортировки
- •4. Сортировка двумерных массивов
- •5. Сортировка строк
- •Приложение 4. СОРТИРОВКА ФАЙЛОВ И СПИСКОВ
- •1. Сортировка двоичных файлов
- •2. Сортировка линейных списков
- •Приложение 5. СОРТИРОВКА С УСЛОВИЕМ
- •1. Сортировка с условием на базе пузырьковой сортировки
- •2. Сортировка с условием на базе быстрой сортировки
- •3. Сортировка с условием двоичных файлов
- •4. Сортировка с условием линейного списка на базе пузырьковой сортировки
- •5. Сортировка с условием линейного списка на базе быстрой сортировки
- •ЛИТЕРАТУРА
*first = q;
else (*last)->next = q; *last = q;
}
/* извлечение информации из первого элемента очереди с последующим удалением этой вершины
*/
int Extract(ELEMENT **first, ELEMENT **last, int *x)
{
ELEMENT *q;
if (*first == NULL) return 0;
*x = (*first)->data; q = *first;
*first = (*first)->next; if (*first == NULL)
*last = NULL; free(q);
return 1;
}
int main ( )
{
int i, x;
ELEMENT *first = NULL, *last = NULL; for (i = 0; i < 10; i++)
AddElement(&first, &last, i); while (Extract(&first, &last, &x))
printf("x = %d\n", x); return 0;
}
14.5.Задачи на стеки и очереди
1.Сформировать стек целых чисел, вводимых пользователем, а затем его содержимое вывести на экран, не удаляя элементы,
261
из которых извлекается информация. Также написать функцию удаления стека.
2.Расположить элементы целочисленного массива размером n в обратном порядке с использованием стека.
3.Написать функцию, которая слова в текстовом файле распечатывает в обратном порядке. По файлу можно пройти только один раз.
4.Элементы целочисленного массива записать в очередь. Написать функцию извлечения элементов из очереди до тех пор, пока первый элемент очереди не станет четным.
5.Даны две непустые очереди, которые содержат одинаковое количество элементов. Объединить очереди в одну, в которой элементы исходных очередей чередуются.
6.Даны две непустые очереди. Элементы каждой из очередей упорядочены по возрастанию. Объединить очереди в одну с сохранением упорядоченности элементов.
7.Пусть имеется файл действительных чисел и некоторое число C. Используя очередь, напечатать сначала все элементы, меньшие числа C, а затем все остальные элементы.
14.6. Двусвязные списки
Для более гибкой работы с линейными списками каждый элемент можно снабдить дополнительным ссылочным полем, чтобы каждый элемент содержал ссылки на два соседних элемента:
В данном случае упрощается операция добавления и удаления элемента списка, а также можно исследовать список в любом направлении.
262
Следующий небольшой пример иллюстрирует работу с двусвязными списками. В данном примере создается двусвязный список вводимых с клавиатуры чисел в порядке возрастания.
#include<stdio.h>
#include<stdlib.h>
typedef struct ELEMENT
{
int data;
struct ELEMENT *prev, *next; } ELEMENT;
/* создание заглавного элемента списка */ void CreateHead(ELEMENT **head)
{
*head = (ELEMENT *)malloc(sizeof(ELEMENT)); (*head)->prev = (*head)->next = NULL;
}
/* вставка элемента в список с условием упорядоченности*/ void Insert(ELEMENT *head, int x)
{
ELEMENT *q, *t; q = head;
/* ищем позицию для нового элемента со значением x: */ while(q->next != NULL && q->next->data <= x)
q = q->next;
t = (ELEMENT *)malloc(sizeof(ELEMENT)); t->data = x;
t->next = q->next;
if (q->next != NULL) q->next->prev = t;
t->prev = q; q->next = t;
}
263
/* вывод списка на экран: */ void Print(ELEMENT *head)
{
ELEMENT *q = head->next; while(q != NULL)
{
printf("%d -> ", q->data); q = q->next;
}
putchar('\n');
}
int main()
{
ELEMENT *head; int x; CreateHead(&head);
while(scanf("%d", &x) == 1) Insert(head, x);
Print(head); return 0;
}
14.7.Задачи на двусвязные списки
1.Пусть имеется односвязный список действительных чисел, каждый элемент которого содержит дополнительное (нереализованное) ссылочное поле prev. Преобразовать исходный односвязный список в двусвязный, в котором каждый элемент связан не только с последующим элементом (с помощью поля next), но и с предыдущим (с помощью поля prev).
2.Написать функцию, которая по произвольному указателю на один из элементов двусвязного списка подсчитывает количество элементов в этом списке.
3.Написать функцию, которая, получив в качестве параметра указатель на один из элементов двусвязного списка действи-
264