Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Рацеев С.М. Программирование на языке Си

.pdf
Скачиваний:
555
Добавлен:
23.03.2016
Размер:
1.77 Mб
Скачать

растанию или убыванию), то это можно сделать максимум за одно прохождение по массиву:

int Check(const double *a, const int n)

{

int i;

for (i = 1; i < n && a[i] != a[i – 1]; i++)

;

return i >= n;

}

Массив с достаточно узким диапазоном значений элемен-

тов. Очень часто множество значений элементов массива составляет довольно узкий диапазон. Например, значения элементов массива могут принадлежать множеству {0,1,…,255}. В этом случае можно очень быстро определить, все ли элементы массива попарно различны, используя не более одного прохождения по данному массиву и не требуя при этом условия упорядоченности. Для этого введем массив count, содержащий 256 элементов, индексы которого будут соответствовать значениям элементов массива a. Изначально массив count заполним нулями. Затем при считывании i-го элемента массива a будем увеличивать значение cout[a[i]] на единицу. Например, если a[i] = 10, то увеличим значение count[10] на единицу. Если после очередного увеличения cout[a[i]] его значение превзойдет единицу, то алгоритм на этом можно остановить, так как это означает, что не все элементы попарно различны.

int Check(const int *a, const int n)

{

int i, flag = 1, count[256] = {0}; for (i = 0; i < n && flag; i++)

{

count[a[i]]++;

if (count[a[i]] > 1) flag = 0;

}

return flag;

61

}

Заметим, что синтаксис языка Си позволяет записывать многие выражения очень компактно. Например, строчки из предыдущего алгоритма

count[a[i]]++;

if (count[a[i]] > 1) flag = 0;

можно заменить на

if (++count[a[i]] > 1) flag = 0;

При этом условие можно поместить в условие цикла. Получаем такую компактную функцию:

int Check(const int *a, const int n)

{

int i, count[256] = {0};

for (i = 0; i < n && ++count[a[i]] <= 1; i++)

;

return i >= n;

}

Пример 3. Требуется определить количество различных элементов массива. В данной задаче используем тот же принцип, что и в предыдущем алгоритме.

Произвольный случай.

int Count(const double *a, const int n)

{

int i, j, count = 0; for(i = 0; i < n; i++)

{

for(j = 0; j < i && a[i] != a[j]; j++)

62

;

count += (i == j);

}

return count;

}

Упорядоченный массив. Если требуется выполнить ту же задачу для упорядоченного (по возрастанию или убыванию) массива, то это можно сделать за одно прохождение по массиву:

int Count (const double *a, const int n)

{

int k, /* количество различных элементов */ i;

k = 1;

for (i = 1; i < n; i++) if (a[i] != a[i – 1])

k++; return k;

}

Массив с достаточно узким диапазоном значений элемен-

тов. Как и в предыдущем примере, рассмотрим случай, когда множество значений элементов массива составляет довольно узкий диапазон. Пусть, например, значения элементов массива принадлежат множеству {0,1,…,255}. Опять же в этом случае можно очень быстро подсчитать количество различных элементов, не требуя при этом условия упорядоченности. Для этого определим массив count, содержащий 256 элементов, индексы которого будут соответствовать значениям элементов массива a. На начальном этапе инициализируем элементы массива count нулями. Затем при считывании i-го элемента массива a будем увеличивать значение cout[a[i]] на единицу. После прохождения по всему массиву a количество различных элементов массива a будет равно количеству ненулевых элементов массива count.

int Count(const int *a, const int n)

63

{

int k, i, count[256] = {0}; for (i = 0; i < n; i++)

count[a[i]]++;

for (i = 0; i < 256; i++) if (count[i] != 0)

k++; return k;

}

Пример 4. Пусть имеется некоторый массив действительных чисел a и некоторое число x. Требуется переставить элементы массива так, чтобы сначала следовали (в произвольном порядке) элементы, меньшие числа x, а затем элементы, не меньшие числа x. Использовать не более одного прохода по массиву.

Будем просматривать элементы массива слева направо, пока не встретим элемент a[i]>=x. Затем будем просматривать элементы массива справа налево, пока не встретим элемент a[j]<x. После этого меняем элементы a[i] и a[j] местами и продолжаем данный процесс.

void Exchange(double *a, const int n, const double x)

{

int i = 0, j = n – 1; double buf;

while (i < j)

{

while (i < j && a[i] < x) i++;

while (i < j && a[j] >= x) j--;

if (i < j)

{

/* меняем местами a[i] и a[j] */ buf = a[i]; a[i] = a[j]; a[j] = buf; i++; j--;

}

}

64

}

Например, если требуется переставить элементы массива так, чтобы сначала следовали отрицательные элементы, а затем неотрицательные, то задача очень просто решается с помощью функции Exchange(), где в качестве второго параметра нужно передать число 0.

Немного усложним задачу. Пусть требуется переставить элементы массива так, чтобы сначала следовали (в произвольном порядке) элементы, меньшие числа x, затем элементы, равные x, а затем элементы, большие числа x. При этом по массиву придется пройтись не более двух раз и алгоритм будет выглядеть следующим образом:

void Exchange(double *a, const int n, const double x)

{

int i = 0, j = n – 1; double buf;

while (i < j)

{

while (i < j && a[i] < x) i++;

while (i < j && a[j] >= x) j--;

if (i < j)

{

buf = a[i]; a[i] = a[j]; a[j] = buf; i++; j--;

}

}

j = n – 1; while (i < j)

{

while (i < j && a[i] == x) i++;

while (i < j && a[j] != x) j--;

65

if (i < j)

{

a[j] = a[i]; a[i] = x; i++; j--;

}

}

}

