
- •Введение
- •Блок-схема алгоритма
- •Общие требования к блок-схеме алгоритма
- •Линейные и разветвляющиеся процессы
- •Циклические процессы
- •Итерационные процессы
- •Основные понятия языка С(С++)
- •Комментарии
- •Типы данных
- •Данные целого типа
- •Данные вещественного типа
- •Модификатор const
- •Переменные перечисляемого типа
- •Константы
- •Структура программы на языке С(С++)
- •Операции и выражения
- •sizeof
- •Операция присваивания
- •Арифметические операции
- •Операции поразрядной арифметики
- •Логические операции
- •Операции отношения
- •Инкрементные и декрементные операции
- •Операция sizeof
- •Порядок выполнения операций
- •Приоритет операций
- •Преобразование типов
- •Операция приведения
- •Операция запятая
- •Ввод и вывод информации
- •Директивы препроцессора
- •Директива #include
- •Директива #define
- •Операторы языка С(С++)
- •Понятие пустого и составного операторов
- •Операторы организации цикла
- •Оператор цикла for
- •Оператор цикла while
- •Оператор цикла do … while
- •Вложенные циклы
- •Примеры программ
- •Массивы
- •Одномерные массивы
- •Примеры программ
- •Многомерные массивы (матрицы)
- •Примеры программ
- •Указатели
- •Понятие указателя
- •Описание указателей
- •Операции с указателями
- •Связь между указателями и массивами
- •Массивы указателей
- •Многоуровневые указатели
- •Примеры программ
- •Символьные строки
- •Ввод/вывод строк.
- •Функции работы со строками.
- •Примеры программ
- •Функции
- •Прототип функции.
- •Определение функции.
- •Параметры функции
- •Передача массива в функцию
- •inline функции
- •Класс памяти
- •Автоматические переменные
- •Статические переменные
- •Регистровые переменные
- •Блочная структура
- •Примеры программ
- •Указатели на функции
- •Примеры программ
- •Рекурсия
- •Примеры программ
- •Аргументы в командной строке
- •Функции с переменным числом параметров
- •Вершина стека
- •Примеры программ
- •Сортировка
- •Пузырьковая сортировка.
- •Шейкер сортировка
- •Сортировка вставкой
- •Сортировка выбором
- •Метод Шелла
- •Метод Хора
- •Структуры
- •Указатели на структуры.
- •Структуры и функции
- •Примеры программ
- •Поля бит
- •Объединения
- •Переменные с изменяемой структурой
- •Организация списков и их обработка
- •Операции со списками при связном хранении
- •Стек
- •Построение обратной польской записи
- •Односвязный линейный список, очередь
- •Двусвязный линейный список
- •Циклический список, кольцо
- •Двусвязный циклический список
- •Примеры программ
- •Деревья
- •Файлы
- •Примеры программ
- •Литература
getch(); |
// задержка |
}
//функция выделения k MAX элементов из списка параметров
//найденные элементы возвратить в main
int * fun(...)
{int i,j,k,n=0,*ii,**jj; void *p;
printf("\n сколько MAX переменных надо выделить из списка = ");
scanf("%d",&k); // ввести ограничение на k (не более чем параметров) ii=(int *)calloc(k+1,sizeof(int)); // k max значений из списка (k+1 эл-т
|
// всегда ==0 - признак конца для main) |
jj=(int **)calloc(k,sizeof(int *)); // адреса k max значений в стеке |
|
while(n<=k-1) |
// пока не найдено k max элементов |
{ p=(...); |
// элипсис, p устанавливается на начало списка |
|
// параметров переменной длинны |
do
{i=0;
while(i<n && jj[i]) if (jj[i++]==(int *)p)
{((int *)p)++; break;
//поиск первого еще не занесенного
//адрес уже есть
//выбор следующего элемента из стека
} |
|
} while(jj[i]); |
|
ii[n]=*(int *)p; |
// предварительное max значение для сравнения |
jj[n]=((int *)p)++; |
// его адрес в стеке |
while(j=*((int *)p)) |
// пока считанный j!= 0 (признак конца списка) |
{ for(i=0;i<n && jj[i]!=(int *)p && jj[i++];); // поиск адреса эл-та в jj if (j>ii[n] && jj[i]!=(int *)p) // эл-т max и его адрес не найден
{ ii[n]=*((int *)p); |
// новое max значение для сравнения |
jj[n]=((int *)p); |
// его адрес в стеке |
} |
|
((int *)p)++; |
// указатель на следующий элемент стека |
} |
|
n++; |
|
} |
|
free(jj); |
|
return ii; |
|
}
Сортировка
Сортировка применяется во многих программах оперирующих информацией. Цель сортировки – упорядочить информацию и облегчить поиск требуемых данных. Существует много методов сортировки данных. Причём для разных типов данных иногда целесообразно применять разные методы сортировки.
Практически каждый алгоритм сортировки можно разбить на три части:
-сравнение двух элементов, определяющее упорядоченность этой пары;
-перестановку, меняющую местами неупорядоченную пару элементов;
-сортирующий алгоритм, определяющий выбор элементов для сравнения
иотслеживающий общую упорядоченность массива данных.
Сортировка данных в оперативной памяти называется внутренней, а сортировка данных в файлах называется внешней. Ниже будут рассматриваться методы сортировки данных в оперативной памяти.
Пузырьковая сортировка.
Название метода отражает его суть. ”Легкие”элементы массива ”всплывают” вверх(в начало), а ”тяжелые” опускаются вниз(в конец). Пузырьковая сортировка проходит по массиву снизу вверх. Каждый элемент массива сравнивается с элементом, который находится непосредственно над ним. И если при их сравнении оказывается, что они удовлетворяют условию их перестановки, то она выполняется. Процесс сравнений и перестановок продолжается до тех пор, пока ”легкий” элемент не “всплывет” вверх. В конце каждого прохода по массиву, верхняя граница сдвигается на один элемент вниз(вправо). Сортировка продолжается анализируя все меньшие массивы. В примере выполняется сортировка элементов массива размерностью k.
void sort(int *ms, int k) { int i,j,m;
for(i=0;i<k-1;++i) for(j=k-1;j>i;--j) {if(ms[j-1]>ms[j]) { m=ms[j-1];
ms[j-1]=ms[j]; ms[j]=m;
}
}
}
Внутренний цикл for выполняет проход по подмассиву в направлении обратном внешнему циклу. Если изменить направление этих циклов, то не наименьший будет “всплывать”, а наибольший “тонуть”.
Шейкер сортировка
В этом методе учитывается тот факт, что от последней перестановки до конца массива будут находиться уже упорядоченные данные. Тогда просмотр имеет смысл делать не до конца массива, а до последней перестановки на предыдущем просмотре. Если же просмотр делать попеременно в двух направлениях и фиксировать нижнюю и верхнюю границы неупорядоченной части, то получим шейкер-сортировку. Ниже приведен пример программы, использующей шейкер-сортировку элементов массива размерностью k.
void shaker(int *ms, int k)
{register int i,a,b,c,d; c=1;
b=k-1; //номер элемента на котором остановка
d=k-1; //номер стартового элемента для сортировки справа налево do
{for(i=d;i>=c;--i)
{if (ms[i-1]>ms[i])
{a=ms[i-1];
ms[i-1]=ms[i]; ms[i]=a;
b=i; // крайний слева упорядоченный элемент
}
}
c=b+1; //номер стартового элемента для сортировки слева направо for(i=c;i<=d;++i)
{if (ms[i-1]>ms[i])
{a=ms[i-1];
ms[i-1]=ms[i];
ms[i]=a; |
|
b=i; |
// крайний слева упорядоченный элемент |
} |
|
} |
|
d=b-1; |
|
} while(c<=d); |
|
}
Сортировка вставкой
Вметоде вставки на первом шаге выполняется сортировка первых двух элементов массива. Далее алгоритм ставит третий элемент в порядковую позицию, соответствующую его положению относительно первых двух элементов. Затем в этот список вставляется четвертый элемент и т.д. Процесс продолжается до тех пор, пока все элементы не будут отсортированы. Например, если бы строка "dcab" сортировалась методом вставки, то каждый из проходов давал бы результат:
Вэтом методе определяется место в отсортированной части массива, куда должен помещаться элемент. Элементы от данной позиции сдвигаются вверх и на освободившееся место помещается элемент. Трудоемкость алгоритма при линейном поиске n*n/4. Если используется двоичный поиск для определения места элемента, то трудоемкость алгоритма составляет n*log(n). Пример программы, использующей сортировку вставкой элементов массива размерностью k :
void srt2(int *ms, int k) { register int i,j,n;
for(i=1;i<k;++i) |
// индекс элемента для упорядочивания |
{ j=i-1; |
// индекс предыдущего элемента |
n=ms[i]; |
// значение предыдущего элемента |
while (j>=0 && n<ms[j])
ms[j--+1]=ms[j]; |
// сдвиг всех элементов направо |
ms[j+1]=n; |
// запись в освободившийся или в тот же элемент |
} |
|
} |
|
Сортировка выбором
Выбирается очередной исходный элемент массива. Последовательно проходя весь массив, сравниваем этот элемент со всеми, находящимися после него, и, при нахождении элемента удовлетворяющего условию замены запоминаем его индекс. После просмотра всего массива, переставляем выбранный элемент с исходным. После перебора всех элементов, получим отсортированный массив. Пример программы, реализующей сортировку
выбором, приведен ниже: |
|
void sort(int *ms, int k) |
|
{ int i,i1,j,m; |
|
for(i=0; i<k-1; i++) |
// выбор исходного элемента к сравнению |
{ i1=i; |
// |
for(j=i+1; j>i; j--) |
// просмотр массива ”снизу” ”вверх” |
if(ms[k]>ms[j]) i1=j; |
// фиксируем координату элемента в массиве |
m=ms[i]; |
// замена i1 и i элементов |
ms[i]=ms[i1]; |
|
ms[i1]=m; |
|
} |
|
} |
|
Отмеченные методы сортировки не являются эффективными. В программах, где требуется высокая производительность работы сортировок, применяются следующие методы.
Метод Шелла
Метод был предложен автором (Donald Lewis Shell) в 1959г. Основная идея алгоритма состоит в том, что на начальном этапе реализуется сравнивание и, если требуется, перемещение далеко отстоящих друг от друга элементов. Интервал между сравниваемыми элементами (gap) постепенно уменьшается до единицы, что приводит к перестановке соседних элементов на последних стадиях сортировки (если это необходимо).
Реализуем метод Шелла следующим образом. Начальный шаг сортировки примем равным n/2, т.е. 1/2 от общей длины массива, и после каждого прохода будем уменьшать его в два раза. Каждый этап сортировки включает в себя проход всего массива и сравнение отстоящих на gap элементов. Проход с тем же шагом повторяется, если элементы переставлялись. Заметим, что после