You are on page 1of 8

Обробка масивів

Масиви, як цілісні структури, можуть брати участь тільки в деяких операціях. Зазвичай, це
операції "дорівнює" / "не дорівнює". Деякі мови підтримують для змінних-масивів операції
присвоєння, коли однією операцією усім елементам масиву присвоюються значення
відповідних елементів іншого масиву. У цьому випадку відповідні масиви повинні бути
ідентичними за структурою (мати однакові типи індексів і типи компонентів). Всі інші дії
виконуються тільки з елементами масивів відповідно до їх типу.
Типовим способом доступу до елементів масиву у мовах програмування є звертання за
індексами. РБНФ-нотація звертання до елемента масиву за індексом у С/С++ має вид:
елемент_масиву = ім’я”[“ індекс ”]” { ”[ “індекс”]”}.
Як індекси можуть використовуватися довільні вирази, що включають цілочисельні
константи і змінні допустимих порядкових типів. Наприклад,
arr[4], d[4] [5], а[і], m[і+5] [2].
УС/С++доступ до елементів масиву може здійснюватися не тільки за індексом, а й за
покажчиком (варіант зпокажчиками в загальному випадку працює швидше). Для звертання до
елементів масиву за покажчиком, треба оголосити відповідний вказівникі ініціалізувати його
адресою першого елемента масиву. Наприклад,
int m[3], *p;
p = &m[0]; // або p = m.
Щоб звернутися до будь-якого елементу масиву, вказівник має одержати приріст, кратний
розміру елементів масиву. Для цього використовується адресна арифметика.
Операції адресної арифметики:
 Інкрементування / декрементування вказівників (“++“ / “--“). Значеннявказівника
збільшується (зменшується) на кількість байт, що визначається типом, на який він
вказує. Після виконання відповідної операції покажчик вказуєна наступний(попередній)
елемент масиву.
 Скорочене додавання / віднімання цілого числа (“+=“ / “-=“). Значеннявказівника
збільшується (зменшується) на кількість байт, яка необхідна для розміщення заданого
числа об’єктів, на які посилається покажчик. Після виконання відповідної операції
покажчик зміщується вперед (назад) на задану кількість елементів масиву.
 Додавання / віднімання цілого числа (“+“ / “-“). Задає логічне зміщення вперед (назад) на
кількість байт, яка необхідна для розміщення заданого числа об’єктів, на які
посилається покажчик.
 Обчислення зміщення покажчиків. Якщо є два вказівники на різні елементи одного