Пример 5. Пусть имеется n-элементный целочисленный массив a, который является перестановкой чисел 0, 1,…, n-1. Требуется в n-элементный целочисленный массив b записать обратную перестановку к перестановке a.

/* генерирование обратной перестановки b */

void ObratnayaPerest(unsigned int *a, unsigned int *b, int n)

{

int i;

for (i = 0; i < n; i++) b[a[i]] = i;

}

Пример 6. Требуется переставить цифры в натуральном числе x так, чтобы полученное число имело максимальное значение.

unsigned long Max(unsigned long x)

{

int count[10] = {0}, i, j; do {

count[x%10]++; x /= 10;

}while(x);

for (i = 9; i >= 0; i--)

for (j = 0; j < count[i]; j++) x = x*10 + i;

return x;

}

66

Пример 7. Пусть B – некоторое фиксированное целое число, причем 0≤B≤1000, и M – некоторое подмножество в {0,1,2,…,B-1}. Пусть также имеется некоторый целочисленный массив a размера n. Требуется подсчитать количество элементов массива a, принадлежащих множеству M.

Элементы множества M запишем в массив set размера m, где m – мощность множества M. Пусть, для примера, B=1000, M={1,10,100,500}. Поэтому в данном случае int set[4] = {1,10,100,500}.

Для эффективной проверки на принадлежность элементов массива a множеству M рассмотрим следующий алгоритм. Пусть flag[B] – массив, состоящий из нулей и единиц, каждый индекс которого будет соответствовать элементам множества M: если элемент i, 0≤i<B, принадлежит множеству M, то определим flag[i]=1, иначе, flag[i]=0. Теперь проверить на принадлежность произвольного элемента a[i] множеству M можно очень просто: if (a[i] >= 0 && a[i] < B && flag[a[i]]).

#define B 1000

#define N 100 /* размерность массива a */

/* вычисление количества элементов массива a, принадлежащих M: */

int Count (int *a, int n, int *set, int m)

{

int flag[B] = {0}; int i, count;

for (i = 0; i < m; i++) flag[set[i]] = 1;

count = 0;

for (i = 0; i < n; i++)

if (a[i] >= 0 && a[i] < B && flag[a[i]]) count++;

return count;

}

int main()

67

{

int set[4] = {1, 10, 100, 500}; int a[N];

…/* заполняем массив a */

printf(“%d\n”, Count(a, N, set, 4)); return 0;

}

Пример 8. Обобщим предыдущий пример. Пусть A и B – некоторые целые числа, причем -1000≤A≤B≤1000, и M – некоторое подмножество в {A,A+1, A+2,…,B-1}. Пусть также имеется некоторый целочисленный массив a размера n. Требуется подсчитать количество элементов массива a, принадлежащих множеству M. После модернизации алгоритма из предыдущего примера эффективный алгоритм решения данной задачи будет выглядеть следующим образом:

#define A -1000 #define B 1000 #define N 10

int Count (int *a, int n, int *set, int m)

{

int flag[B – A] = {0}; int i, count;

for (i = 0; i < m; i++) flag[set[i] – A] = 1;

count = 0;

for (i = 0; i < n; i++)

if (a[i] >= A && a[i] < B && flag[a[i] – A]) count++;

return count;

}

68

Пример 9. В массиве целых чисел a размера n найти m (mn) различных минимальных элементов. Например, в массиве 10, 20, 10, 30, 40 при m=3 результат должен быть такой: 10, 20, 30.

#include<stdio.h>

#define N 10 /* размер массива */

#define M 5 /*количество искомых минимальных элементов */

/* сдвиг элементов массива a размера n на одну позицию влево с конца массива до позиции i0 */

void Sdvig(int *a, int i0, int n)

{

int i;

for(i = n – 1; i > i0; i--) a[i] = a[i – 1];

}

/* поиск m минимальных значений */ void Min(int *a, int n, int *min, int m)

{

int i, j, max;

for(i = 1, max = a[0]; i < n; i++) if (a[i] > max)

max = a[i];

for (i = 0; i < m; i++) min[i] = max;

for (i = 0; i < n; i++) if (a[i] < min[0])

{

Sdvig(min, 0, m); min[0] = a[i];

}

else

{

j = 1;

while (j < m && !(min[j – 1] < a[i] && a[i] < min[j])) j++;

if (j < m)

69

{

Sdvig(min, j, m); min[j] = a[i];

}

}

}

int main()

{

int i, a[N], /* исходный массив */

min[M]; /* массив из M минимальных элементов */

Min(a, N, min, M); for(i = 0; i < M; i++)

printf(“%d “, min[i]); return 0;

}

Если минимальные элементы могут повторяться, например в массиве 10, 20, 10, 30, 40 при m=3 минимальные элементы будут такими 10, 10, 20, то функцию Min() можно записать в таком виде:

void Min(int *a, int n, int *min, int m)

{

int i, j, max;

for(i = 1, max = a[0]; i < n; i++) if (a[i] > max)

max = a[i];

for (i = 0; i < m; i++) min[i] = max;

for (i = 0; i < n; i++)

{

for (j = 0; j < m && a[i] >= min[j]; j++)

;

if (j < m)

{

Sdvig(min, j, m); min[j] = a[i];

}

70