JavaRush /Blog Jawa /Random-JV /Algoritma ngurutake ing teori lan praktik
Viacheslav
tingkat

Algoritma ngurutake ing teori lan praktik

Diterbitake ing grup
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?
Algoritma ngurutake ing teori lan praktik - 1

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 loop for. 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."
Algoritma ngurutake ing teori lan praktik - 2

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:
Algoritma ngurutake ing teori lan praktik - 3

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:
Algoritma ngurutake ing teori lan praktik - 4

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:
Algoritma ngurutake ing teori lan praktik - 5

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:
Algoritma ngurutake ing teori lan praktik - 6

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:
Algoritma ngurutake ing teori lan praktik - 7

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:
Algoritma ngurutake ing teori lan praktik - 8

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:
Algoritma ngurutake ing teori lan praktik - 9
Bahan:
Algoritma ngurutake ing teori lan praktik - 10

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 Lnganti kita nemokake nilai luwih saka pivot. Yen nilai sing luwih cilik ora ditemokake, banjur L совпадёт с pivot. Потом двигаем указатель 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 при помощи обмена. Материал:

Итог

Выше мы рассмотрели "джентельменский" набор алгоритмов сортировки, реализованных на Java. Алгоритмы вообще штука полезная, How с прикладной точки зрения, так и с точки зрения развития мышления. Некоторые из них попроще, некоторые посложнее. По Howим-то умные люди защищали различные работы на степени, а по другим писали толстые-толстые книги. Надеюсь, приложенный к статье материал позволит вам узнать ещё больше, так How это обзорная статья, которая и так получилась увесистой. И цель её — дать небольшое вступление. Про введение в алгоритмы можно так же прочитать ознакомиться с книгой " Грокаем Алгоримы". Также мне нравится плэйлист от Jack Brown — AQA Decision 1 1.01 Tracing an Algorithm. Ради интереса можно посмотреть на визуализацию алгоритмов на sorting.at и visualgo.net. Ну и весь Youtube к вашим услугам.
Komentar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION