
- •1. Двоичная система счисления.
- •2. Восьмеричная система счисления.
- •3. Шестнадцатеричная система счисления.
- •4. Сложение и вычитание в 2, 8 и 16 c/c.
- •2. Вещественные числа (числа с плавающей запятой).
- •3. Логические данные.
- •1. Алфавит языка Си
- •2. Элементы языка
- •3. Типы данных
- •1. Формульно-словесный способ.
- •2. Блок-схемный способ.
- •Ввод - вывод одномерного массива
- •2. Вывод одномерного массива на экран.
- •Примеры обработки одномерных массивов
- •Удаление элементов из массива
- •«Школьный» алгоритм сортировки
- •Группировка массива методом прямой выборки
- •Группировка массива методом прямого обмена
- •Вставка элемента в упорядоченный массив
- •1. Ввод элементов матрицы с клавиатуры.
- •2. Вывод матрицы на экран.
2. Вывод одномерного массива на экран.
Для компактности изображения массива на экране в каждой строке экрана целесообразно печатать несколько элементов массива.
Пусть формат вывода элемента вещественного массива имеет вид 8:2. Устанавливая между числами два пробела, в одной строке экрана можно разместить 8 чисел (строка экрана содержит 80 позиций). Тогда программа вывода может иметь вид:
.....................
k=0;
for (i=0;i<n;i++)
{
k++;
if (k<8)
printf("%8.2lf ",x[i]);
else
{
k=0;
printf("%8.2lf\n",x[i]);
}
}
if (k>0) printf("\n");
.....................
Переменная k - это счетчик количества чисел, выводимых в одну строку экрана. Пока k < 8, функция printf("%8.2lf ",x[i]) размещает очередные элементы массива в одной и той же строке экрана через 2 пробела. При k = 8 после вывода числа производится переход на следующую строку экрана printf("%8.2lf\n",x[i]), при этом счетчику k присваивается нулевое значение.
Предположим,
что в массиве X
количество
элементов не кратно 8. Тогда вывод
последнего элемента
будет осуществлен без перехода на новую
строку экрана. Если в программе после
вывода массива X
выполняется
еще вывод хотя бы одного числа, то это
число будет размещено в той же строке
экрана, где расположен элемент
,
что по крайней мере неэстетично. Для
предотвращения такой ситуации в
вышеприведенной программе записан
оператор "if
(k>0) printf("\n");".
Если известно, что печатаемые значения будут занимать менее, например, 8 символов, то можно воспользоваться оператором:
for(i=0; i<n;i++) printf("%8.2lf",x[i]);
без использования счетчика выведенных значений. В этом случае, в строке распечатается 10 значений, разделенных, по меньшей мере, одним пробелом, после чего курсор автоматически будет переведен на следующую строку. Этот способ уменьшает сложность программы печати значений массива и применим в тех случаях, когда можно в формате печати задать длину поля числом, на которое 80 будет делиться без остатка.
Примеры обработки одномерных массивов
Пример 1. Вычислить среднее арифметическое значение элементов массива X=(x0, x1, x2, …, xn-1).
Блок-схема
Си-программа
#define NMAX 500
int main()
{
int i, // параметр цикла
n; // кол-во элементов массива
float S; // среднее арифметическое значение
float X[NMAX]; // исходный массив
//Ввод и печать n, X
S=0;
for (i=0;i<n;i++)
S += X[i];
S /= n;
//Печать S
getch();
return 0;
}
В программе переменная S используется, как сумматор. Поэтому в программе перед накоплением в переменной S суммы элементов массива X производится обнуление этой переменной оператором S = 0. Вместо этого оператора можно было использовать инициализацию переменной при ее определении, если до использования в цикле ее значение не изменялось.
Следует
обратить внимание еще на одно
обстоятельство. Массиву Х
в программе выделяется память, необходимая
для размещения
500 элементов. В принципе в программе
можно было бы написать: for
(i=0;i<NMAX;i++).
Однако это означало бы, что программа может обрабатывать лишь массив, содержащий 500 элементов, но не 499 или 100. Поэтому для обеспечения универсальности работы программы обработке подвергается текущее количество элементов n в предположении, что 0<n<NMAX.
Пример 2. Вычислить среднее арифметическое значение положительных элементов массива X=(x0, x1, x2, …, xn-1).
Блок-схема
Отличие решения этой задачи от предыдущего примера заключается в том, что перед накоплением сумы элементов массива в S проверяется его значение (оно должно быть положительным X[i] > 0). В переменной k подсчитывается количество таких чисел. В частном случае в массиве X может не быть ни одного положительного элемента (k==0). Тогда при отсутствии оператора «if (k>0)» было бы деление на нуль, что даст неправильный результат. В программе используется оператор «if (k)», т.к. значение k может быть только нулевым или больше нуля.
Си-программа
#define NMAX 500
int main()
{
int i, // параметр цикла
k, // счетчик положительных чисел
n=10;// кол-во элементов массива
float S; // среднее арифметическое значение
float x[NMAX]; // исходный массив
//Ввод и печать n, X
S=k=0;
for (i=0;i<n;i++)
if (x[i]>0)
{
S+=x[i]; k++;
}
if (k)
S/=k;
//Печать S
getch();
return 0;
}
Пример 3. Определить y=max(x0, x1, x2, …, xn-1).
Первый элемент массива принимается за максимальный и записывается в переменную max. Далее в цикле осуществляется просмотр массива, и каждый i-й элемент сравнивается со значением max. Если i-й элемент массива больше max, то запоминается новое значение максимального элемента.
Си программа
#define NMAX 500
int main()
{
int i, // параметр цикла
n; // кол-во элементов массива
float max; // максимальное значение
float x[NMAX]; // исходный массив
//Ввод и печать n, X
max=x[0];
for (i=1;i<n;i++)
if (x[i]>max)
max=x[i];
//Печать max
getch();
return 0;
}
Пример 4. Определить y=max(x0, x1, x2, …, xn-1) среди отрицательных элементов массива.
Применить алгоритм из примера 2 нельзя. Если первый элемент массива принимать за максимальный, а он может быть положительным или нулем, то в этом случае ни одно из отрицательных чисел не будет больше него. Поэтому в переменную Xmax записывается неотрицательное значение, например, 0. Это так называемый «флаг». Далее в цикле осуществляется просмотр массива, начиная с нулевого, и каждый i-й элемент проверяется на соответствие требованиям к элементам, включаемым в поиск максимума (отрицательные). Если i-й элемент массива отрицательный, то проверяется значение флага. Если Xmax==0 (значение, которое в него было записано до начала цикла), считается, что это появление первого отрицательного элемента, его сравнивать еще не с чем, он просто запоминается. Таким образом, в Xmax появляется ненулевое значение – признак того, что отрицательные числа были, и очередной отрицательный элемент массива есть с чем сравнивать. Говорят, что значение флага изменилось. В качестве флага можно использовать отдельную переменную. В нашем случае Xmax является флагом и одновременно в ней запоминается значение максимума.
Если Xmax!=0, то x[i] сравнивается со значением Xmax. Если i-й элемент массива больше Xmax, то запоминается новое значение максимального элемента.
Си программа
#define NMAX 500
int main()
{
int i, // параметр цикла
n; // кол-во элементов массива
float Xmax; // максимальное значение
float x[NMAX]; // исходный массив
//Ввод и печать n, X
Xmax=0;
for (i=0;i<n;i++)
if (x[i]<0)
if (Xmax==0)
Xmax=x[i];
else
if (x[i]>Xmax)
Xmax=x[i];
//Печать max
getch();
return 0;
}
Пример 5. Обменять местами максимальный и минимальный элементы массива X.
Здесь требуется найти не только значения переменных Xmax и Xmin, но и положение (индексы) соответствующих им элементов в массиве X (значения переменных imax и imin).
Вариант 1.
#define NMAX 500
int main()
{
int i, // параметр цикла
n, // кол-во элементов массива
imax,imin; // позиции max и min элементов
float Xmax,Xmin; // максимальное и минимальное значение
float x[NMAX]; // исходный массив
//Ввод и печать n, X
Xmax=x[0]; Xmin=x[0];
imax=0; imin=0;
for (i=1;i<n;i++)
{
if (x[i]>Xmax)
{
Xmax=x[i]; imax=i;
}
if (x[i]<Xmin)
{
Xmin=x[i]; imin=i;
}
}
x[imax]=Xmin; x[imin]=Xmax;
//Печать x
getch();
return 0;
}
В
каждом цикле элемент
сравнивается со значениями переменных
Xmax
и Xmin.
В то же время очевидно, что если выполняется
условие
> Xmax,
то проверка отношения
< Xmin
является
излишней. Указанное замечание учтено
в варианте 2.
Вариант 2 (фрагмент).
for (i=1;i<n;i++)
{
if (x[i]>Xmax)
{
Xmax=x[i]; imax=i;
}
else
if (x[i]<Xmin)
{
Xmin=x[i]; imin=i;
}
}
При обмене значений элементов x[imax] и x[imin] использовано то обстоятельство, что после окончания обработки массива известны не только значения индексов imax и imin, но и численные значения переменных Xmax и Xmin. Обмен в данном случае осуществляется по такой схеме:
Примечание. Было бы совершенно неправильно выполнять обмен максимального и минимального элементов массива X операторами
R = Xmax; Xmax = Xmin; Xmin = R;
(R - переменная типа float), так как здесь обмениваются значения переменных Xmax и Xmin, а не значения элементов массива X с индексами imax и imin.
Пример 6. В массиве X найти значение и положение первого отрицательного элемента.
Решение рассматриваемой задачи выполнено в двух вариантах.
Вариант 1.
#define NMAX 500
int main()
{
int i, // параметр цикла
n, // кол-во элементов массива
ineg=-1; // положение первого отрицательного элемента
float Xneg; // значение первого отрицательного элемента
float x[NMAX]; // исходный массив
//Ввод и печать n, X
i=0;
while(i<n && x[i] >= 0)
i++;
if(i==n)
printf("\nОтрицательных элементов нет\n");
else
{
ineg=i; Xneg=x[i];
//Печать ineg, Xneg
}
getch();
return 0;
}
В программе последовательно просматриваются элементы массива X и, как только обнаруживается отрицательный элемент или заканчивается просмотр массива, цикл прерывается. Если в массиве X нет ни одного отрицательного элемента (признаком будет выполнение условия i==n), в программе будет отпечатано соответствующее сообщение.
Если i<n, значит цикл был прерван по нарушению условия x[i] >= 0. Отрицательный элемент найден, в переменных Xneg, ineg запоминаются значение и индекс этого элемента, после чего они распечатываются.
Вариант 2 (фрагмент).
//Ввод и печать n, X
for(i=0; i<n && x[i]>=0;i++);
if(i==n)
printf("\nОтрицательных элементов нет\n");
else
{
ineg=i; Xneg=x[i];
//Печать ineg, Xneg
}
Вместо цикла while можно использовать цикл for .
В таком применении цикл for не использует тело цикла. Алгоритм реализуется в заголовке цикла.
Пример 7. В массиве X определить местоположение последнего положительного элемента.
Вариант 1.
#define NMAX 500
int main()
{
int i, // параметр цикла
n, // кол-во элементов массива
ipos=-1;// положение последнего положительного элемента
float x[NMAX]; // исходный массив
//Ввод и печать n, X
i=0;
while( i < n )
if (x[i] > 0) ipos=i;
if(ipos==-1)
printf("\nПоложительных элементов нет\n");
else
//Печать ipos, X[ipos]
getch();
return 0;
}
Переменная ipos используется двояко:
- для запоминания позиции найденного положительного элемента. Так как просматривается весь массив, то в переменной запишется позиция последнего найденного положительного элемента. Это и будет искомый элемент;
- как флаг. Если по окончании цикла в ней будет значение -1, значит, не было найдено ни одного положительного элемента.
В программе просматриваются все элементы массива X, что нерационально с точки зрения затрат машинного времени. Более эффективной является программа варианта 2, в которой просмотр массива X осуществляется справа налево до обнаружения ближайшего положительного элемента.
Вариант 2 (фрагмент).
//Ввод и печать n, X
for(i=n-1; i>=0 && x[i]<=0;i--);
if(i==-1)
printf("\nПоложительных элементов нет\n");
else
{
ipos=i;
//Печать ipos, X[ipos]
}
Пример 8. Дано целое десятичное число А. Сформировать массив двоичных цифр числа. Выполнить сложение с 1. Из массива цифр сформировать преобразованное число А1.
Введенное десятичное число необходимо проверить на диапазон допустимых значений:
1. А должно быть неотрицательным. Отрицательные числа представляются в дополнительном коде, а, значит, необходимо по другому алгоритму формировать массив двоичных цифр и иначе учесть это при добавлении 1.
2. А не должно быть равно максимальному числу, записанному в формате int, в противном случае добавление 1 вызовет переполнение разрядной сетки, результат будет неверным. Для проверки можно использовать предопределенную константу INT_MAX - максимальной целое положительное число в формате int.
Для формирования массива двоичных цифр используется алгоритм получения двоичных цифр числа. Первой выделяется цифра младшего 0-го разряда, затем 1-го и т.д. Цифры записываются в массив b[i++]=a1%2 – сначала в i-й элемент массива запишется двоичная цифра, после чего переменная i увеличится на 1.
Выполнение сложения двух двоичных чисел a и b выполняется поразрядно: si=ai+bi+pi-1. В нем участвуют текущие разряды чисел и перенос из младшего разряда. В нашем случае числа b нет. Для прибавления единицы в младший разряд перед началом цикла перенос из несуществующего младшего разряда устанавливается в 1. Вычисления проводятся по формуле ai=ai+pi-1.
Сумма в текущем разряде может принимать значения 0, 1, 2. Цифры 2 в 2-ой системе счисления нет, поэтому устанавливается перенос в старший разряд, а в текущий разряд записывается 0. При значении суммы 0 или 1 – перенос в старший разряд равен 0 – цикл можно прерывать.
Десятичное число формируется из массива двоичных цифр по схеме Горнера.
int main()
{
int a,a1,digit,i,n,buf,p;
char b[32];
//Ввод и контроль a>0
a1=a;//Для сохранения исходных данных
i=0;
do //Цикл формирования массива двоичных цифр целого числа
{
b[i++]=a1%2;
a1=a1/2;
}
while(a1 != 0);
n=i;//n - размер массива b и одновременно количество цифр в двоичном числе
//Выполним сложение с 1.
p=1; i=0;
while(i<n && p==1)
{b[i]=b[i]+p;
if(b[i]==2)
b[i]=0; //Перенос был - значение p не изменяется
else
p=0; //Переноса не было - p устанавливается в 0
i++;
}
// если был перенос из старшего разряда, то добавляется 1 разряд и в него записывается 1
if(p==1)
{b[n]=1;n++;}
//В поле a1 сформируем преобразованное число по схеме Горнера
a1=0;
for(i=n-1; i>=0; i--)
a1=a1*2+b[i];
//Печать а1
getch();
return 0;
}
Формировать число можно и с использованием весовых коэффициентов. Но такой способ требует больших затрат времени на его выполнение
a1=0; p=1;
for(i=0; i<n; i++)
{ a1+=b[i]*p; p*=2; }
Пример
9.
Расположить элементы массива
в обратном порядке.
При
выполнении заданного преобразования
нужно произвести обмен значений элементов
и
,
и
,
и
и так далее до достижения "середины"
массива
.
Вариант 1.
#define NMAX 1000
int main()
{
double x[NMAX];
int n,i,R;
//Ввод n и массива x
for (i=0;i<n/2;i++)
{
R=x[i]; x[i]=x[n-1-i]; x[n-1-i]=R;
}
//Печать массива x
getch();
return 0;
}
Вариант 2 (фрагмент).
i=0; j=n-1;
while (i<j)
{
R=x[i]; x[i]=x[j]; x[j]=R;
i++; j--;
}
Вариант 3 (фрагмент).
for (i=0,j=n-1; i<j; i++,j--)
{
R=x[i]; x[i]=x[j]; x[j]=R;
}
Все три варианта примера 9 равнозначны с точки зрения эффективности вычислений, но варианты 2 и 3 более наглядны. Они более удобны также в случае, когда требуется выполнить перестановку не всего массива, а подмассива, например, с номера n1 до номера n2. Тогда записываются операторы i=n1 и j=n2, остальная часть программы не изменяется.
В третьем варианте применяется операция последовательного вычисления «запятая». Так как в выражениях заголовка цикла for может стоять только один оператор, а есть необходимость использовать в данном примере 2 оператора, это реализуется операцией «запятая». Таким образом, в заголовке цикла for можно инициализировать и изменять любое количество переменных.
Пример 10. Дано целое десятичное число А. Определить, сколько раз каждая десятичная цифра встречается в числе А.
Наиболее эффективный способ решения этой задачи заключается в использовании массива счетчиков. Определяется массив из десяти целочисленных элементов (десяти счетчиков). Индексы элементов изменяются от 0 до 9. Каждый элемент соответствует своей цифре. При выделении очередной цифры, к соответствующему счетчику прибавляется 1. Выделенная цифра является индексом при обращении к массиву. Например, если очередная выделенная цифра равна 4, то 4-й элемент массива наращивается на 1. Таким образом, наиболее просто определить, какая цифра сколько раз встречается в заданном числе.
int main()
{ int a,a1,digit,i,n;
char c_dig[10]={0,0,0,0,0,0,0,0,0,0};//Массив счетчиков цифр
//Ввод а
a1=abs(a);
i=0;
do //Цикл формирования массива цифр целого десятичного числа
{
digit=a1%10;
a1=a1/10;
c_dig[digit]++;
}
while(a1 != 0);
//Печать результата из массива счетчиков
printf("\nВхождение десятичных цифр в А\n");
for(i=0; i<10; i++)
printf("Цифра %d входит %d раз \n",i,c_dig[i]);
getch();
return 0;
}
Перед началом цикла формирования массива цифр все его элементы необходимо обнулить. Если массив формируется однократно, его можно проинициализировать при определении, как показано в примере. Если эта операция будет производиться неоднократно, то обнуление массива необходимо выполнять каждый раз перед запуском цикла формирования массива цифр оператором:
for(i=0; i<10; i++) c_dig[i]=0;