Розробка уроку

Тема: опрацювання табличних величин мовою C#.

Мета:

Обладнання: комп'ютери зі встановленими ОС та середовищем програму­вання мовою C# MonoDevelop.

Структура уроку

  1. Організаційний момент.
  2. Актуалізація опорних знань.
  3. Інструктаж з ТБ.
  4. Вивчення нового матеріалу.
  5. Закріплення вивченого матеріалу.
  6. Підбиття підсумків уроку.
  7. Домашнє завдання.
Хід уроку

1. Організаційний момент
Привітання з класом, знайомство з класом. Перевірка присутності учнів.

2. Актуалізація опорних знань.
Дати визначення або опис понять і порівняти з очікуваним, перейшовши за посиланням:

3. Інструктаж з ТБ
4. Вивчення нового матеріалу


Необхідність запам’ятовувати, зберігати та використовувати великий набір однотипних даних (величин) привела до створення масивів (таблиць). Наприклад, масиви створюють у випадку потреби зберігати 30 різних імен і щоб для кожного імені не створювати окрему змінну.

Масивзгруповані за місцем розташування у пам'яті величини (всі або змінні, або сталі) одного типу, що мають одну назву (ідентифікатор) і різні порядкові номери (індекси). Це поняття програмування відповідає математичним поняттям послідовності, таблиці (матриці), тензора.

Елемент масивуодна з величин, що утворюють масив. Це поняття програмування відповідає математичному поняттю елемента послідовності чи матриці.

Індекс масивувеличина перелічуваного (зазвичай цілого) типу, яка (сукупність яких) вказує на конкретний елемент масиву. Це поняття програмування відповідає математичному поняттю номера елемента послідовності чи номеру рядка/стовпчика таблиці (матриці).

У C# масиви втілено як об'єкти. Кожний масив має програмно втілену властивість Length — кількість елементів масиву (довжину).

Властивості масиву:

Примітка:

  1. Масиви можна створювати не лише із змінних базових типів, але і з довільних об'єктів.

  2. Назва масиву може бути довільною, але вона має відповідати правилам створення назв (ідентифікаторів).

Cукупність розмірності й діапазонів зміни номерів індексів називають формою масиву.

Звернення до значення елемента масиву у коді програми здійснюють, вказавши після його назви у квадратних дужках індекси у вигляді (арифметичних) виразів, що набувають значень з діапазопу цілочисленного типу (int). Наприклад,
а[3] — 3-тій елемент масиву а,
с[j] — елемент масиву с з індексом j.

Елемент масиву може мати і тип простої (неструктурованої) величини і складений тип (масиву, рядка тощо), тобто може існувати масив масивів, масив рядків тощо. Глибину вкладеності (а значить, і кількість індексів) необмежено, але загальну довжину структури обмежено.

Найпростішими за формою є масиви розмірності 1 (лише один індекс). Такі масиви називають лінійними або одновимірними чи векторами.

Найменше значення індекса вектора дорівнює 0. Найбільше значення індекса лінійного масиву дорівнює довжині масиву, зменшеній на одиницю. У математиці лінійному масиву відповідає поняття послідовності, а індекс масиву — номеру члена послідовності.

Оголошення (опис) масиву

Наприклад,

byte[]  a;
byte[,] b;

Дужки [] вказують лише на те, що змінні розташовано у масиві, але власне масиви ще не існують. Для створення масиву потрібно зарезервувати (виділити) пам'ять за допомогою оператора new.

Загальна форма оператора new має такий вигляд:

Наприклад,

a = new byte[9];

Лише після того, як описано масив і для нього зарезервовано пам'ять, можна звертатись до довільного елемента масиву.

Суміщення оголошення і виділення пам'яті для масиву можна здійснити таким чином:

У C# всі значення масиву ініціалізують — заповнюють комірки пам'яті на початку виконання програми — певними значеннями як усталено для відповідних типів:

Для успішної роботи з масивами потрібно уміти здійснювати щонайменше такі (базові) операції при роботі з масивами:

Робота з багатовимірними масивами подібна до роботи з одновимірними. Відмінність лише в тому, що використовують додаткові індекси, записні через кому. Переважно використовуються двовимірні масиви для подання прямокутних таблиць даних. Наприклад, двовимірний і тривимірний масиви можна створити таким чином:

int[,]  twoD   = new int[3,4];   //створення масиву 3x4
int[,,] threeD = new int[3,4,5]; //створення масиву 3х4х5

Двовимірний масив можна уявити у вигляді прямокутника, розділеного на квадрати-елементи. У цьому випадку перший індекс, записаний ліворуч, тлумачать як номер рядка, а другий індекс, записаний праворуч як номер — стовпчика. Це можна зобразити умовно таким чином:

[0,0][0,1][0,2][0,3][0,4]
[1,0][1,1][1,2][1,3][1,4]
[2,0][2,1][2,2][2,3][2,4]
[3,0][3,1][3,2][3,3][3,4]

Тривимірний масив можна уявити у вигляді прямокутного паралеле­піпеда, розділе­ного на куби-елементи.

Способи заповнення масиву подано прикладами консольних застосунками, які можна компілювати вказівкою терміналу і запускати на виконання з викликом Терміналу.

  1. Ініціалізацією у коді
    using System;
    using System.IO; 
    class Example
    { public static void Main()
      { int[]  a = new int[7] {1,2,4,8,16,32,64};
        for (int j=0; j<7; j++) Console.Write("{0} ",a[j]);
        Console.ReadLine(); // Очікування натискання клавіші Enter
      }
    }

    Службове слово var дозволяє описати змінну таким чином, щоб компілятор визначав її тип за значенням — див. приклад коду

    using System;
    class Example
    { static void Main(string[] args)
      { var a = new[] {  1 ,  2 ,  3 };
        var b = new[] {"Aa","Бб","Вв"};
        Console.WriteLine("Тип масиву a: {0}",a.GetType());
        Console.WriteLine("Тип масиву b: {0}",b.GetType());
        Console.ReadLine();
      }
    }
    з таким виведенням.
    Тип масиву a: System.Int32[]
    Тип масиву b: System.String[]
    У основі кожного типу у системі типів .NET (зокрема фундаментальних типів даних) лежить базовий клас System.Object. Тому можливе визначення масиву об'єктів, що містять різні типи даних — див. приклад коду

    using System;
    class Example
    { static void Main(string[] args)
      { object[] a = { true, 10, "Вітаю", 13.7};
        foreach (object b in a) Console.WriteLine("Тип {0} - {1}", b, b.GetType());
        Console.ReadLine();
      }
    }
    з таким виведенням.
    Тип True - System.Boolean
    Тип 10 - System.Int32
    Тип Вітаю - System.String
    Тип 13,7 - System.Double
  2. Згідно з формулою (у тому числі рекурентною)
    using System;
    using System.IO; 
    class Example
    { public static void Main()
      { int[] a = new int[7];
        int j;
        for (j=0; j<7; j++) a[j]=j*j;
        for (j=0; j<7; j++) Console.Write("{0} ",a[j]); // 0 1 4 9 16 25 36 
        Console.WriteLine(); 
        a[0]=1;
        a[1]=2;
        for (j=2; j<7; j++) a[j]=a[j-1]+a[j-2];
        for (j=0; j<7; j++) Console.Write("{0} ",a[j]); //1 2 3 5 8 13 21  
        Console.ReadLine();    // Очікування натискання клавіші Enter
      }
    }
  3. З клавіатури
    using System;
    using System.IO; 
    class Example
    { public static void Main()
      { Console.Write("Введіть кількість елементів масиву: ");
        int n = Int32.Parse(Console.ReadLine());
        int[] a = new int[n];
        for (int j=0; j<n; j++) 
        { Console.Write("Введіть значення елемнта з індексом {0} ",j);
          a[j] = Int32.Parse(Console.ReadLine());
        }
        for (int j=0; j<n; j++) Console.Write(" {0} ",a[j]); 
        Console.ReadLine(); // Очікування натискання клавіші Enter
      }
    }
  4. З файлу

    using System;
    using System.IO; 
    class Example
    { public static void Main()
      { int[] a = new int[9]; // Масив, елементам якого потрібно надати значень
        int  na = 0;          // Кількість наданих значень
        string[] s = File.ReadAllLines("input.txt");
        for (int j=0; j<s.Length; j++)
        { string[] t = s[j].Split(' ');
          for (int k=0; k<t.Length; k++) if (t[k]!="")
          { try { a[na] = Int32.Parse(t[k]); na++;}
            catch (Exception e) {Console.WriteLine(e.Message);}
          }
        }
        for (int j=0; j<na; j++) Console.Write("{0} ",a[j]);
        Console.ReadLine(); // Очікування натискання клавіші Enter
      }
    }
  5. За допомогою методу Fill класу Array для заповнення всього масиву або його частини однаковими значеннями.
    using System;
    using System.IO; 
    class Example
    { public static void Main()
      { int[] a = new int[7]; // Масив, елементам якого потрібно надати значень
        for (int j=0; j<7; j++) Console.Write("{0} ",a[j]); // 0 0 0 0 0 0 0
        Console.WriteLine();
        Array.Fill(a, 5);
        for (int j=0; j<7; j++) Console.Write("{0} ",a[j]); // 5 5 5 5 5 5 5 
        Console.WriteLine();
        Array.Fill(a, 1,2,3);
        for (int j=0; j<7; j++) Console.Write("{0} ",a[j]); // 5 5 1 1 1 5 5
        Console.ReadLine(); // Очікування натискання клавіші Enter
      }
    }
  6. З використанням породжувача випадкових чисел Random
    using System;
    using System.IO; 
    class Example
    { public static void Main()
      { Random r = new Random();// Породжувач випадкових чисел
        int[]  a = new int[9];  // Масив, елементам якого потрібно надати
                                // випадкових значень від 0 до 6 включно
        for (int j=0; j<9; j++) { a[j] = r.Next(7);
                                  Console.Write("{0} ",a[j]);
                                }
        Console.ReadLine();    // Очікування натискання клавіші Enter
      }
    }

Дані, породжені випадковим чином, часто допомагають при тестуванні програм. Наприклад, для того, щоб перевірити правильність упорядкування послідовності чисел.

Звичайний двовимірний масив можна подати прямокутною таблицею елементів зі сталою довжиною рядків. Але в C# можна створювати спеціальний тип двовимірного масиву, так званий ступінчастий масив масивів. Такий масив оголошують за допомогою низки квадратних дужок, у яких вказується розмірність підмасивів. Для оголошення двовимірного ступінчастого масиву використовують вказівку такого вигляду:

тип [][] назва_масиву = new тип[кількість_рядків] [];

— див. приклад коду

using System;
class Example
{ static void Main(string[] args)
  { int j,k;
    int[][] a = new int[3][];
    a[0] = new int[4];
    a[1] = new int[3];
    a[2] = new int[5];
    for (j = 0; j < 4; j++) a[0][j] = (j+1);
    for (j = 0; j < 3; j++) a[1][j] = (j+1)*(j+1);
    for (j = 0; j < 5; j++) a[2][j] = (j+1)*(j+1)*(j+1);
    for (k = 0; k < a.Length; k++)
    { for (j = 0; j < a[k].Length; j++) Console.Write("{0} ", a[k][j]);
      Console.WriteLine();
    }
    Console.ReadLine(); // Очікування натискання клавіші Enter
  }
}

з таким виведенням.

1 2 3 4 
1 4 9 
1 8 27 64 125

Масиви — це типи посилань: надання значення змінної типу масиву інший змінної означає лише існування двох змінних, що посилаються на одні й ті самі дані — див. код

using System;
class Example
{ static void Main(string[] args)
  { var a = new[] {"Aa", "Bb",  "Cc"};
    string[] b = new string[3];
    b=a;
    foreach (string s in b) Console.Write("{0} ",s);
    Console.WriteLine();
    a[0]="Xx";
    foreach (string s in b) Console.Write("{0} ",s);
    Console.ReadLine();
  }
}

з таким виведенням.

Aa Bb Cc 
Xx Bb Cc

Приклад 1 — знаходження найбільший елемент лінійного масиву.

using System;
using System.IO; 
class Example
{ public static void Main()
  { double[] a = new double[] {1.1, 2.2, 1.1, 3.2, 1.2, 2.1};
    double max = a[0];
    for (int j = 0; j < a.Length; j++) if (max < a[j]) max = a[j];
    Console.Write("Найбільше число у масиві: {0}",max);  
    Console.ReadLine(); // Очікування натискання клавіші Enter
  }
}

