Ngurutake minangka salah sawijining jinis dhasar kegiatan utawa tumindak sing ditindakake ing obyek. Malah ing kanak-kanak, bocah-bocah diwulang kanggo ngurutake, ngembangake pikirane. Komputer lan program uga ora ana sing istiméwa. Ana macem-macem algoritma. Aku saranake sampeyan ndeleng apa iku lan cara kerjane. Kajaba iku, kepiye yen ing sawijining dina sampeyan ditakoni babagan salah sawijining wawancara?
Bahan:
L совпадёт с
Pambuka
Ngurutake unsur minangka salah sawijining kategori algoritma sing kudu digunakake pangembang. Yen biyen, nalika aku sinau, ilmu komputer ora ditindakake kanthi serius, saiki ing sekolah kudu bisa ngetrapake algoritma ngurutake lan ngerti. Algoritma dhasar, sing paling gampang, diimplementasikake nggunakake loopfor
. Alami, kanggo ngurutake koleksi unsur, contone, array, sampeyan kudu ngliwati koleksi iki. Tuladhane:
int[] array = {10, 2, 10, 3, 1, 2, 5};
for (int i = 0; i < array.length; i++) {
System.out.println(array[i]);
}
Apa sampeyan bisa ngomong babagan potongan kode iki? Kita duwe daur ulang sing ngganti nilai indeks ( int i
) saka 0 menyang unsur pungkasan ing array. Ateges, kita mung njupuk saben unsur ing array lan nyithak isine. Sing luwih akeh unsur ing array, luwih suwe kode kasebut bakal ditindakake. Yaiku, yen n minangka jumlah unsur, kanthi n = 10 program bakal njupuk 2 kaping luwih suwe tinimbang karo n = 5. Nalika program kita duwe siji daur ulang, wektu eksekusi mundhak linear: luwih akeh unsur, luwih dawa eksekusi. Pranyata algoritma kode ing ndhuwur mlaku ing wektu linear (n). Ing kasus kasebut, "kompleksitas algoritma" diarani O (n). Notasi iki uga disebut "O gedhe" utawa "prilaku asimtotik". Nanging sampeyan mung bisa ngelingi "kerumitan algoritma."
Ngurutake paling gampang (Urut Gelembung)
Dadi, kita duwe array lan bisa ngulang. Agung. Ayo saiki nyoba ngurutake kanthi urutan munggah. Apa tegese iki kanggo kita? Iki tegese yen diwenehi rong unsur (contone, a = 6, b = 5), kita kudu ngganti a lan b yen a luwih gedhe tinimbang b (yen a > b). Apa tegese iki kanggo kita nalika nggarap koleksi kanthi indeks (kaya kasus array)? Iki tegese yen unsur kanthi indeks a luwih gedhe tinimbang unsur kanthi indeks b, (larik[a] > larik[b]), unsur kasebut kudu diganti. Ganti panggonan asring diarani swap. Ana macem-macem cara kanggo ngganti panggonan. Nanging kita nggunakake kode prasaja, cetha lan gampang kanggo elinga:private void swap(int[] array, int ind1, int ind2) {
int tmp = array[ind1];
array[ind1] = array[ind2];
array[ind2] = tmp;
}
Saiki kita bisa nulis ing ngisor iki:
int[] array = {10, 2, 10, 3, 1, 2, 5};
System.out.println(Arrays.toString(array));
for (int i = 1; i < array.length; i++) {
if (array[i] < array[i - 1]) {
swap(array, i, i-1);
}
}
System.out.println(Arrays.toString(array));
Kaya sing kita deleng, unsur kasebut pancen wis owah. Kita miwiti karo siji unsur, amarga ... yen array kasusun saka mung siji unsur, expression 1 < 1 ora bakal bali bener lan kanthi mangkono kita bakal nglindhungi dhéwé saka kasus array karo siji unsur utawa tanpa kabeh, lan kode bakal katon luwih apik. Nanging array pungkasan kita ora diurutake, amarga ... Ora bisa ngurutake kabeh wong ing siji pass. Kita kudu nambah daur ulang liyane sing bakal ditindakake siji-sijine nganti entuk array sing diurutake:
int[] array = {10, 2, 10, 3, 1, 2, 5};
System.out.println(Arrays.toString(array));
boolean needIteration = true;
while (needIteration) {
needIteration = false;
for (int i = 1; i < array.length; i++) {
if (array[i] < array[i - 1]) {
swap(array, i, i-1);
needIteration = true;
}
}
}
System.out.println(Arrays.toString(array));
Dadi ngurutake pisanan kita makarya. We iterate ing daur ulang njaba ( while
) nganti kita mutusaké sing ora perlu maneh iterasi. Kanthi gawan, sadurunge saben pengulangan anyar, kita nganggep yen array kita diurutake, lan kita ora pengin maneh. Mulane, kita ngliwati unsur-unsur kasebut kanthi urutan lan mriksa asumsi iki. Nanging yen unsur wis metu saka urutan, kita ngganti unsur lan éling sing kita ora yakin sing unsur saiki ing urutan bener. Mulane, kita pengin nindakake siji pengulangan maneh. Contone, [3, 5, 2]. 5 luwih saka telu, kabeh apik. Nanging 2 kurang saka 5. Nanging, [3, 2, 5] mbutuhake pass siji liyane, amarga 3> 2 lan kudu diganti. Amarga kita nggunakake loop ing loop, ternyata kerumitan algoritma kita mundhak. Kanthi n unsur dadi n * n, yaiku O(n^2). Kompleksitas iki diarani kuadrat. Kaya sing dingerteni, kita ora bisa ngerti persis jumlah iterasi sing dibutuhake. Indikator kerumitan algoritma serves tujuan kanggo nuduhake tren nambah kerumitan, kasus paling awon. Carane akeh wektu mlaku bakal nambah nalika nomer unsur n owah-owahan. Urut gelembung minangka salah sawijining jinis sing paling gampang lan ora efisien. Iki uga kadhangkala disebut "ngurutake bodho". Materi sing gegandhengan:
Seleksi Urut
Jenis liyane yaiku jinis pilihan. Uga nduweni kerumitan kuadrat, nanging luwih akeh mengko. Dadi gagasan iku prasaja. Saben pass milih unsur paling cilik lan pindhah menyang wiwitan. Ing kasus iki, miwiti saben pass anyar kanthi pindhah menyang tengen, yaiku, pass pisanan - saka unsur pisanan, pass kapindho - saka kaloro. Bakal katon kaya iki:int[] array = {10, 2, 10, 3, 1, 2, 5};
System.out.println(Arrays.toString(array));
for (int left = 0; left < array.length; left++) {
int minInd = left;
for (int i = left; i < array.length; i++) {
if (array[i] < array[minInd]) {
minInd = i;
}
}
swap(array, left, minInd);
}
System.out.println(Arrays.toString(array));
Urut iki ora stabil, amarga unsur podho rupo (saka titik tampilan saka karakteristik kang kita ngurutake unsur) bisa ngganti posisi sing. Conto apik diwenehi ing artikel Wikipedia: Sorting_by-selection . Materi sing gegandhengan:
Urut sisipan
Urut sisipan uga nduweni kerumitan kuadrat, amarga kita duwe daur ulang ing daur ulang. Kepiye bedane karo jinis pilihan? Urut iki "stabil". Iki tegese unsur sing padha ora bakal ngganti urutane. Identik ing babagan karakteristik sing diurutake.int[] array = {10, 2, 10, 3, 1, 2, 5};
System.out.println(Arrays.toString(array));
for (int left = 0; left < array.length; left++) {
// Retrieve the value of the element
int value = array[left];
// Move through the elements that are before the pulled element
int i = left - 1;
for (; i >= 0; i--) {
// If a smaller value is pulled out, move the larger element further
if (value < array[i]) {
array[i + 1] = array[i];
} else {
// If the pulled element is larger, stop
break;
}
}
// Insert the extracted value into the freed space
array[i + 1] = value;
}
System.out.println(Arrays.toString(array));
Materi sing gegandhengan:
Shuttle Sort
Antarane macem-macem prasaja, ana siji liyane - ngurutake antar-jemput. Nanging aku luwih seneng macem-macem anter jemput. Iku misale jek kula sing kita arang pirembagan bab shuttle angkasa, lan anter jemput luwih roto. Mulane, luwih gampang mbayangno carane shuttles diluncurake menyang angkasa. Mangkene asosiasi karo algoritma iki. Apa inti saka algoritma? Inti saka algoritma iku kita iterate saka kiwa menyang tengen, lan nalika ngganti unsur, kita mriksa kabeh unsur liyane sing kiwa konco kanggo ndeleng yen swap kudu bola.int[] array = {10, 2, 10, 3, 1, 2, 5};
System.out.println(Arrays.toString(array));
for (int i = 1; i < array.length; i++) {
if (array[i] < array[i - 1]) {
swap(array, i, i - 1);
for (int z = i - 1; (z - 1) >= 0; z--) {
if (array[z] < array[z - 1]) {
swap(array, z, z - 1);
} else {
break;
}
}
}
}
System.out.println(Arrays.toString(array));
Materi sing gegandhengan:
Urut cangkang
Urut prasaja liyane yaiku Urut Shell. Intine padha karo urutan gelembung, nanging saben pengulangan kita duwe celah sing beda ing antarane unsur sing dibandhingake. Saben pengulangan dipérang dadi setengah. Punika conto implementasine:int[] array = {10, 2, 10, 3, 1, 2, 5};
System.out.println(Arrays.toString(array));
// Calculate the gap between the checked elements
int gap = array.length / 2;
// As long as there is a difference between the elements
while (gap >= 1) {
for (int right = 0; right < array.length; right++) {
// Shift the right pointer until we can find one that
// there won't be enough space between it and the element before it
for (int c = right - gap; c >= 0; c -= gap) {
if (array[c] > array[c + gap]) {
swap(array, c, c + gap);
}
}
}
// Recalculate the gap
gap = gap / 2;
}
System.out.println(Arrays.toString(array));
Materi sing gegandhengan:
Gabung urut
Saliyane jinis prasaja sing dituduhake, ana jinis sing luwih rumit. Contone, nggabungake sort. Kaping pisanan, rekursi bakal mbantu kita. Kapindho, kerumitan kita ora bakal dadi kuadrat maneh, kaya biasane. Kerumitan algoritma iki yaiku logaritmik. Ditulis minangka O(n log n). Dadi ayo padha nindakake iki. Pisanan, ayo nulis telpon rekursif menyang cara ngurutake:public static void mergeSort(int[] source, int left, int right) {
// Choose a separator, i.e. split the input array in half
int delimiter = left + ((right - left) / 2) + 1;
// Execute this function recursively for the two halves (if we can split(
if (delimiter > 0 && right > (left + 1)) {
mergeSort(source, left, delimiter - 1);
mergeSort(source, delimiter, right);
}
}
Saiki, ayo nambah tumindak utama. Iki minangka conto supermethod kanthi implementasine:
public static void mergeSort(int[] source, int left, int right) {
// Choose a separator, i.e. split the input array in half
int delimiter = left + ((right - left) / 2) + 1;
// Execute this function recursively for the two halves (if we can split(
if (delimiter > 0 && right > (left + 1)) {
mergeSort(source, left, delimiter - 1);
mergeSort(source, delimiter, right);
}
// Create a temporary array with the desired size
int[] buffer = new int[right - left + 1];
// Starting from the specified left border, go through each element
int cursor = left;
for (int i = 0; i < buffer.length; i++) {
// We use the delimeter to point to the element from the right side
// If delimeter > right, then there are no unadded elements left on the right side
if (delimiter > right || source[cursor] > source[delimiter]) {
buffer[i] = source[cursor];
cursor++;
} else {
buffer[i] = source[delimiter];
delimiter++;
}
}
System.arraycopy(buffer, 0, source, left, buffer.length);
}
Ayo mbukak conto kanthi nelpon mergeSort(array, 0, array.length-1)
. Nalika sampeyan bisa ndeleng, pet teka mudhun kanggo kasunyatan sing kita njupuk minangka input Uploaded nuduhake awal lan mburi bagean bakal diurutake. Nalika ngurutake diwiwiti, iki minangka wiwitan lan pungkasan saka array. Sabanjure kita ngetung delimiter - posisi divider. Yen divider bisa dibagi dadi 2 bagean, banjur kita nelpon rekursif ngurutake kanggo bagean kang divider dibagi Uploaded. Kita nyiyapake array buffer tambahan sing kita pilih bagean sing diurutake. Sabanjure, kita nyelehake kursor ing wiwitan area sing bakal diurutake lan miwiti ngliwati saben unsur saka array kosong sing wis disiapake lan isi karo unsur sing paling cilik. Yen unsur nuding kursor luwih cilik tinimbang unsur sing ditunjuk dening pembagi, kita nyelehake unsur iki ing buffer array lan mindhah kursor. Yen ora, kita sijine unsur nuding dening separator menyang buffer Uploaded lan mindhah separator. Sanalika pemisah ngluwihi wates wilayah sing diurutake utawa kita ngisi kabeh array, sawetara sing ditemtokake dianggep diurutake. Materi sing gegandhengan:
Ngitung Urut lan Radix Urut
Algoritma ngurutake liyane sing menarik yaiku Counting Sort. Kompleksitas algoritma ing kasus iki bakal dadi O (n + k), ing ngendi n minangka jumlah unsur, lan k minangka nilai maksimum unsur kasebut. Ana siji masalah karo algoritma: kita kudu ngerti nilai minimal lan maksimal ing array. Punika conto implementasine saka counting sort:public static int[] countingSort(int[] theArray, int maxValue) {
// Array with "counters" ranging from 0 to the maximum value
int numCounts[] = new int[maxValue + 1];
// In the corresponding cell (index = value) we increase the counter
for (int num : theArray) {
numCounts[num]++;
}
// Prepare array for sorted result
int[] sortedArray = new int[theArray.length];
int currentSortedIndex = 0;
// go through the array with "counters"
for (int n = 0; n < numCounts.length; n++) {
int count = numCounts[n];
// go by the number of values
for (int k = 0; k < count; k++) {
sortedArray[currentSortedIndex] = n;
currentSortedIndex++;
}
}
return sortedArray;
}
Nalika kita ngerti, iku banget nyenengake nalika kita kudu ngerti ing advance nilai minimal lan maksimum. Lan banjur ana algoritma liyane - Radix Sort. Aku bakal nampilake algoritma ing kene mung kanthi visual. Kanggo implementasine, deleng materi:
Jawa Cepet Urut
Inggih, kanggo panganan cuci mulut - salah siji saka algoritma paling misuwur: Urut cepet. Wis kerumitan algoritma, yaiku, kita duwe O (n log n). Urut iki uga disebut Hoare sort. Sing nggumunake, algoritma kasebut diciptakake dening Hoare nalika dheweke manggon ing Uni Soviet, ing ngendi dheweke sinau terjemahan komputer ing Universitas Moskow lan ngembangake buku frasa Rusia-Inggris. Algoritma iki uga digunakake ing implementasine luwih rumit ing Arrays.sort ing Jawa. Kepiye babagan Collections.sort? Aku suggest sampeyan ndeleng dhewe carane padha diurutake "ing hood". Dadi, kode:public static void quickSort(int[] source, int leftBorder, int rightBorder) {
int leftMarker = leftBorder;
int rightMarker = rightBorder;
int pivot = source[(leftMarker + rightMarker) / 2];
do {
// Move the left marker from left to right while element is less than pivot
while (source[leftMarker] < pivot) {
leftMarker++;
}
// Move the right marker until element is greater than pivot
while (source[rightMarker] > pivot) {
rightMarker--;
}
// Check if you don't need to swap elements pointed to by markers
if (leftMarker <= rightMarker) {
// The left marker will only be less than the right marker if we have to swap
if (leftMarker < rightMarker) {
int tmp = source[leftMarker];
source[leftMarker] = source[rightMarker];
source[rightMarker] = tmp;
}
// Move markers to get new borders
leftMarker++;
rightMarker--;
}
} while (leftMarker <= rightMarker);
// Execute recursively for parts
if (leftMarker < rightBorder) {
quickSort(source, leftMarker, rightBorder);
}
if (leftBorder < rightMarker) {
quickSort(source, leftBorder, rightMarker);
}
}
Kabeh ing kene medeni banget, mula kita bakal ngerti. Kanggo sumber input array int[]
, kita nyetel loro tandha, kiwa (L) lan tengen (R). Nalika disebut pisanan, padha cocog wiwitan lan pungkasan Uploaded. Sabanjure, unsur pendukung ditemtokake, alias pivot
. Sawise iki, tugas kita yaiku mindhah nilai sing kurang saka pivot
, ngiwa pivot
, lan sing luwih gedhe ing sisih tengen. Kanggo nindakake iki, pisanan mindhah pointer L
nganti kita nemokake nilai luwih saka pivot
. Yen nilai sing luwih cilik ora ditemokake, banjurpivot
. Потом двигаем указатель
R
пока не найдём меньшее, чем
pivot
meaning. Если меньшее meaning не нашли, то
R
совпадёт с
pivot
. Далее, если указатель
L
находится до указателя
R
or совпадает с ним, то пытаемся выполнить обмен элементов, если элемент
L
меньше, чем
R
. Далее
L
сдвигаем вправо на 1 позицию,
R
сдвигаем влево на одну позицию. Когда левый маркер
L
окажется за правым маркером
R
это будет означать, что обмен закончен, слева от
pivot
меньшие значения, справа от
pivot
— большие значения. После этого рекурсивно вызываем такую же сортировку для участков массива от начала сортируемого участка до правого маркера и от левого маркера до конца сортируемого участка. Почему от начала до правого? Потому что в конце итерации так и получится, что правый маркер сдвинется настолько, что станет границей части слева. Этот алгоритм более сложный, чем простая sorting, поэтому его лучше зарисовать. Возьмём белый лист бумаги, запишем: 4 2 6 7 3 , а
Pivot
по центру, т.е. число 6. Обведём его в круг. Под 4 напишем
L
, под 3 напишем
R
. 4 меньше чем 6, 2 меньше чем 6. Total,
L
переместился на положение
pivot
, т.к. по условию
L
не может уйти дальше, чем
pivot
. Напишем снова 4 2 6 7 3 , обведём 6 вкруг (
pivot
) и поставим под ним
L
. Теперь двигаем указатель
R
. 3 меньше чем 6, поэтому ставим маркер
R
на цифру 3. Так How 3 меньше, чем
pivot 6
выполняем
swap
, т.е. обмен. Запишем результат: 4 2 3 7 6 , обводим 6 вкруг, т.к. он по прежнему
pivot
. Указатель
L
на цифре 3, указатель
R
на цифре 6. Мы помним, что двигаем указатели до тех пор, пока
L
не зайдём за
R
.
L
двигаем на следующую цифру. Тут хочется разобрать два варианта: если бы предпоследняя цифра была 7 и если бы она была не 7, а 1.
Предпоследня цифра 1: Сдвинули указатель
L
на цифру 1, т.к. мы можем двигать
L
до тех пор, пока указатель
L
указывает на цифру, меньшую чем
pivot
. А вот
R
мы не можем сдвинуть с 6, т.к. R не мы можем двигать только если указатель
R
указывает на цифру, которая больше чем
pivot
.
swap
не делаем, т.к. 1 меньше 6. Записываем положение: 4 2 3 1 6, обводим
pivot 6
.
L
сдвигается на
pivot
и больше не двигается.
R
тоже не двигается. Обмен не производим. Сдвигаем
L
и
R
на одну позицию и подписываем цифру 1 маркером
R
, а
L
получается вне числа. Т.к.
L
вне числа — ничего не делаем, а вот часть 4 2 3 1 выписываем снова, т.к. это наша левая часть, меньшая, чем
pivot 6
. Выделяем новый
pivot
и начинаем всё снова )
Предпоследняя цифра 7: Сдвинули указать
L
на цифру 7, правый указатель не можем двигать, т.к. он уже указывает на pivot. т.к. 7 больше, чем
pivot
, то делаем
swap
. Запишем результат: 4 2 3 6 7, обводим 6 кружком, т.к. он
pivot
. Указатель
L
теперь сдвигается на цифру 7, а указатель
R
сдвигается на цифру 3. Часть от
L
до конца нет смысла сортировать, т.к. там всего 1 элемент, а вот часть от 4 до указателя
R
отправляем на сортировку. Выбираем
pivot
и начинаем всё снова ) Может на первый взгляд показаться, что если расставить много одинаковых с
pivot
значений, это сломает алгоритм, но это не так. Можно напридумывать каверзных вариантов и на бумажке убедиться, что всё правильно и подивиться, How такие простые действия предоставляют такой надёжный механизм. Единственный минус — такая sorting не является стабильной. Т.к. при выполнении обмена одинаковые элементы могут поменять свой порядок, если один из них встретился до
pivot
до того, How другой элемент попал в часть до
pivot
при помощи обмена. Материал:
GO TO FULL VERSION