масиву, то їх можна відняти один від одного і з’ясувати, на якій відстані один від
одного знаходяться елементи масиву, на які вказують відповідні покажчики.
Наприклад,
float m[7], n,*р1,*р2;
р1 = &m[0];
р2 = m;
р1 += 5; // зміщення вперед на 4 об’єкта (дійсних числа)
р1 --; // перехід до попереднього елемента масиву
р2 ++; // перехід до наступного елемента масиву
cout<<*(р2+3)<<“\n”; // покажчик-зміщення вперед на 5 об’єктів (дійсних чисел)
cout<<*(р1-2)<<“\n”; // покажчик-зміщення назад на 3 об’єкта (дійсних числа)
р1 -= 1; // зміщення назад на 1 об’єкт (дійсне число)
n = р1 - р2; // зміщення між елементами, на які вказують mPtr1 і mPtr2
Таким чином у С/С++ при роботі з елементами масиву мають місце дві інтерпретації
покажчика, розрізнити які в тексті програми можна тільки в контексті використання
покажчика:
1. покажчик як посилання на окрему змінну(цій традиційній інтерпретації відповідає
операція непрямого звернення за вказівником*p);
2. покажчик на пам'ятьз відносною адресацією від поточного положення (підтримується
операціями адресної арифметики).
Покажчик на масив можна індексувати точно так само, як і масив: при цьому компілятор
перетворює індексацію в арифметику покажчиків. Наприклад, звернення до елемента масиву
m[3] перетворюється в *(m +3). Тому будь-яке з присвоювань
*m = 2.4;
m [0] = 2.4;
*(m+0) = 2.4;
*р1 = 2.4;
p1[0] = 2.4;
*(р1+0) = 2.4;
виконує одну й ту ж дію: присвоює початковому елементу масива m значення 2.4.
Слід зазначити, що між ім'ям масиву і покажчиком на масив існує одна відмінність: покажчик
- це змінна, тому можна написати рtr1 = m або рtr1++; але ім'я масиву не є змінною, і записи
на зразок m = рtr1 або m ++ не допускаються.
Оскільки багатовимірні масиви в мові C/С++ - це масиви масивів, тобто масиви, елементами
яких, у свою чергу, є масиви, то при оголошенні таких масивів в оперативній пам'яті
створюється кілька різних об'єктів (виділяється пам'ять під них):
 покажчик на двовимірний масив (його ім'я співпадає з іменем масиву);
 масив покажчиків, кожен із яких містить адресу масиву-рядка вихідного двовимірного
масиву;
 двовимірний масив даних базового типу.
Наприклад, при виконанні оголошення двовимірного масиву int arr[4][3] в програмі
створюється покажчик arr, який визначає в пам'яті місце розташування першого елемента
масиву і, крім того, є покажчиком на масив з чотирьох покажчиків, кожен з яких містить
адресу одновимірного масиву, що представляє собою рядок вихідного двовимірного масиву і
складається з трьох елементів типу int (рис. 1).
arr


arr [0]  arr [0] [0] arr [0] [1] arr [0] [2]

arr [1]  arr [1] [0] arr [1] [1] arr [1] [2]

arr [2]  arr [2] [0] arr [2] [1] arr [2] [2]

arr [3]  arr [3] [0] arr [3] [1] arr [3] [2]
Рис. 1 Розподіл пам'яті для двовимірного масиву
Доступ до елементів масиву покажчиків здійснюється у формі arr[2] або *(arr+2), до елементів
двовимірного масиву чисел типу int - у формі arr[1][2] або еквівалентних їй *(*(arr +1) +2) і
(*(arr +1))[2] . Слід враховувати, що з точки зору синтаксису мови С/С++ покажчик arr і
покажчики arr[0], arr[1], arr[2], arr[3] є константами і їх значення не можна змінювати під час
виконання програми.
Розміщення тривимірного масиву відбувається аналогічно. Так, наприклад, оголошення float
mas[3][4][5] породжує в програмі, окрім самого тривимірного масиву з 60 чисел типу float,
масив з чотирьох покажчиків на тип float, масив з трьох покажчиків на масив покажчиків на
float і покажчик на масив масивів покажчиків на float.
Оскільки елементи багатовимірних масивів розташовуються в пам'яті підряд по рядках, то
такий порядок дає можливість звертатися до будь-якого елементу багатовимірного масиву,
використовуючи адресу його початкового елемента і тільки один індексний вираз. Наприклад,
звернення до елементу arr[1][2] можна здійснити за допомогою покажчика ptr, оголошеного у
формі int *ptr = arr[0], як звернення ptr[5].

Методи пошуку у масивах

Пошук – це процес знаходження серед елементів даного типу елемента з заданими


властивостями. Задане значення критерію пошуку називається ключем пошуку. Це може
бути умова рівності елемента заданій величині або
інша умова. При подальшому розгляді методів пошуку будемо вважати, що кількість
елементів даного типу, в якій провадиться пошук – відома.
Прямий лінійний пошук

Найпростішим, але не самим оптимальним методом пошуку є прямий лінійний


пошук. Цей метод використовується тоді, коли немає ніякої додаткової інформації про
групу елементів серед якої провадиться пошук.
Метод полягає в послідовному перегляді всіх елементів і перевірці їх на
відповідність ключу пошуку. Умовою закінчення пошуку може бути або факт
знаходження даного елемента, або той факт, що дану сукупність елементів перевірено
повністю і не знайдено елементів, що відповідають критерію пошуку. Розглянемо
приклад:
#include <iostream.h> #include <conio.h> #include <stdlib.h>
//Опис параметризованої функції
template <class T> int search(T* mas,T search_key,int size);

void main ()
{
//Опис змінних int n,i,key;
int* massive; float* massive1; float key1;
//Очищення екрану clrscr();
//Запит на введення розміру масивів cout << "Input n=" ;
cin >> n;
//Динамічне виділення пам’яті під масив цілих чисел massive= new int[n];
// Динамічне виділення пам’яті під масив дійсних чисел massive1= new float[n];
//Активація генератора випадкових чисел randomize();
//Заповнення масивів випадковими числами for(i=0;i<n;i++)
{
massive[i]=random(50)-25; massive1[i]=massive[i]/2.0;
}
//Відображення на екрані масиву цілих чисел cout << "Massive of integer numbers:"<<endl;
for(i=0;i<n;i++)
{
cout.width(7); cout << massive[i];
}
cout <<endl;
//Запит на введення ключа для пошуку у масиві цілих чисел cout << "Input integer number
key=";
cin >> key;
//Пошук у масиві цілих чисел та виведення результату на екран search(massive, key, n);
//Відображення на екрані масиву дійсних чисел cout << "Massive of float numbers:"<<endl;
for(i=0;i<n;i++)
{
cout << massive1[i] << " ";
}
cout <<endl;
//Запит на введення ключа для пошуку у масиві дійсних чисел cout << "Input float number
key1=";
cin >> key1;
//Пошук у масиві дійсних чисел та виведення результату на екран search(massive1, key1, n);
//Очищення пам’яті, виділеної під масиви delete massive;
delete massive1; return;
}

//Параметризована функція прямого пошуку


template <class T> int search(T* mas, T search_key, int size)
{
int index=0;

for (index=0;index<size;index++)
{
if(search_key==mas[index])
{
cout << "Found element with number n= " << index+1 << endl;
return index;
}

cout << "Element not found"<<endl; return -1;


}

Бінарний пошук

Прямий пошук вимагає великих затрат машинного часу. А чи можна якось


прискорити пошук потрібного елемента? Очевидно, що без додаткової інформації про
задану сукупність елементів, це неможливо. Проте пошук можна зробити значно
ефективнішим, якщо відомо, що задана послідовність елементів є впорядкованою за
критерієм пошуку. Прикладом такої впорядкованої послідовності може бути телефонний
довідник, всі записи якого впорядковано відповідно до абетки.

Основною ідеєю пошуку у такій послідовності є вибір деякого випадкового елемента


і порівняння його з критерієм пошуку. При цьому може виникнути три випадки:

-елемент відповідає критерію пошуку. Тоді шуканий елемент знайдено і пошук можна
завершити;
-елемент має значення більше за величину ключа пошуку. Тоді треба продовжити пошук у
тій частині сукупності де значення менші за значення обраного елемента;
-елемент має значення менше за величину ключа пошуку. Тоді треба продовжити пошук у
тій частині сукупності де значення більші за значення обраного елемента.
При такій організації пошуку критерієм зупинки може бути або факт знаходження
даного елемента, або той факт, що дану сукупність елементів перевірено повністю і не
знайдено елементів, що відповідають критерію пошуку. Найпростішим способом
реалізації такого алгоритму є метод бінарного пошуку (метод поділу навпіл). При
такому методі розглядувану послідовність ділять пополам і порівнюють критерій
пошуку з центральним елементом послідовності. Якщо критерій співпадає, то
елемент знайдено, якщо значення елементу менше за заданий критерій то ділять
пополам ту частину послідовності де значення елементів більше за значення
обраного елемента, якщо ж воно більше, то ділять ту половину де значення
елементів менше за значення обраного елемента. Ці дії виконують доти, доки не буде
знайдено потрібний елемент, або поки у досліджуваній частині сукупності не
залишиться лише один елемен.

Оскільки при цьому кожен раз кількість досліджуваних елементів зменшується


вдвічі, то швидкість пошуку значно зростає порівняно з лінійним пошуком.
Розглянемо приклад:

#include <iostream.h> #include <conio.h> #include <stdlib.h>

template <class T> int b_search(T* mas,T search_key,int size); template <class T> void sort(T*
mas, int size);
void main ()
{
//Опис змінних int n,i,key;
int* massive; float* massive1; float key1;
//Очищення екрану clrscr();
//Запит на введення розміру масивів cout << "Input n=" ;
cin >> n;
//Виділення пам’яті під масиви чисел massive= new int[n];
massive1= new float[n];
//Активація генератора випадкових чисел randomize();
//Формування масивів випадкових чисел for(i=0;i<n;i++)
{
massive[i]=random(50)-25; massive1[i]=massive[i]/2.0;
}
//сортування масиву цілих чисел sort(massive,n);
//Відображення відсортованого масиву на екрані cout << "Massive of integer
numbers:"<<endl; for(i=0;i<n;i++)
{
cout.width(7); cout << massive[i];
}
//Запит на введення ключа для пошуку у масиві цілих чисел cout <<endl;
cout << "Input integer number key="; cin >> key;
//Пошук у масиві цілих чисел та відображення на екрані результату пошуку
b_search(massive, key, n);
//Сортування масиву дійсних чисел sort(massive1,n);
//Відображення відсортованого масиву на екрані cout << "Massive of float numbers:"<<endl;
for(i=0;i<n;i++)
{
cout << massive1[i] << " ";
}
cout <<endl;
//Запит на введення ключа для пошуку у масиві дійсних чисел cout << "Input float number
key1=";
cin >> key1;
//Бінарний пошук та відображення на екрані результату пошуку b_search(massive1, key1,
n);
//Вивільнення пам’яті виділеної під масиви delete massive;
delete massive1; return;
}

//Параметризована функція бінарного пошуку


template <class T> int b_search(T* mas, T search_key, int size)
{
int low, high, mid;
low=0; high=size-1;
while(low<=high)
{
mid=(low+high)/2;

if(search_key<mas[mid])
{
high=mid-1;
}
else
{
if(search_key>mas[mid])
{
low=mid+1;
}
else
{
cout << "Found element with number n= " << mid+1 << endl; return mid;
}
}
}
cout << "Element not found" << endl; return -1;
}
//Параметризована функція сортування методом перестановок template <class T> void
sort(T* mas, int size)
{

int i,j,d,tmp;

d=size; for(i=0;i<size-1;i++)
{
for(j=0;j<d-1;j++)
{
if(mas[j]>mas[j+1])
{
tmp=mas[j];
mas[j]=mas[j+1];
mas[j+1]=tmp;
}

You might also like