Файл - 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. Застосування індексів для пошуку у невпорядкованих даних
 Індекси – додаткові допоміжні дані, які створюються для
прискорення пошуку
 Індекси доцільно використовувати у випадках:
 Коли задача реалізації пошуку є пріоритетною
 Коли порядок даних не можна змінювати
 Коли дані розміщені у пам’яті з повільним доступом
 Використання індексів передбачає певну попередню підготовчу
роботу для побудови індексу, а також підтримки його у
актуальному стані, коли поновлюються дані
 Найпоширенішими є індекси на основі різних варіантів дерев,
зокрема, бінарних та Б-дерев