Файл - KSU Online
Download
Report
Transcript Файл - KSU Online
Тема7 – Алгоритми обробки
масивів
Перелік питань
Перелік питань:
1.
2.
3.
4.
5.
Алгоритми сортування масивів
Вибір методів сортування.
Алгоритми пошуку в упорядкованих масивах .
Вибір методів пошуку в упорядкованих текстових даних.
Застосування індексів для пошуку у невпорядкованих даних.
1. Алгоритми сортування масивів.
Сортування – процес перестановки об’єктів у певному
порядку.
Основне призначення сортування – полегшення пошуку
об’єктів.
Існує велика кількість алгоритмів сортування, одного
«найкращого» до нинішнього часу не винайдено. Вибір
підходящого алгоритму залежить від характеру даних, їх
розподілу, пам’яті, у якій вони зберігаються.
Поширеним є поділ алгоритмів сортування на:
внутрішні – сортування масивів і даних в оперативній пам’яті;
зовнішні – сортування даних у файлах.
За зміною порядку елементів розрізняють:
стійке сортування – при виконанні алгоритму на
відсортованому наборі даних порядок елементів не
змінюється;
нестійке сортування – при виконанні алгоритму на
відсортованому наборі даних порядок елементів змінюється.
Ключ – елемент даних, за яким здійснюється сортування.
Для ключа має використовуватися такий тип даних, для
якого існує спосіб визначення порядку елементів.
Складність виконання алгоритму – кількість операцій, а
також обсяг додаткової пам’яті, необхідної для його
виконання.
Найпростіший алгоритм сортування водночас є
неефективним (нерозумним), його складність О(n3).
Програмна реалізація “нерозумного” алгоритму сортування
using System;
using System.Collections.Generic;
using System.Text;
namespace SortApplication
{
class Program
{
static void swap(int[] arr, int i, int
j)
{
int t = arr[i];
arr[i] = arr[j];
}
arr[j] = t;
}
static void stupidSort(int[] arr)
{
int i = 0;
while (i < arr.Length - 1)
{
if (arr[i + 1] < arr[i])
{
swap(arr, i, i + 1);
i = 0;
}
else
{
i++;
}
}
}
static void Main(string[] args)
{
int[] testArr = {44, 55, 12, 42, 94,
18, 06, 67};
stupidSort(testArr);
foreach (int i in testArr)
{
Console.Write("{0} ", i);
}
Console.ReadLine();
}
}
Алгоритм сортування вставками
Основна ідея алгоритму полягає в тому, щоб поступово
впорядковувати масив, вставляючи нові елементи у вже
впорядковану частину .
Складність алгоритму – О (n2).
Програмна реалізація алгоритму сортування вставками
using System;
using System.Collections.Generic;
using System.Text;
namespace SortingApplication
{
class Program
{
static void insertSort(int[] a)
{
int i, j, value;
for (i = 1; i < a.Length ; i++)
{
value = a[i];
for (j = i - 1; (j >= 0) && (a[j] > value); j--)
{
a[j + 1] = a[j];
}
a[j + 1] = value;
}
}
static void Main(string[] args)
{
int[] testArr = {44, 55, 12, 42, 94, 18, 06, 67};
insertSort(testArr);
foreach (int i in testArr)
{
Console.Write("{0} ", i);
}
Console.ReadLine();
}
}
}
Висновки за алгоритмом сортування вставками
Особливості алгоритму – простий і наглядний в реалізації,
однак ресурсоємний.
Модифікований варіант даного алгоритму – сортування
бінарними вставками оснований на тому, що у вже
відсортованій частині масиву елементи можна включати,
виконуючи бінарний пошук.
Алгоритм бульбашкового сортування
Алгоритм бульбашкового сортування передбачає повторні проходи по масиву,
який сортується. За кожен прохід елементи порівнюються попарно і якщо
порядок серед двох елементів невірний, то здійснюється обмін елементів.
Проходи по масиву виконуються до тих пір, доки не стане зрозуміло, що
обміни елементів вже не потрібні, тобто масив відсортований.
Свою назву алгоритм отримав за принципом роботи – у процесі його
виконання елемент, який стоїть не на власному місці, «спливає» до необхідної
позиції наче бульбашка у воді.
Складність алгоритму – О(n2).
using System;
using System.Collections.Generic;
using System.Text;
static void Main(string[] args)
{
int[] testArr = {44, 55, 12, 42,
94, 18, 06, 67};
bubbleSort(testArr);
foreach (int i in testArr)
{
Console.Write("{0} ", i);
}
Console.ReadLine();
}
namespace SortApplication
{
class Program
{
static void swap(int[] arr, int i, int j)
{
int t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
static void bubbleSort(int[] arr)
{
for (int i = arr.Length - 1; i > 0; i--)
{
for (int j = 0; j < i; j++)
{
if (arr[j] > arr[j + 1])
{
swap(arr, j, j + 1);
}
}
}
}
}
}
Модифікації алгоритму
виявлення проведених обмінів;
шейкер-сортування.
Алгоритм сортування за методом Шелла
Ідея алгоритму – необхідно порівнювати елементи, які знаходяться не тільки
поряд, а і на відстані один від одного. Спочатку порівнюються і сортуються між
собою ключі, які стоять на певній відстані d. Після цього процедура
повторюється для деяких менших значень d, і так до тих пір, доки d не стане
рівним одному, у такому разі робота методу буде ідентична сортуванню
простими вставками.
Складність алгоритму: O(n log n)
Код програми сортування за методом Шелла
using System;
using System.Collections.Generic;
using System.Text;
namespace SortApplication
{
class Program
{
static void shellSort(int[] arr)
{
int j;
int step = arr.Length / 2;
while (step > 0)
{
for (int i = 0; i < (arr.Length - step); i++)
{
j = i;
while ((j >= 0) && (arr[j] > arr[j + step]))
{
int tmp = arr[j];
arr[j] = arr[j + step];
arr[j + step] = tmp;
j--;
}
}
step = step / 2;
}
}
static void Main(string[] args)
{
int[] testArr = {44, 55, 12, 42, 94, 18,
06, 67};
shellSort(testArr);
foreach (int i in testArr)
{
Console.Write("{0} ", i);
}
Console.ReadLine();
}
}
}
Висновки за алгоритмом сортування Шелла
За звичайних обставин кількість порівнянь є меншою, ніж
у алгоритмів сортування вставками та бульбашкового
сортування.
Алгоритм швидкого сортування (Хоара)
Алгоритм швидкого сортування або алгоритм сортування Хоара –
найшвидший серед відомих алгоритмів сортування загального
призначення. Розроблений англійським інформатиком Чарльзом
Хоаром. Швидшими за нього є лише спеціалізовані алгоритми, які
побудовані з урахуванням специфіки даних, для яких вони
використовуються.
Ідея алгоритму основана на принципі «поділяй і володарюй»:
Обираємо деякий елемент, який буде називатися опорним
елементом.
Здійснюємо операцію поділу масиву: реорганізуємо його таким
чином, щоб всі елементи менші чи рівні опорному були зліва від
нього, а всі більші – справа.
Рекурсивно за цим же алгоритмом впорядковуємо списки елементів,
які знаходяться зліва і справа від опорного.
Складність алгоритму: в середньому – О(n log n), найгірший випадок –
О (n2).
Код програми сортування за методом Хоара
using System;
using System.Collections.Generic;
using System.Text;
items[i] = items[j];
items[j] = y;
i++; j--;
}
} while (i <= j);
namespace SortApplication
{
class Quicksort
{
public static void qsort(int[] items)
{
qs(items, 0, items.Length - 1);
}
if (left < j)
{
qs(items, left, j);
}
if (i < right)
{
qs(items, i, right);
}
static void qs(int[] items, int left, int
right)
}
{
}
int i, j;
int x, y;
i = left; j = right;
x = items[(left + right) / 2];
do
{
class Program
{
static void Main(string[] args)
{
int[] testArr = {44, 55, 12, 42, 94, 18,
06, 67};
while ((items[i] < x) && (i < right))
Quicksort.qsort(testArr);
{
foreach (int i in testArr)
i++;
{
}
Console.Write("{0} ", i);
while ((x < items[j]) && (j > left))
}
{
Console.ReadLine();
j--;
}
}
}
}
if (i <= j)
{
y = items[i];
Метод qs
static void qs(int[] items, int left, int right)
{
int i, j;
int x, y;
i = left; j = right;
x = items[(left + right) / 2];
do
{
while ((items[i] < x) && (i < right))
{
i++;
}
while ((x < items[j]) && (j > left))
{
j--;
}
if (i <= j)
{
y = items[i];
items[i] = items[j];
items[j] = y;
i++; j--;
}
} while (i <= j);
if (left < j)
{
qs(items, left, j);
}
if (i < right)
{
qs(items, i, right);
}
}
2. Вибір методів сортування
Вибір підходящого алгоритму сортування залежить від декількох
умов:
Кількість операцій (складність алгоритму)
Доступність пам’яті (у тому числі для реалізації рекурсії)
Вимоги до стійкості алгоритму
Швидкість доступу на запис/читання даних
Характер даних
і т.д.
У загальному випадку – якщо не підходить алгоритм швидкого сортування Хоара,
відомий як найшвидший серед алгоритмів сортування загального призначення,
то слід обрати інший алгоритм, враховуючи існуючі обмеження.
3. Алгоритми пошуку в упорядкованих масивах
Пошук – операція, яка дуже часто здійснюється при роботі з
інформацією
У процесі пошуку (вибірки) необхідно знайти один чи декілька
елементів, які задовольняють певним вимогам (значенню одного чи
декількох ключів)
Під час реалізації пошуку необхідно сприймати до уваги наступне:
для пошуку дуже важливою є швидкість його виконання, алгоритми
пошуку мають масштабуватися відповідно до росту обсягів даних;
в окремих випадках доступ до самих даних у процесі виконання пошуку
може бути ускладнений(наприклад, недопустимо повільний), тому власне
процедура пошуку має здійснюватися у спеціальних допоміжних даних
(індексах);
у багатьох випадках пошук здійснюється частіше, ніж додавання нових
даних, тому у процесі додавання даних можна здійснювати операції, які
полегшують пошук (підтримка індексу);
іноді пошук має здійснюватися за неповним співпаданням з ключем;
іноді результатів пошуку може бути настільки багато, що необхідно
знаходити способи впорядкування результатів за значимістю.
Послідовний пошук за допомогою повного перебору елементів
Послідовний пошук за допомогою повного перебору елементів
– найпростіша реалізація пошуку, однак вона є і
найповільнішою.
В окремих ситуаціях подібна реалізація пошуку є неможливою
чи недоцільною.
Підходить для відносно невеликих обсягів даних, які
зберігаються в оперативній пам’яті
Приклад реалізації послідовного пошуку повним перебором елементів масиву
class Program
{
static void Main(string[] args)
{
InitArray();
Console.WriteLine("Введіть прізвише студента для пошуку");
string NameToFind = Console.ReadLine();
int StudIdx = Find(NameToFind);
if (StudIdx != -1)
{
Console.WriteLine("Студента знайдено: індекс у масиві - {0}, група - {1}",
StudIdx, studArr[StudIdx].Group);
}
else
{
Console.WriteLine("Студента не знайдено");
}
Console.ReadLine();
}
}
Приклад реалізації послідовного пошуку повним перебором елементів (продовж.)
struct Student
{
public string Name;
public string Group;
}
static Student[] studArr;
static void InitArray()
{
studArr = new Student[3];
studArr[0] = new Student() {Name = "Іванов", Group = "ЕК-01"};
studArr[1] = new Student() { Name = "Петров", Group = "ЕК-02" };
studArr[2] = new Student() { Name = "Сидоров", Group = "ЕК-03" };
}
static int Find(string Name)
{
for (int i = 0; i < studArr.Length; i++)
{
if (studArr[i].Name == Name)
{
return i;
}
}
return -1;
}
Пошук шляхом перебору сортованих послідовностей елементів
Послідовний пошук повним перебором елементів у сортованих списках
спрощується за рахунок того, що елементи у них впорядковані, і якщо ми
здійснюємо пошук у списку, відсортованому у порядку зростання елементів, і
зустрічаємо елемент зі значенням ключа більшим, ніж шукаємо, то пошук
можна припинити, оскільки елемент знайти вже не вдасться
Відповідно модифікуємо метод Find попереднього прикладу:
static int Find(string Name)
{
for (int i = 0; i < studArr.Length; i++)
{
if (String.Compare(studArr[i].Name, Name) >= 0)
{
if (studArr[i].Name == Name)
{
return i;
}
else
{
return -1;
}
}
}
return -1;
}
Двійковий пошук
Якщо пошук необхідно здійснювати у відсортованій
послідовності елементів, то значно кращим, ніж послідовний
перебір є використання двійкового пошуку
Двійковий пошук суттєво скорочує кількість операцій, які
необхідно виконати у процесі пошуку, однак можливий лише
для таких структур даних, у яких можливий довільний доступ
(наприклад, масиви), однак неприйнятний до структур даних із
послідовним доступом (наприклад, зв’язані списки)
Приклад реалізації бінарного пошуку
static void Main(string[] args)
{
int[] sortedArr = {1, 4, 7, 9, 9, 12, 13, 17, 19, 21, 24, 32, 36, 44,
45, 54, 55, 63, 66, 70};
Console.WriteLine("Введіть число для пошуку");
int NumToFind = int.Parse(Console.ReadLine());
int NumIdx = BinarySearch(sortedArr, NumToFind);
if (NumIdx != -1)
{
Console.WriteLine("Число знайдено: індекс у масиві - {0}", NumIdx);
}
else
{
Console.WriteLine("Число не знайдено");
}
Console.ReadLine();
}
Приклад реалізації бінарного пошуку (продовж.)
static int BinarySearch(int[] Arr, int Val)
{
int min = 0;
int max = Arr.Length - 1;
while (min <= max)
{
int middle = (max + min) / 2;
if (Val == Arr[middle])
{
return middle; // Число знайдено
}
else
{
if (Val < Arr[middle])
{
max = middle - 1; // Виконуємо бінарний пошук зліва
}
else
{
min = middle + 1; // Виконуємо бінарний пошук зправа
}
}
}
return -1;
}
Інтерполяційний пошук
Якщо елементи у впорядкованій послідовності розподілені відносно
рівномірно (особливо характерно для великих обсягів даних), то пошук можна
істотно пришвидшити завдяки тому, щоб прогнозувати позицію елемента
даних
Прогноз можна зробити, виходячи із значення елемента, кількості елементів у
діапазоні та значень першого і останнього елемента послідовності
Пошук, оснований на такому підході, має назву інтерполяційного, його
можливості і обмеження використання дуже близькі до бінарного, однак на
великих наборах даних він може працювати значно швидше
Особливо помітні переваги інтерполяційного пошуку при роботі з даними,
розміщеними на повільних носіях даних
Приклад реалізації інтерполяційного пошуку
static int InterpolationSearch(int[] Arr, int Val)
{
int min = 0;
int max = Arr.Length - 1;
while (min <= max)
{
if (Arr[min] == Arr[max]) // Запобігаємо ділення на нуль у випадку вичерпання діапазону
{
if (Arr[min] == Val)
{
return min;
}
else
return -1;
}
// Інтерполяційний розрахунок індексу елемента
int target = (int)Math.Round((double)(min + ((Val - Arr[min]) * ((max - min) / (Arr[max] Arr[min])))));
if ((target < min) | (target > max)) // Переконатися, що ми не вийшли за межі діапазону
{
return -1; // Якщо так, то елемент у списку відсутній
}
if (Val == Arr[target]) // Елемент знайдено
{
return target;
}
else if (Val < Arr[target])
{
max = target - 1;
}
else
{
min = target + 1;
}
}
return -1; // Якщо дісталися цього рядку, то елемент у списку відсутній
}
Слідкуючий пошук
В певних випадках необхідно здійснити пошук декількох
елементів у послідовності. Для того, щоб пришвидчити пошук
можна скористатися алгоритмами слідкуючого пошуку (hunt
search), які дозволяють скористуватись даними попереднього
пошуку для здійснення нового пошуку
В найпростішій реалізації слідкуючого пошуку необхідно
порівняти елемент, який шукався останнім, із наступним і
відповідним чином розпочати новий пошук до чи після нього
В залежності від алгоритму, який використовується, слідкуючий
пошук може бути бінарним чи інтерполяційним
4. Вибір методів пошуку у впорядкованих текстових даних
Найпростіший прийнятний у більшості випадків варіант для
текстових даних – бінарний пошук
Використання інтерполяційного пошуку ускладнено у зв’язку з
тим, що у ньому використовуються розрахунки, основані на
значеннях елементів, що напряму з текстовими даними зробити
неможливо.
Проте інтерполяційний пошук можна реалізувати також і з
текстовими даними, однак для цього необхідно використати
спеціальні алгоритми, за допомогою яких текстові рядки можна
закодувати і отримати числа придатні для розрахунків.
Алгоритм кодування має бути таким, щоб отримані числа не
були занадто великими для розрахунків, однак відповідали
порядку сортування текстових даних
5. Застосування індексів для пошуку у невпорядкованих даних
Індекси – додаткові допоміжні дані, які створюються для
прискорення пошуку
Індекси доцільно використовувати у випадках:
Коли задача реалізації пошуку є пріоритетною
Коли порядок даних не можна змінювати
Коли дані розміщені у пам’яті з повільним доступом
Використання індексів передбачає певну попередню підготовчу
роботу для побудови індексу, а також підтримки його у
актуальному стані, коли поновлюються дані
Найпоширенішими є індекси на основі різних варіантів дерев,
зокрема, бінарних та Б-дерев