Java經典演算法詳解-不來看看可惜咯
正在學Java的各位這幾種Java程式設計中的經典演算法千萬不要錯過,演算法在Java開發中會起到非常重要作用,瞭解這些常用的演算法會讓你在工作中達到事半功倍的效果!
看看今天為大家整理經典演算法詳解:
一、插入排序
這是一個很好的理解打麻將或撲克。例如,如果左手中有一套卡1、2、4、7和一張3,則從右向左將此卡插入2、4是來驗證否正確。
一次插入排序的操作過程:
將待插元素,依次與已排序好的子數列元素從後到前進行比較,如果當前元素值比待插元素值大,則將移位到與其相鄰的後一個位置,否則直接將待插元素插入當前元素相鄰的後一位置,因為說明已經找到插入點的最終位置:
public class InsertSort { public static void sort(int[] arr) { if (arr.length >= 2) { for (int i = 1; i < arr.length; i++) { //挖出一個要用來插入的值,同時位置上留下一個可以存新的值的坑 int x = arr[i]; int j = i - 1; //在前面有一個或連續多個值比x大的時候,一直迴圈往前面找,將x插入到這串值前面 while (j >= 0 && arr[j] > x) { //當arr[j]比x大的時候,將j向後移一位,正好填到坑中 arr[j + 1] = arr[j]; j--; } //將x插入到最前面 arr[j + 1] = x; } } } }
二、選擇排序
選擇排序的基本思想是遍歷陣列的過程中,以 i 代表當前需要排序的序號,則需要在剩餘的 [i…n-1] 中找出其中的最小值,然後將找到的最小值與 i 指向的值進行交換。因為每一趟確定元素的過程中都會有一個選擇最大值的子流程,所以人們形象地稱之為選擇排序。
如下示例:
初始值為: [38, 17, 16, 16, 7, 31, 39, 32, 2, 11]
i = 0: [2 , 17, 16, 16, 7, 31, 39, 32, 38 , 11] (0th [38]<->8th [2])
i = 1: [2, 7 , 16, 16, 17 , 31, 39, 32, 38, 11] (1st [38]<->4th [17])
i = 2: [2, 7, 11 , 16, 17, 31, 39, 32, 38, 16 ] (2nd [11]<->9th [16])
i = 3: [2, 7, 11, 16, 17, 31, 39, 32, 38, 16] ( 無需交換 )
i = 4: [2, 7, 11, 16, 16 , 31, 39, 32, 38, 17 ] (4th [17]<->9th [16])
i = 5: [2, 7, 11, 16, 16, 17 , 39, 32, 38, 31 ] (5th [31]<->9th [17])
i = 6: [2, 7, 11, 16, 16, 17, 31 , 32, 38, 39 ] (6th [39]<->9th [31])
i = 7: [2, 7, 11, 16, 16, 17, 31, 32, 38, 39] ( 無需交換 )
i = 8: [2, 7, 11, 16, 16, 17, 31, 32, 38, 39] ( 無需交換 )
i = 9: [2, 7, 11, 16, 16, 17, 31, 32, 38, 39] ( 無需交換 )
由例子可以看出,選擇排序隨著排序的進行( i 逐漸增大),比較的次數會越來越少,但是不論陣列初始是否有序,選擇排序都會從 i 至陣列末尾進行一次選擇比較,所以給定長度的陣列,選擇排序的比較次數是固定的: 1 + 2 + 3 + …. + n = n * (n + 1) / 2 ,而交換的次數則跟初始陣列的順序有關,如果初始陣列順序為隨機,則在最壞情況下,陣列元素將會交換 n 次,最好的情況下則可能 0 次(陣列本身即為有序)。
程式碼示例:
*
* Selection Sorting
*/
SELECTION(new Sortable() {
public <T extends Comparable<T>> void sort(T[] array, boolean ascend) {
int len = array.length;
for (int i = 0; i < len; i++) {
int selected = i;
for (int j = i + 1; j < len; j++) {
int compare = array[j].compareTo(array[selected]);
if (compare != 0 && compare < 0 == ascend) {
selected = j;
}
}
exchange(array, i, selected);
}
}
})
三、快速排序法
簡單的說, 就是設定一個標準值, 將大於這個值的放到右邊(不管排序), 將小於這個值的放到左邊(不管排序), 那麼這樣只是區分了左小右大, 沒有排序, 沒關係, 左右兩邊再重複這個步驟.直到不能分了為止.
程式碼示例:
public class QuickSort {
public static void sort(int[] arr,int begin,int end) {
//先定義兩個引數接收排序起始值和結束值
int a = begin;
int b = end;
//先判斷a是否大於b
if (a >= b) {
//沒必要排序
return;
}
//基準數,預設設定為第一個值
int x = arr[a];
//迴圈
while (a < b) {
//從後往前找,找到一個比基準數x小的值,賦給arr[a]
//如果a和b的邏輯正確--a<b ,並且最後一個值arr[b]>x,就一直往下找,直到找到後面的值大於x
while (a < b && arr[b] >= x) {
b--;
}
//跳出迴圈,兩種情況,一是a和b的邏輯不對了,a>=b,這時候排序結束.二是在後面找到了比x小的值
if (a < b) {
//將這時候找到的arr[b]放到最前面arr[a]
arr[a] = arr[b];
//排序的起始位置後移一位
a++;
}
//從前往後找,找到一個比基準數x大的值,放在最後面arr[b]
while (a < b && arr[a] <= x) {
a++;
}
if (a < b) {
arr[b] = arr[a];
//排序的終止位置前移一位
b--;
}
}
//跳出迴圈 a < b的邏輯不成立了,a==b重合了,此時將x賦值回去arr[a]
arr[a] = x;
//呼叫遞迴函式,再細分再排序
sort(arr,begin,a-1);
sort(arr,a+1,end);
}
}
四、氣泡排序 基礎版
每個冒泡過程從序列的第一個元素開始,然後依次將其與其餘元素進行比較。與佇列一樣,兩個相鄰元素的大小之比從左到右改變高和低的位置。最後,最高(最大值)必須排在後面
但是這只是把最高的一個放在後面,我們必須找到第二個最高的,所以我們比較第一個的兩個,高的一個向後,然後第二個最高的一個落後
然後是第三高的再往後排…
程式碼示例:
public class MaoPao {
public static void sort(int[] arr){
for (int i = 1; i < arr.length; i++) { //第一層for迴圈,用來控制冒泡的次數
for (int j = 0; j < arr.length-1; j++) { //第二層for迴圈,用來控制冒泡一層層到最後
//如果前一個數比後一個數大,兩者調換 ,意味著泡泡向上走了一層
if (arr[j] > arr[j+1] ){
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
}
}
五、氣泡排序 進階版
補充、改進,參見下面測試結果發現提升並不大,這是正常的,因為改進的一步是省略了成功後的判斷,甚至如果沒有改進,成功後的排序和只是陣列遍歷,沒有資料更新,我們知道讀取陣列快更新慢,所以它看起來並沒有比之前的版本有多大的改進
在這個版本中,改動了兩點分別是:
第一點是加入了一個布林值,判斷第二層迴圈中的調換有沒有執行,如果沒有進行兩兩調換,說明後面都已經排好序了,已經不需要再迴圈了,直接跳出迴圈,排序結束.
第二點是第二層迴圈不再迴圈到arr.length - 1,因為外面的i迴圈遞增一次,說明陣列最後就多了一個排好序的大泡泡.第二層迴圈也就不需要到最末尾一位了,可以提前結束迴圈
程式碼示例:
/**
* 進階氣泡排序
* 加入一個布林變數,如果內迴圈沒有交換值,說明已經排序完成,提前終止
* @param arr
*/
public static void sortPlus(int[] arr){
if(arr != null && arr.length > 1){
for(int i = 0; i < arr.length - 1; i++){
// 初始化一個布林值
boolean flag = true;
for(int j = 0; j < arr.length - i - 1 ; j++){
if(arr[j] > arr[j+1]){
// 調換
int temp;
temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
// 改變flag
flag = false;
}
}
if(flag){
break;
}
}
}
}
六、希爾排序
希爾排序的誕生是由於插入排序在處理大規模陣列的時候會遇到需要移動太多元素的問題。
希爾排序的思想是將一個大陣列劃分為幾個較小的陣列,按間gap劃分,例如陣列[1,2,3,4,5,6,7,8]。如果 gap = 2,可以將其劃分為兩個陣列[1,3,5,7]和[2,4,6,8](對應的,如gap = 3,被劃分的陣列為[1,4,7],[2,5,8],[3,6]),然後分別對被劃分的陣列插入排序。對每個子陣列進行排序後,減小gap值,重複前面的步驟,直到gap = 1,即對整個陣列插入排序。此時,陣列幾乎已經排好序了,所以需要移動的元素非常非常小,這就解決了在處理大型陣列時插入排序需要更多移動的問題。
具體例項請參照插入排序。
希爾排序是插入排序的改進版本,在資料量較大的情況下,Hill排序有助於提高效率。當資料量很小時,建議直接使用插入排序。
程式碼示例:
/**
* Shell Sorting
*/
SHELL(new Sortable() {
public <T extends Comparable<T>> void sort(T[] array, boolean ascend) {
int length = array.length;
int gap = 1;
// use the most next to length / 3 as the first gap
while (gap < length / 3) {
gap = gap * 3 + 1;
}
while (gap >= 1) {
for (int i = gap; i < length; i++) {
T next = array[i];
int j = i;
while (j >= gap) {
int compare = array[j - gap].compareTo(next);
// already find its position
if (compare == 0 || compare < 0 == ascend) {
break;
}
array[j] = array[j - gap];
j -= gap;
}
if (j != i) {
array[j] = next;
}
}
gap /= 3;
}
}
})
順便在列據出幾道經典的演算法面試題:
第一
問題:有一對兔子。從出生後的第三個月開始,他們每月生一對兔子。當小兔子長到第三個月時,它們每個月都會產下另一對兔子。如果兔子沒有死,每個月兔子的總數是多少?
程式碼演示:
//這是一個菲波拉契數列問題
public class test01 {
public static void main(String[] args) {
int f1=1,f2=1,f;
int M=30;
System.out.println(f1);
System.out.println(f2);
for(int i=3;i<M;i++) {
f=f2;
f2=f1+f2;
f1=f;
System.out.println(f2);
}
}
}
第二
問題:判斷101-200之間有多少個素數,並輸出所有素數。
程式分析:判斷素數的方法:用一個數分別去除2到sqrt(這個數),如果能被整除, 則表明此數不是素數,反之是素數。
public class test02 {
public static void main(String[] args) {
int count=0;
for(int i=101;i<200;i+=2) {
boolean flag=true;
for(int j=2;j<=Math.sqrt(i);j++) {
if(i%j==0) {
flag=false;
break;
}
}
if(flag==true) {
count++;
System.out.println(i);
}
}
System.out.println(count);
}
}
第三
問題:打印出所有的 "水仙花數 ",所謂 "水仙花數 "是指一個三位數,其各位數字立方和等於該數本身。例如:153是一個 "水仙花數 ",因為153=1的三次方+5的三次方+3的三次方。
public class test03 {
public static void main(String[] args) {
int a,b,c;
for(int i=101;i<1000;i++) {
a=i%10;
b=i/10%10;
c=i/100;
if(a*a*a+b*b*b+c*c*c==i)
System.out.println(i);
}
}
}
第四:
問題:將正整數分解為素因子。例如,輸入90並列印90=2*3*3*5。
程式分析:要分解N的素數因子,首先找到一個最小素數k,然後完成以下步驟:
(1) 如果素數正好等於N,則表示分解素數因子的過程已經結束,您可以將其打印出來。
(2) 如果n>k,但n可以被k除,則打印出k的值,將n除以k的商作為新的正整數,然後重複第一步。
(3) 如果n不能除以K,則以K+1作為K的值重複第一步。
import java.util.Scanner;
public class test04 {
public static void main(String[] args) {
Scanner input=new Scanner(System.in);
int n=input.nextInt();
int k=2;
while(n>=k) {
if(n==k) {
System.out.println(k);
break;
}else if (n%k==0) {
System.out.println(k);
n=n/k;
}else {
k++;
}
}
}
}
第五:
問題:使用條件運算子巢狀完成此問題:學業成績>=90分的學生用a表示,60-89分的學生用B表示,60分以下的學生用C表示。
import java.util.Scanner;
public class test05 {
public static void main(String[] args) {
Scanner input=new Scanner(System.in);
int score=input.nextInt();
char grade=score>=90?'A':score>=60?'B':'C';
System.out.println(grade);
}
}
第六:
問題:輸入一行字元,分別統計出其中英文字母、空格、數字和其它字元的個數。
import java.util.Scanner;
public class test07 {
public static void main(String[] args) {
int abccount=0;
int spacecount=0;
int numcount=0;
int othercount=0;
Scanner input=new Scanner(System.in);
String toString=input.nextLine();
char [] ch=toString.toCharArray();
for(int i=0;i<ch.length;i++) {
if(Character.isLetter(ch[i])) {
abccount++;
}else if(Character.isDigit(ch[i])) {
numcount++;
}else if(Character.isSpaceChar(ch[i])){
spacecount++;
}else {
othercount++;
}
}
System.out.println(abccount);
System.out.println(spacecount);
System.out.println(numcount);
System.out.println(othercount);
}
}
以上就本次的全部內容, 文章內容部分摘抄與網路,資料共享時代為正在學習的你,新增一份提醒
歷史文章推薦: