Починаючи з даного розділу, розглянемо декілька методів впорядкування елементів масиву, які широко використовуються у практичному програмуванні.
1.12.1 Метод бульбашкового сортування Метод "бульбашкового сортування" ґрунтується на перестановці сусідніх елементів. Для впорядкування елементів масиву здійснюються повторні проходи по масиву. Переміщення елементів масиву здійснюється таким чином: масив переглядається зліва направо, здійснюється порівняння пари сусідніх елементів; якщо елементи в парі розміщені в порядку зростання, вони лишаються без змін, а якщо ні - міняються місцями. В результаті першого проходу найбільше число буде поставлено в кінець масиву. У другому проході такі операції виконуються над елементами з першого до (N-1)-ого, у третьому - від першого до (N-2)-ого і т.д. Впорядкування масиву буде закінчено, якщо при проході масиву не виконається жодної перестановки елементів масиву. Факт перестановки фіксується за допомогою деякої змінної (у наступному прикладі - is), яка на початку має значення 0 і набуває значення 1 тоді, коли виконається перестановка в якій-небудь парі.
Масив до впорядкування
-1
-40
-75
-22
Перший перегляд масиву
-1
-40
-75
-22
Другий перегляд масиву
-1
-40
-75
-22
Третій перегляд масиву
-40
-1
-75
-22
Четвертий перегляд масиву
-40
-75
-22
-1
П'ятий перегляд масиву
-75
-40
-22
-1
Рис. 1.15. Бульбашкове сортування
const n=10; int a[n], i, c, is; /* … */ do { is=0; for (i=1;i<n;i++) if (a[i-1]>a[i]) { c=a[i]; a[i]=a[i-1]; a[i-1]=c; is=1; } } while (is);
1.12.2 Сортування методом вибору Даний метод сортування передбачає наступні дії: масив переглядається перший раз, знаходиться мінімальний елемент цього масиву, який міняється місцями з першим елементом. Другий раз масив переглядається, починаючи з другого елементу. Знову знаходиться мінімальний елемент, який міняється місцями з другим елементом масиву. Даний процес виконується до тих пір, поки не буде поставлено на місце N-1 елемент.
Масив до впорядкування
-1
-40
-75
-22
Перший перегляд масиву
-75
-1
-40
-22
Другий перегляд масиву
-75
-40
-1
-22
Третій перегляд масиву
-75
-40
-22
-1
Четвертий перегляд масиву
-75
-40
-22
-1
П'ятий перегляд масиву
-75
-40
-22
-1
Шостий перегляд масиву
-75
-40
-22
-1
Рис. 1.16. Сортування методом вибору
const int n=20; int b[n]; int imin, i, j, a; /* … */ for (i=0;i<n-1;i++) { imin=i; for (j=i+1;j<n;j++) if (b[j]<b[imin]) imin=j; a=b[i]; b[i]=b[imin]; b[imin]=a; }
1.12.3 Сортування вставками Даний метод сортування називається сортування вставками, так як на і-му етапі відбувається "вставка" і-ого елемента a[i] в потрібну позицію серед елементів a[1], a[2], …, a[i-1], які вже впорядковані. Після цієї вставки перші і елементів будуть впорядковані. Саме таким способом звичайно сортують карти, тримаючи в лівій руці вже впорядковані карти, і взявши правою рукою чергову карту вставляють її в потрібне місце, порівнюючи її з іншими проходячи справа наліво.
Масив до впорядкування
-1
-40
-75
-22
Перший перегляд масиву
-1
-40
-75
-22
Другий перегляд масиву
-1
-40
-75
-22
Третій перегляд масиву
-40
-1
-75
-22
Четвертий перегляд масиву
-40
-1
-75
-22
П'ятий перегляд масиву
-75
-40
-1
-22
Шостий перегляд масиву
-75
-40
-22
-1
Рис. 1.17. Сортування вставками
Реалізувати сортування масиву вставками можна так:
const int n=20; int b[n]; int i,j,c; /* … */ for (i=1;i<n;i++) { c=a[i]; for (j=i-1;j>=0&&a[j]>c;j--) a[j+1]=a[j]; a[j+1]=c; }
1.12.4 Швидке сортування Швидке сортування полягає в тому, що множина елементів В { k1, k2, …, kn } перетворюється на множину B1, {k1}, B2, де В1 - підмножина В з елементами, не більшими за k1, а В2 - підмножина В з елементами більшими k1. Причому елемент k1 після розбиття множини В буде перебувати на потрібному місці. Далі до множин B1 і B2 знову застосовують впорядкування швидким сортуванням. Час роботи алгоритму швидкого сортування в гіршому випадку складає О(n2), але на практиці цей алгоритм виявляється одним із найшвидших.
1.13.1 Оголошення структури Структури дозволяють об'єднувати в єдиному об'єкті сукупність значень, які можуть мати різні типи. Оголошення структури здійснюється за допомогою ключового слова struct. Синтаксис опису структури виглядає так: struct [ім'я_структури] { тип1 елемент1; тип2 елемент2; ........................ типN елементN; } [список описів]; З метою ознайомлення з цим типом даних розглянемо найпростіший приклад представлення поняття "дата", що складається з декількох частин: число (день, місяць, рік), назва тижня та місяця: struct date { int day; int month; int year; char day_name[15]; char mon_name[14]; } arr[100],*pd,data,new_data; В даному прикладі оголошуються: data, new_data - змінні типу структури date; pd - покажчик на тип data arr - масив із 100 елементів, кожний елемент якого має тип date. Можливий і наступний опис структури з використанням typedef: typedef struct mystruct { int year; char size; float field; } MYSTRUCT; MYSTRUCT s; /* те саме, що й struct mystruct s; */ Пам'ять розподіляється у структурі покомпонентно, зліва-направо, від молодших до старших адрес пам'яті (рис. 1.18). typedef struct dataTypes { float aFloat; int anInt; char aString[8]; char aChar; char aLong; } DataTypes; DataTypes data;
Потрібно відзначити, що на відміну від описів інших типів даних, опис структури не виділяє місця у пам'яті під елементи структури. Її опис визначає лише так званий шаблон, що описує характеристики змінних, що будуть розміщуватися у конкретній структурі. Щоб ввести змінні та зарезервувати для них пам'ять необхідно або після фігурної дужки, що завершує опис структури, вказати список ідентифікаторів, як це зроблено у вищенаведеному прикладі, або окремо оголосити змінні типу, як ми це робимо у звичайних випадках. Доступ до окремого елемента структури забезпечується операторами вибору:. (прямий селектор) та -> (непрямий селектор), наприклад, struct mystruct { int i; char str[21]; double d; } s,*sptr=&s; s.i =3; sptr->d = 1.23; Ініціалізація структури подібна до тієї, що у масивах, але з урахуванням розміщення даних різного типу. struct person { char frnm[20]; char nm[30]; int year; char s; }; struct person poet={"Taras", "Shevtchenko",1814, 'M'},classics[]={{"Alfred", "Aho", 1939, 'M'},{"Seimour", "Ginzburg",}, /* … */ {"Jeffrey", "Ulman", 1938, 'M'}}; У вищенаведеному прикладі ініціалізується змінна poet і масив структур classics. Значення classics[1].year і classics[1].s мають значення відповідно 0 і '\0'. Для змінних одного і того ж самого структурного типу визначена операція присвоювання, при цьому здійснюється поелементне копіювання значень полів. struct date { int day; int month; int year; char day_name[15]; char mon_name[14]; } data,new_data; /*... */ data=new_data; Але, для порівняння структур необхідно перевіряти рівність відповідних полів цих структур. struct point { float x,y; char c; } point1,point2; if (point1.x==point2.x&&point1.y==point2.y&&point1.c==point2.c) { /* … */ }; Звертання до окремих елементів структури теж не викликає труднощів: data.year=2005; printf("%d-%d-%d",data.day,data.month,data.year); scanf("%d",data.day); gets(arr[0].day_name); Доцільним та корисним є зв'язок структур та покажчиків, який дозволяє обійти деякі складні моменти. Так опис date *pdate утворить покажчик на структуру типу date. Використовуючи цей покажчик, можна звернутися до будь-якого елемента структури шляхом застосування операції ->, тобто date ->year, або що еквівалентно операції (*pdate).year. Однак слід зауважити, що спільне використання цих типів потребує від програміста достатньо високої кваліфікації, аби використовувати можливості найбільш ефективно та безпомилково. Приклад 1. #include<stdio.h> #include<conio.h> #define MAXTIT 41 #define MAXAUT 31 struct book { char title[MAXTIT]; char author[MAXAUT]; float value; }; void main() { struct book libry; printf("Введiть назву книги.\n"); gets(libry.title); printf("Тепер введiть прiзвище автора.\n"); gets(libry.author); printf("Тепер введiть цiну.\n"); scanf("%f",&libry.value); printf("\n%s '%s',%g grn.\n",libry.author, libry.title,libry.value); }; Кожний опис структури вводить унікальний тип структури, тому в наступному фрагменті програми: struct A { int i,j; double d; } a, a1; struct B { int i,j; double d; } b; об'єкти a і a1 мають однаковий тип struct A, але об'єкти a і b мають різні типи структури. Структурам можна виконувати присвоювання тільки в тому випадку якщо і вихідна структура, і структура, які присвоюється мають один і той же тип. a = a1; /*можна виконати, так як a і a1 мають однаковий тип */ a = b; /* помилка */
1.13.2 Масиви структур Як і звичайними масивами простих типів, так само можна оперувати масивами структур, елементи якого мають структурований тип. Розглянемо наочний зразок, який ілюструє оголошення масиву структур: typedef struct Date { int d; /* день */ int m; /* мiсяць */ int y; /* рiк */ } Date; Date arr[100]; Вище було оголошено масив arr, що складається із 100 елементів, кожний з яких має тип Data. Кожний елемент масиву - це окрема змінна типу Data, що складається із трьох цілих елементів - d, m, y. Доступ до полів структури аналогічний доступу до звичайних змінних, плюс використання індексу номеру елементу у квадратних дужках: arr[25].d=24; arr[12].m=12; Запропонуємо програму, в якій реалізується концепція структурованого типу Data. Окремими функціями реалізуємо ініціалізацію елементів структури, додавання нового значення, виведення дати на екран, визначення високосного року. #include<stdio.h> #include<conio.h> typedef struct Date { int d; /* день */ int m; /* мiсяць */ int y; /* рiк */ } Date; void set_date_arr(Date *arr,Date value,int n) { int i; for (i=0;i<n;i++) { arr[i].d=value.d; arr[i].m=value.m; arr[i].y=value.y; } }
void print_date_arr(Date *arr,int n) { int i; for (i=0;i<n;i++) { printf("%d.%d.%d\n",arr[i].d,arr[i].m,arr[i].y); } }
void print_date(Date &d) /* виведення на екран дати */ { printf("%d.%d.%d\n",d.d,d.m,d.y); }
void init_date(Date &d,int dd,int mm,int yy) /* iнiцiалiзацiя структури типу Date */ { d.d=dd; d.m=mm; d.y=yy; }
int leapyear(int yy) /* визначення, чи високосний рiк */ { if ((yy%4==0&&yy%100!=0)||(yy%400==0)) return 1; else return 0; }
void add_year(Date &d,int yy) /* додати yy рокiв до дати */ { d.y+=yy; }
void add_month(Date &d,int mm) /* додати mm мiсяцiв до дати */ { d.m+=mm; if (d.m>12) { d.y+=d.m/12; d.m=d.m%12; } }
void add_day(Date &d,int dd) /* додати dd днiв до дати */ { int days[]={31,28,31,30,31,30,31,31,30,31,30,31}; d.d+=dd; if (leapyear(d.y)) days[1]=29; while ((d.d>days[d.m-1])) { if (leapyear(d.y)) days[1]=29; else days[1]=28; d.d-=days[d.m-1]; d.m++; if (d.m>12) { d.y+=d.m%12; d.m=d.m/12; } } }
void main(void) { Date date1,date2; Date array[10]={{12,11,1980},{15,1,1982},{8,6,1985},{8,8,1993},{20,12,2002},{10,1,2003}}; clrscr(); init_date(date1,15,12,2002); add_day(date1,16); print_date(date1); puts(""); init_date(date2,1,1,2003); add_month(date2,10); print_date(date2); puts(""); print_date_arr(array,6); }
1.13.3 Бітові поля Бітові поля (bit fields) - особливий вид полів структури. Вони дають можливість задавати кількість бітів, в яких зберігаються елементи цілих типів. Бітові поля дозволяють раціонально використовувати пам'ять за допомогою зберігання даних в мінімально потрібній кількості бітів. При оголошенні бітового поля вслід за типом елемента ставиться двокрапка (:) і вказується цілочисельна константа, яка задає розмір поля (кількість бітів). Розмір поля повинен бути константою в діапазоні між 0 і заданим загальним числом бітів, яке використовується для зберігання даного типу даних. struct bit_field { int bit_1: 1; int bits_2_to_5: 4; int bit_6: 1; int bits_7_to_16: 10; } bit_var;
1.14 Об'єднання (union)
Об'єднання дозволяють в різні моменти часу зберігати в одному об'єкті значення різного типу. В процесі оголошення об'єднання з ним асоціюється набір типів, які можуть зберігатися в даному об'єднанні. В кожний момент часу об'єднання може зберігати значення тільки одного типу з набору. Контроль за тим, значення якого типу зберігається в даний момент в об'єднанні покладається на програміста. Синтаксис: union [ім'я_об'єднання] { тип1 елемент1; тип2 елемент2; ........................ типN елементN; } [список описів]; Пам'ять, яка виділяється під змінну типу об'єднання, визначається розміром найбільш довгого з елементів об'єднання. Всі елементи об'єднання розміщуються в одній і тій же області пам'яті з однієї й тієї ж адреси. Значення поточного елемента об'єднання втрачається, коли іншому елементу об'єднання присвоюється значення. Приклад 1: union sign { int svar; unsigned uvar; } number; Приклад 2: union { char *a,b; float f[20]; } var; В першому прикладі оголошується змінна типу об'єднання з ім'ям number. Список оголошень елементів об'єднання містить дві змінні: svar типу int і uvar типу unsigned. Це об'єднання дозволяє запам'ятати ціле значення в знаковому або в без знаковому вигляді. Тип об'єднання має ім'я sign. В другому прикладі оголошується змінна типу об'єднання з ім'ям var. Список оголошень елементів містить три оголошення: покажчика a на значення типу char, змінної b типу char і масиву f з 20 елементів типу float. Тип об'єднання не має імені. Пам'ять, що виділяється під змінну var, рівна пам'яті, необхідної для зберігання масиву f, так як це найдовший елемент об'єднання.
studopedia.su - Студопедия (2013 - 2026) год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав!Последнее добавление