Cпочатку змінній max надано значення елемента масиву з індексом 0, після чого (в циклі) йде послідовне порівняння max з кожним наступним елементом масиву числом до останнього. Якщо при порівнянні значення чергового значення у масиві більше за значення max, то змінній max буде надано це значення. По закінченню циклу змінна max міститиметься максимальне значення, яке і буде виведене у консоль:

Найбільше число у масиві: 3.2

У C# масиви є спеціальними об’єктами. Це забезпечує деяку додаткову функціональність масивам. Зокрема, можна дізнатися довжину масиву за допомогою властивості Length — див. попередній приклад коду.

Приклад 2 — обчислення суми значень елементів масиву

using System;
using System.IO; 
class Example
{ public static void Main()
  { int[] a = new int[5] {10, 11, 12, 13, 14};
    int   s = 0;
    for (int j = 0; j < a.Length; j++) s+=a[j];
    Console.Write("Cума значень елементів масиву дорівнює {0}",s);
    Console.ReadLine(); // Очікування натискання клавіші Enter
  }
}

У результаті на консоль буде виведено таке повідомлення:

Cума значень елементів масиву дорівнює 60

Приклад 3 — обчислення середнього арифметичного значень елементів масиву

using System;
using System.IO; 
class Example
{ public static void Main()
  { double[] a = new double[5] {10.1, 11.2, 12.3, 13.4, 14.5 };
    double  av = 0;
    for (int j = 0; j < a.Length; j++) av+=a[j];
    av=av/a.Length;
    Console.Write("Середнє значення дорівнює {0}",av);
    Console.ReadLine(); // Очікування натискання клавіші Enter
  }
}

У результаті на консоль буде виведено таке повідомлення:

Середнє значення дорівнює 12.3

Приклад 4 — об’єднання (склеювання) масивів

using System;
using System.IO; 
class Example
{ public static void Main()
  { int[] a = {3, 2, 1};
    int[] b = {9, 8, 7, 6};
    int[] c = new int [a.Length+b.Length];
    a.CopyTo(c, 0);
    b.CopyTo(c, a.Length);
    for (int j = 0; j < c.Length; j++)
    Console.Write("{0} ",c[j]);         // 3 2 1 9 8 7 6  
    Console.ReadLine(); // Очікування натискання клавіші Enter
  }
}

Cортування (лінійного масиву) — упорядкування елементів масиву у порядку зростання чи спадання їхніх значень.

Це найпоширеніше завдання при роботі з лінійними масивами. Існує велика кількість різних алгоритмів, призначених для розв'язання цього завдання: метод бульбашки, вибором найбільшого (найменшого) значення, шейкерний, злиттям, вставки, пірамідальний, швидким методом Гоара. Всі методи різняться швидкістю виконання і потрібним об'ємом пам'яті, необхідної для зберігання проміжних даних і результатів. Далі подано описи деяких із них разом з програмною реалізацією мовою C# із випадковим заповненням масиву.

Метод сортування бульбашкою (Bubble sort)
Алгоритм проходить масив від початку і до кінця, порівнюючи попарно сусідні елементи, для яких різниця значень індексів дорівнює 1. Значення елементів розташовано у неправильному порядку, то їх міняють місцями, таким чином, після першого проходу на кінці масиву буде розташовано найбільший елемент (для упорядкування за зростанням). Тобто, найбільший елемент «спливе» до потрібної позиції як бульбашка у воді. Потім прохід масиву повторюють, і на передостанньому місці буде розташовано найбільший після максимального елемент і т.д.

using System;
using System.IO; 
class Example
{ public static void Main()
  { Random r = new Random();
    int j, n = 15;
    int[]  a = new int[n]; 
    for (j=0; j<n; j++) a[j] = r.Next(10);
    Console.Write("Початковий масив:   ");
    for (j=0; j<n; j++) Console.Write(" {0}",a[j]);
    bubbleSort(a);
    Console.Write("\nУпорядкований масив:");
    for (j=0; j<n; j++) Console.Write(" {0}",a[j]);
    Console.ReadLine();  // Очікування натискання клавіші Enter
  }
  public static void bubbleSort (int[] a)
  { for (int i = a.Length - 1; i > 0; i--)
    { for (int j = 0; j < i; j++) if (a[j] > a[j+1])
      { int tmp = a[j];
                  a[j] = a[j+1];
                         a[j+1] = tmp;
      }
    }
  }
}

Сортування вибором (Selection sort)
Змінюючи i від 0 — найменшого значення індексу — до (n – 1) — найбільшого значення індексу (n — кількість елементів масиву, яку називають довжиною масиву), робимо таке:

Подамо для наочності ілюстрацію такого впорядкування 10-елементного масиву, запози­чену зі сторінки Вікіпедії.

Тут червоним тлом виділено те значення, яке серед елементів з номером від j до n вважають найменшим у поточний момент виконання алгоритму, блакитним — значення поточного елемента масиву, яке порівнюють з тим, що наразі вважають найменшим, жовтим — ті значення, які вже стоять на потрібному місці.

using System;
using System.IO; 
class Example
{ public static void Main()
  { Random r = new Random();
    int j, n = 15;
    int[]  a = new int[n]; 
    for (j=0; j<n; j++) a[j] = r.Next(10,100);
    Console.Write("Початковий масив:   ");
    for (j=0; j<n; j++) Console.Write(" {0}",a[j]);
    selectionSort(a);
    Console.Write("\nУпорядкований масив:");
    for (j=0; j<n; j++) Console.Write(" {0}",a[j]);
    Console.ReadLine();  // Очікування натискання клавіші Enter
  }
  public static void selectionSort(int[] a)
  { int i,j,k,m,min;
    for (i = 0; i < a.Length; i++)
    { min = a[i];
      k   = i;
      for (j = i+1; j < a.Length; j++) if (a[j] < min)
      { min = a[j];
        k   = j;
      }
      if (i != k)
      { m = a[i];
            a[i] = a[k];
                   a[k] = m;
      }
    }
  }
}

Вище подано алгоритм вибору найменшого елемента при упорядкуванні за зростанням. Можливі й інші варіанти:

Метод швидкого сортування
Основу цього алгоритму розробив у 1962 році британський учений Чарлз Ентоні Річард Гоар (Charles Antony Richard Hoare). Рекурсивний алгоритм для впорядкування за неспаданням масиву зі значеннями індексів від l до r включно має такий вигляд:

  1. Вибрати значення елемента масиву.

  2. Значення елементів масиву, що не менші за вибране, розташувати на місцях з номерами (індексами) більшими від індексу обраного значення, а ті, що не перевищують — на місцях з номерами меншими від індексу обраного значення. У результаті вибране значення матиме той номер j у масиві, що потрібно. Тому надалі відповідний елемент масиву буде незмінним.

  3. Якщо l < (j – 1), виконати алгоритм для індексів від l до (j – 1) включно.

  4. Якщо (j + 1) < r, виконати алгоритм для індексів від (j + 1) до r включно.

Рекурсивність алгоритму означає виклик цього алгоритму в ньому самому — див. кроки 3–4. На малюнку нижче подано частину схеми впорядкування.

Тут кольоровими кругами позначено вибрані значення елементів масиву. Після переставлянь навколо них ці значення надалі залишаються нерухомими у масиві. Спочатку буде впорядковано ті елементи, які менші від значення, позначеного зеленим кольором, потім — менші від значення, позначеного червоним кольором, ще пізніше — менші від значення, позначеного синім кольором.

Примітка. Поданий вище запис стане алгоритмом лише після того, як буде деталізовано вибір значення. У багатьох описах алгоритму можна зустріти вибір елемента з найменшим чи з найбільшим номером. Розглянемо спробу впорядкувати вже впорядкований масив при такому виборі. Глибина занурення — кількість послідовних викликів рекурсивного алгоритму без виходу з нього — буде майже збігатися з довжиною масиву. Останнє може призвести до переповнення стеку частини оперативної пам'яті, куди програма заносить дані про виклик програми та функцій, їхні аргументи, точку повернення тощо. А переповнення стеку означає аварійне завершення роботи. Який би не був детерміністичний (без випадковостей) вибір значення (наприклад, із середини масиву), завжди можна вказати вхідні дані, для яких глибина занурення для цих вхідних даних лише на 1 відрізнятиметься від довжини масиву. Тому вибір потрібно робити з використанням генератора випадкових чисел. І в цьому випадку можливе переповнення стеку, але ймовірність цієї події настільки мала, що аварійне припинення роботи зазвичай не відбувається.

Проаналізуйте на відповідність опису ілюстрацію для вибору правого краю (елемента з найбільшим номером), запозичену із сайту http://uk.wikipedia.org/wiki

using System;
using System.IO; 
class Example
{ public static void Main()
  { Random r = new Random();
    int j, n = 15;
    int[]  a = new int[n]; 
    for (j=0; j<n; j++) a[j] = r.Next(10,100);
    Console.Write("Початковий масив:   ");
    for (j=0; j<n; j++) Console.Write(" {0}",a[j]);
    quickSort(a,0,a.Length-1);
    Console.Write("\nУпорядкований масив:");
    for (j=0; j<n; j++) Console.Write(" {0}",a[j]);
    Console.ReadLine();  // Очікування натискання клавіші Enter
  }
  public static void quickSort(int[] a, int first, int last)
  { Random r = new Random();
    int i = first;
    int j = last;
    int x = a[r.Next(first,last + 1)];
    do { while (a[i] < x) i++;
         while (a[j] > x) j--;
         if (i <= j)
         { if (a[i] > a[j])
           { int temp = a[i];
                        a[i] = a[j];
                               a[j] = temp;
           }
           i++;
           j--;
         }
       } while (i <= j);
    if (i < last)  quickSort(a, i, last);
    if (first < j) quickSort(a, first, j);
  }
}

Розробники, які використовують мови програмування, актуальні для сучасної індустрії програмного забезпечення, для упорядкування масивів чи інших вбудованих структур даних використовують вбудовані методи. Це істотно скорочує код і полегшує роботу розробника. Для класу масивів Array таким є метод Sort — див. приклад коду з його використанням.

using System;
using System.IO; 
class Example
{ public static void Main()
  { Random r = new Random();
    int j, n = 15;
    int[]  a = new int[n]; 
    for (j=0; j<n; j++) a[j] = r.Next(10,100);
    Console.Write("Початковий масив:   ");
    for (j=0; j<n; j++) Console.Write(" {0}",a[j]);
    Array.Sort(a);
    Console.Write("\nУпорядкований масив:");
    for (j=0; j<n; j++) Console.Write(" {0}",a[j]);
    Console.ReadLine();  // Очікування натискання клавіші Enter
  }
}

Метод Array.Sort має такі варіанти виклику (у дужках перелічено типи відповідних арґументів):

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

using System;
using System.Collections;

public class Example
{ public class myReverserClass : IComparer
  { // Викликає CaseInsensitiveComparer.Compare зі зміненими параметрами
    int IComparer.Compare( Object x, Object y )
    { return( (new CaseInsensitiveComparer()).Compare( y, x ));
    }
  }

  public static void Main()
  { int j;  // лічильник елементів масиву

     // Створення масивів, надання їхнім елементам початкових значень
     String[] a = { "x", "Y", "z", "w", "v", "u"};  
     String[] b = { "1", "2", "3", "4", "5", "6"};

     //Налаштовуваний порівнювач
     IComparer c = new myReverserClass();

     Console.WriteLine("Початкові значення елементів масивів");
     for (j=0; j<a.Length; j++)  Console.WriteLine(" {0} {1}", a[j], b[j]);

     Array.Sort (a, b, 1, 3 );
     Console.WriteLine("\nПісля упорядкування за допомогою стандартного відношення порядку в діапазоні 1-3" );
     for (j=0; j<a.Length; j++)  Console.WriteLine(" {0} {1}", a[j], b[j]);

     Array.Sort (a, b, 1, 3, c );
     Console.WriteLine("\nПісля упорядкування за допомогою порядку, зворотного до стандартного, в діапазоні 1-3");
     for (j=0; j<a.Length; j++)  Console.WriteLine(" {0} {1}", a[j], b[j]);

     Array.Sort (a, b );
     Console.WriteLine("\nПісля упорядкування за допомогою стандартного відношення порядку");
     for (j=0; j<a.Length; j++)  Console.WriteLine(" {0} {1}", a[j], b[j]);

     Array.Sort( a, b, c );
     Console.WriteLine( "\nПісля упорядкування за допомогою порядку, зворотного до стандартного");
     for (j=0; j<a.Length; j++)  Console.WriteLine(" {0} {1}", a[j], b[j]);
     Console.Read(); // Очікування натискання клавіші Enter
  }
}
/*
Код породжує таке виведення

Початкові значення елементів масивів
 x 1
 Y 2
 z 3
 w 4
 v 5
 u 6

Після упорядкування за допомогою стандартного відношення порядку в діапазоні 1-3
 x 1
 w 4
 Y 2
 z 3
 v 5
 u 6

Після упорядкування за допомогою порядку, зворотного до стандартного, в діапазоні 1-3
 x 1
 z 3
 Y 2
 w 4
 v 5
 u 6

Після упорядкування за допомогою стандартного відношення порядку
 u 6
 v 5
 w 4
 x 1
 Y 2
 z 3

Після упорядкування за допомогою порядку, зворотного до стандартного
 z 3
 Y 2
 x 1
 w 4
 v 5
 u 6
*/

Методи класу Array (у дужках перелічено типи відповідних арґументів, cимвол * після назви методу означає, що він має більше одного способу виклику (сиґнатури), за додатковою інформацією звертатися до офіційної документації):

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

using System;
using System.IO; 
class Example
{ public static void Main()
  { Random r = new Random();
    int j, n = 15, // кількість елементів
           m =100; // верхня межа діапазону
    int[]  a = new int[n];
    for (j=0; j<n; j++) a[j] = r.Next(m);
    Console.Write("Початковий масив:   ");
    for (j=0; j<n; j++) Console.Write(" {0}",a[j]);
    countingSort(a,m);
    Console.Write("\nУпорядкований масив:");
    for (j=0; j<n; j++) Console.Write(" {0}",a[j]);
    Console.ReadLine();  // Очікування натискання клавіші Enter
  }
  public static void countingSort(int[] a, int m)
  { int j,k,l=0;
    int[] b = new int[m];
    for (j = 0; j < a.Length; j++) b[a[j]]++;
    for (j = 0; j < m;    j++)
    for (k = 0; k < b[j]; k++)
    { a[l] = j;
      l++;
    }
  }
}

Алгоритм двійкового (бінарного) пошуку використовують для пошуку (індекса) елемента масиву з певним значенням. Цей алгоритм застосовують до попередньо упорядкованого лінійного масиву при розташуванні заданого значення елемента між найбільшим та найменшим значеннями елементів масиву. За рахунок упорядкованості масиву досягають великої швидкості пошукукількість операцій пропорційна log2 N, що позначають таким чином: O(log2 N).

Головна ідея двійкового пошукуз кожним наближенням поділяти множину значень (приблизно) навпіл.

Для стислого опису алгоритму запровадимо такі позначення для змінних, початкові значення яких задають на початку виконання алгоритму:

Алгоритм бінарного пошуку:
  1. Поки jminjmax робити таке:
    1. Обчислити значення j = [( jmin + jmax) / 2].
    2. Якщо b = aj, то припинити виконання алгоритму з отриманим значенням j.
    3. Якщо b < aj, то надати змінній jmax значення j – 1.
    4. Якщо aj < b, то надати змінній jmin значення j + 1.
  2. Повернути від'ємне значення – 1 як ознаку того, що шуканого індексу немає.

using System;
using System.IO; 
class Example
{ public static void Main()
  { Random r = new Random();
    int j, n = 15, // кількість елементів
          da = 5;  // верхня межа приросту значень елементів
    int[]  a = new int[n];
    a[0]=0;
    for (j=1; j<n; j++) a[j] = a[j-1]+1+r.Next(da);
    Console.Write("Масив:");
    for (j=0; j<n; j++) Console.Write(" {0}",a[j]);
    int b = r.Next(a[n-1]+1); // число для розташування серед елементів масиву 
    Console.Write("\nЗначення {0} ",b);
    j = binarySearch(a, b);
    if (j<0) Console.WriteLine("немає серед елементів масиву.",b);
    else     Console.WriteLine("має індекс {0}.",j);
    Console.ReadLine();  // Очікування натискання клавіші Enter
  }
  public static int binarySearch(int[] a, int b)
  { int j_min = 0,  j, c;
    int j_max = a.Length - 1;
    while (j_min <= j_max)
    { j = (j_max  + j_min) / 2;
      c = a[j];
      if (c == b) return j; else
      if (c  < b) j_min = j + 1;
      else        j_max = j - 1;
    }
    return -1;
  }
}

В C# є вбудований метод бінарного пошуку BinarySearch у класі Arrays, який застосовують до упорядкованих лінійних масивів — див. еквівалент попереднього коду.

using System;
using System.IO; 
class Example
{ public static void Main()
  { Random r = new Random();
    int j, n = 15, // кількість елементів
          da = 5;  // верхня межа приросту значень елементів
    int[]  a = new int[n];
    a[0]=0;
    for (j=1; j<n; j++) a[j] = a[j-1]+1+r.Next(da);
    Console.Write("Масив:");
    for (j=0; j<n; j++) Console.Write(" {0}",a[j]);
    int b = r.Next(a[n-1]+1); // число для розташування серед елементів масиву 
    Console.Write("\nЗначення {0} ",b);
    j = Array.BinarySearch(a, b);
    if (j<0) Console.WriteLine("немає серед елементів масиву.",b);
    else     Console.WriteLine("має індекс {0}.",j);
    Console.ReadLine();  // Очікування натискання клавіші Enter
  }
}

Метод Array.BinarySearch має такі варіанти виклику (у дужках перелічено типи відповідних арґументів):

  • BinarySearch(Array, Object) — пошук значення (2-ий арґумент) в упорядкованому масиві (1-ий арґумент) з використанням інтерфейсу IComparable, втіленого для кожного елемента масиву.

  • BinarySearch(Array, Object, IComparer) — пошук значення (2-ий арґумент) в упорядкованому масиві (1-ий арґумент)з використанням універсального інтерфейсу (3-ій арґумент).

  • BinarySearch(Array, Int32, Int32, Object) — пошук значення (4-ий арґумент) у діапазоні значень індексів (2-ий і 3-ій арґументи) елементів масиву (1-ий арґумент) з використанням інтерфейсу IComparable, втіленого для кожного елемента масиву.

  • BinarySearch(Array, Int32, Int32, Object, IComparer) — пошук значення (4-ий арґумент) у діапазоні значень індексів (2-ий і 3-ій арґументи) елементів масиву (1-ий арґумент) з використанням інтерфейсу (5-ий арґумент).

  • BinarySearch<T>(T[], T) — пошук значення (2-ий арґумент) у масиві (1-ий арґумент) з використанням універсальний інтерфейсу IComparable<T>, втіленого для кожного елементаом масиву і заданого об'єкта.

  • BinarySearch<T>(T[], T, IComparer<T>) — пошук значення (2-ий арґумент) у масиві з використанням універсального інтерфейсу (3-ій арґумент).

  • BinarySearch<T>(T[], Int32, Int32, T) — пошук значення (4-ий арґумент) у діапазоні значень індексів (2-ий і 3-ій арґументи) елементів масиву (1-ий арґумент) з використанням універсального інтерфейсу IComparable<T&пt;, втіленого для кожного елемента масиву і заданого значення.

  • BinarySearch<T>(T[], Int32, Int32, T, IComparer<T>) — пошук значення (4-ий арґумент) у діапазоні значень індексів (2-ий і 3-ій арґументи) елементів масиву (1-ий арґумент) з використанням універсального інтерфейсу (5-ий арґумент).

5. Закріплення вивченого матеріалу

  1. Що таке масив?
  2. Які властивості має масив?
  3. Що таке форма масиву?
  4. Як описують масив мовою C#?
  5. Які методи впорядкування лінійних масивів Ви знаєте?
  6. У чому полягає впорядкування масиву швидким методом Хоара?
  7. У чому полягає бінарний пошук?

6. Підбиття підсумків уроку
Виставлення оцінок.

7. Домашнє завдання

  1. Вивчити матеріал уроку.

  2. Написати програму розв'язання такої задачі: заповнити лінійний масив випадковим чином і визначити, скільки разів досягається найбільше значення у даному масиві та найменший порядковий номер (індекс) для такого найбільшого значення.


Текст упорядкував Олександр Рудик.