一、冒泡排序
如圖所示,如果有五個元素需要排序,那麼需要排n-1趟,那麼我們是不是可以寫出一個迴圈出來,
for(int i = 1; i <= size-1; i++)
但是我們發現每一趟的比較次數隨著趟數的增大而減小,所以我們是不是可以 修改一下迴圈呢
for(int i = size-1; i > 0; i--)
這樣不就成了隨著趟數的增大i在減小嗎,現在我們算是寫出了演算法的框框了,我們還要比較元素,有圖上分析可知,比較次數在逐趟減小,並且都是從下標為0開始比較的,於是,還需要一個迴圈來比較元素
for(int j =0; j < ?; j++)
此時,問題來了,臨界值是什麼,“。”是幾呢,再分析一下,第一趟比較下標從0到4,第二趟比較下標從0到3發現小標的變化和i是一樣的,並且可以等於i,於是修改迴圈
for(int j =0; j <= i; j++)
此時感覺很對了,但是仔細想想,這個for迴圈的內容是什麼呢,應該是source[j] 和source[j+1]進行比較,然後交換,如果j可以等於i,那麼j+1不就比i還大麼,所以我們應該把等號去掉,變為
for(int j =0; j < i; j++)
為了使取名有意義,可以修改一下這兩個迴圈
for(int outer = size - 1; outer > 0; outer--){ for(int inner = 0; inner < outer; inner++) {//比較邏輯 int temp = source[inner + 1]; source[inner + 1] = array[inner]; source[inner] = temp; }}完整程式如下所示:
package com.darren.test.sort;public class BubbleSortTest { public static void main(String[] args) { int[] array = new int[] { 3, 5, 9, 8, 6, 4, 3, 1, 8, 7 }; System.out.print("before sort:"); print(array); System.out.println(); int size = array.length; for (int outer = size - 1; outer >= 0; outer--) { for (int inner = 0; inner < outer; inner++) { if (array[inner] > array[inner + 1]) { int temp = array[inner + 1]; array[inner + 1] = array[inner]; array[inner] = temp; } } } System.out.print("after sort:"); print(array); } private static void print(int[] source) { for (Integer data : source) { System.out.print(data); System.out.print(", "); } }}
二、插入排序
如圖所示,如果有五個元素需要排序,那麼需要排n-1趟,並且下標是從1到n-1,那麼我們是不是可以寫出一個迴圈出來
for(int i = 1; i < size; i++)
由圖可知,我們每次都是把source[i]插入到左邊,那麼是不是應該把source[i]臨時儲存一下呢,我想是應該的
int temp = source[i];
那麼演算法內部是如何進行比較插入的呢,接下來進行分析,我們發現一直向左邊插入,其實左邊的序列一直是有序的,那麼我們就會從右向左比較插入就行了,所以第二個for迴圈是倒著的
for(int j = ?; j >=0; j--)
從圖上可知j可以為0,那麼最大可以是多少呢,再分析一下,最大比i小1,所以修改for迴圈
for(int j = i - 1; j >=0; j--)
怎麼比較呢
source[j] 和 source[j+1]進行比較。此時source[j+1]剛好等於source[i],因為j + 1 = i;
把變數取得有意義一點,結果為
for(int outer = 1; outer < size; outer++){ int sentinel = source[i]; for(int inner = outer - 1; inner >= 0; inner--) { if(source[inner] > source[inner + 1]) { source[inner + 1] = source[inner]; source[inner] = sentinel; } }}完整程式如下所示:
package com.darren.test.sort;public class InsertSortTest { public static void main(String[] args) { int[] array = new int[] { 3, 5, 9, 8, 6, 4, 3, 1, 8, 7 }; System.out.print("before sort:"); print(array); System.out.println(); int size = array.length; for (int i = 1; i < size; i++) { int sentinel = array[i]; for (int j = i - 1; sentinel < array[j]; j--) { array[j + 1] = array[j]; array[j] = sentinel; if (j == 0) { break; } } } System.out.print("after sort:"); print(array); } private static void print(int[] source) { for (Integer data : source) { System.out.print(data); System.out.print(", "); } }}
三、選擇排序
如圖所示,如果有五個元素需要排序,宏觀上看需要排n-1趟,並且下標是從0到n-2(注意黑色底線的起始位置),那麼我們是不是可以寫出一個迴圈出來
for(int i = 0; i < size - 1; i++)
第一個for迴圈並不關心具體是怎麼選出一個最小的放左邊的,只是先找到下標的變化規律,接著我們看內部是如何?選出最小值放左邊的,這個實現相對插入排序有所不同,只用圈圈不易表達,所以我們用一個具體例子來看。
對一個五個元素的數組進行排序:3 1 5 2 4
要比較,必須有一個參照物,我們假設每一趟中最左邊元素(黑色底線對應的最左邊元素)為參照物(int minValueIndex = i),然後拿之後的一個元素(int j = i + 1)和它比較,如果比它小,那麼就改變參照物,把新的最小的元素作為參照物,繼續拿它之後的元素與之比較,直到本趟結束,因為要拿j和左邊的元素比較而沒有j + 1,所以j可取到n-1
即
for(int j = i + 1; j < size; j++)
這裡有兩個問題
1.選個元素作為參照物還是元素的下標。
2.每一趟比較完應該做些什麼。
我們先回答第二個問題,每一趟比較完我們應該把最小的元素放到左邊的位置上,需要進行一次元素交換
然後回答第一個問題,我們知道要進行元素交換,如果使用元素作為參照物,那麼我只知道最小值是幾,卻不能和其他元素交換,因為我不知道最小值的下標,但是如果我們知道了最小值的下標,就能知道最小值,所以我們選用下標作為參照物
for(int outer = 0; outer < size - 1; outer++){ // 設定參照物為最左邊元素 int min = outer; for(int inner = outer + 1; inner < size; inner++) { if(source[inner] < source[min]) { // 更新參照物 min = inner; } } // 交換最小值和最左邊值 int temp = source[outer]; source[outer] = source[min]; source[min] = temp;}
完整程式如下所示:
package com.darren.test.sort;public class SelectSortTest { public static void main(String[] args) { int[] array = new int[] { 3, 5, 9, 8, 6, 4, 3, 1, 8, 7 }; System.out.print("before sort:"); print(array); System.out.println(); int size = array.length; for (int outer = 0; outer < size - 1; outer++) { int min = outer; for (int inner = outer + 1; inner < size; inner++) { if (array[inner] < array[min]) { min = inner; } } int temp = array[outer]; array[outer] = array[min]; array[min] = temp; } System.out.print("after sort:"); print(array); } private static void print(int[] source) { for (Integer data : source) { System.out.print(data); System.out.print(", "); } }}
總結:
幾種簡單排序演算法的比較
一般情況下不使用冒泡排序,它過於簡單了,以至於可以毫不費力的寫出來,當資料很小的時候會有些使用價值。
選擇排序雖然把交換次數降到最低,但比較次數仍然很大,當資料量很小,並且交換資料相對於比較資料更耗時的情況下可是使用選擇排序。
大多數情況系,假設資料量比較小或基本有序時,插入排序演算法是三種簡單演算法最好的選擇,對於更大資料量來說,快速排序更好,以後會講快速排序演算法。
除了在速度方面比較演算法外,還有一種對演算法的衡量標準是演算法需要的記憶體空間有多大。這三種簡單演算法除了初始的數組外幾乎不需要其他記憶體空間。所有排序演算法都
需要一個額外的變數來儲存交換時的資料項目。