Java基礎-演算法
演算法
插入排序
有一個已經 有序 的資料序列,要求在這個已經排好的資料序列中插入一個數,但要求插入後此資料序列仍然有序,這個時候就要用到一種新的排序方法——插入排序法,插入排序的基本操作就是將一個數據插入到已經排好序的有序資料中,從而得到一個新的、個數加一的有序資料,演算法適用於少量資料的排序,時間複雜度為 O(n^2)。是穩定的排序方法。
- 從第一個元素開始,該元素可以認為已經被排序;
- 取出下一個元素,在已經排序的元素序列中從後向前掃描;
- 如果該元素(已排序)大於新元素,將該元素移到下一位置;
- 重複步驟 3,直到找到已排序的元素小於或者等於新元素的位置;
- 將新元素插入到該位置後;
- 重複步驟 2 ~ 5,直至最後一個元素。
import java.util.Arrays; public class InsertSort { private static void insertSort(int[] arr) { int j; // 已排序列表下標 int t; // 待排序元素 for (int i = 1; i < arr.length; i++) { if (arr[i] < arr[i - 1]) { t = arr[i]; // 賦值給待排序元素 for (j = i - 1; j >= 0 && arr[j] > t; j--) { arr[j + 1] = arr[j]; // 從後往前遍歷已排序列表,逐個和待排序元素比較,如果已排序元素較大,則將它後移 } arr[j + 1] = t; // 將待排序元素插入到正確的位置 } } } public static void main(String[] args) { int[] ints = {5, 3, 4, 1, 2}; insertSort(ints); System.out.println(Arrays.toString(ints)); } }
氣泡排序
氣泡排序(Bubble Sort),是一種電腦科學領域的較簡單的排序演算法。 它重複地走訪過要排序的元素列, 依次比較兩個相鄰的元素 ,如果他們的順序(如從大到小、首字母從 A 到 Z)錯誤就把他們交換過來。走訪元素的工作是重複地進行直到沒有相鄰元素需要交換,也就是說該元素已經排序完成。 這個演算法的名字由來是因為越大的元素會經由交換慢慢“浮”到數列的頂端(升序或降序排列),就如同碳酸飲料中二氧化碳的氣泡最終會上浮到頂端一樣,故名“氣泡排序”。
- 比較相鄰的元素。如果第一個比第二個大,就交換他們兩個。
- 對每一對相鄰元素作同樣的工作,從開始第一對到結尾的最後一對。這步做完後,最後的元素會是最大的數。
- 針對所有的元素重複以上的步驟,除了最後一個。
- 持續每次對越來越少的元素重複上面的步驟,直到沒有任何一對數字需要比較。
import java.util.Arrays;
public class BubbleSort {
public static void sort(int[] arr) {
for (int i = 0; i < arr.length-1; i++) {
for (int j = 0; j < arr.length - i - 1; j++) {
//如果當前元素比後一位元素大 交換位置
if (arr[j] > arr[j + 1]) {
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
public static void main(String[] args) {
int[] ints = {5, 3, 4, 1, 2};
sort(ints);
System.out.println(Arrays.toString(ints));
}
}
歸併排序
歸併排序(MERGE-SORT)是建立在歸併操作上的一種有效的排序演算法,該演算法是採用分治法(
Divide and Conquer
)的一個非常典型的應用。將已有序的子序列合併,得到完全有序的序列;即先使每個子序列有序,再使子序列段間有序。若將兩個有序表合併成一個有序表,稱為二路歸併。
- 申請空間,使其大小為兩個已經排序序列之和,該空間用來存放合併後的序列
- 設定兩個指標,最初位置分別為兩個已經排序序列的起始位置
- 比較兩個指標所指向的元素,選擇相對小的元素放入到合併空間,並移動指標到下一位置
- 重複步驟 3 直到某一指標到達序列尾
- 將另一序列剩下的所有元素直接複製到合併序列尾
import java.util.Arrays;
public class MergeSort {
public static void mergeSort(int[] arrays, int left, int right) {
// 如果陣列還可以拆分
if (left < right) {
//陣列的中間位置
int middle = (left + right) / 2;
//拆分左邊陣列
mergeSort(arrays, left, middle);
//拆分右邊陣列
mergeSort(arrays, middle + 1, right);
//合併
merge(arrays, left, middle, right);
}
}
/**
* 合併陣列
*/
public static void merge(int[] arr, int left, int middle, int right) {
//申請合併空間 大小為兩個已經排序序列之和
int[] temp = new int[right - left + 1];
//i 和 j為兩個已經排好序的陣列的起始位置
int i = left;
int j = middle + 1;
int k = 0;
//排序
while (i <= middle && j <= right) {
//將比較小的陣列放入合併空間
if (arr[i] < arr[j]) {
temp[k++] = arr[i++];
} else {
temp[k++] = arr[j++];
}
}
//將左邊剩餘元素寫入合併空間
while (i <= middle) {
temp[k++] = arr[i++];
}
//將右邊剩餘的元素寫入合併空間
while (j <= right) {
temp[k++] = arr[j++];
}
//將排序後的陣列寫回原來的陣列
for (int l = 0; l < temp.length; l++) {
arr[l + left] = temp[l];
}
}
public static void main(String[] args) {
int[] ints = {5, 3, 4, 1, 2};
mergeSort(ints,0,ints.length-1);
System.out.println(Arrays.toString(ints));
}
}
快速排序
快速排序(英語:Quicksort),又稱劃分交換排序(partition-exchange sort),簡稱快排,也是一種排序演算法。最早由東尼·霍爾提出。在平均狀況下,排序 n 個專案要 O(nlogn) 次比較。在最壞狀況下則需要 O(n^2) 次比較,但這種狀況並不常見。事實上,快速排序 O(nlogn) 通常明顯比其他演算法更快,因為它的內部迴圈(inner loop)可以在大部分的架構上很有效率地達成。
- 從數列中挑出一個元素,稱為“基準”(pivot),
- 重新排序數列,所有比基準值小的元素擺放在基準前面,所有比基準值大的元素擺在基準後面(相同的數可以到任何一邊)。在這個分割結束之後,該基準就處於數列的中間位置。這個稱為分割(partition)操作。
- 遞迴地(recursively)把小於基準值元素的子數列和大於基準值元素的子數列排序。
- 遞迴到最底部時,數列的大小是零或一,也就是已經排序好了。這個演算法一定會結束,因為在每次的迭代(iteration)中,它至少會把一個元素擺到它最後的位置去。
import java.util.Arrays;
public class QuickSort {
public static void sort(int[] arr, int head, int tail) {
if (head >= tail || arr == null || arr.length <= 1) {
return;
}
//設定陣列的起始位置 i 結束位置j 基準 pivot 為陣列的中間
int i = head, j = tail, pivot = arr[(head + tail) / 2];
while (i <= j) {
//當陣列小於基準 迴圈結束後 相當於i所處的位置的值為大於基準的元素
while (arr[i] < pivot) {
++i;
}
//當陣列大於基準 迴圈結束後 相當於j所處的位置的值為小於於基準的元素
while (arr[j] > pivot) {
--j;
}
//如果i<j 那麼則將互動i j對應位置的值
if (i < j) {
int t = arr[i];
arr[i] = arr[j];
arr[j] = t;
//將指標繼續移動
++i;
--j;
} else if (i == j) {
//如果i=j 那麼說明本次排序已經結束 將i++ 如果這裡不使用i++ 那麼後面的sort(arr,i,tail)將改為arr(arr,i+1,tail)
++i;
}
}
//繼續將陣列分割
sort(arr, head, j);
sort(arr, i, tail);
}
public static void main(String[] args) {
int[] ints = {5, 3, 4, 1, 2};
sort(ints, 0, ints.length - 1);
System.out.println(Arrays.toString(ints));
}
}
線性搜尋
指按一定的順序檢查陣列中每一個元素,直到找到所要尋找的特定值為止。是最簡單的一種搜尋演算法。
public class LinearSearch {
public static void main(String[] args) {
int[] ints = {5, 3, 4, 1, 2};
System.out.println(search(ints, 4));
}
public static int search(int[] arr, int key) {
//迴圈
for (int i = 0; i < arr.length; i++) {
//比較是否等於key
if (arr[i] == key) {
return arr[i];
}
}
//找不到就返回-1
return -1;
}
}
二分查詢
是一種在有序陣列中查詢某一特定元素的搜尋演算法。搜尋過程從陣列的中間元素開始,如果中間元素正好是要查詢的元素,則搜尋過程結束;如果某一特定元素大於或者小於中間元素,則在陣列大於或小於中間元素的那一半中查詢,而且跟開始一樣從中間元素開始比較。如果在某一步驟陣列為空,則代表找不到。這種搜尋演算法每一次比較都使搜尋範圍縮小一半。
public class BinarySearch {
public static int search(int[] arr, int key) {
int low = 0;
int high = arr.length - 1;
while (low <= high) {
int middle = (high + low) / 2;
//如果相等 返回值
if (key == arr[middle]) {
return key;
} else if (key < arr[middle]) {
//如果key小於中間值,那麼改變high,值可能在左邊部(比較小的部分)
high = middle - 1;
}else {
//如果key大於中間值,那麼改變low,值可能在右邊部(比較大的部分)
low = middle + 1;
}
}
return -1;
}
public static void main(String[] args) {
int[] ints = {1, 2, 3, 4, 5};
System.out.println(search(ints, 4));
}
}