013 數組的排序方法(升序、降序、冒泡排序法、快速排序法、選擇排序法、直接插入排序法)


首先要知道數組中的排序有升序降序,(這就需要去好好看看數據結構的排序方法原理了)

排序方法對應的有冒泡排序法快速排序法選擇排序法直接插入排序法等方法

我們先搞明白這些排序方法的思想和基本原理,然后再去看代碼應該怎么寫。下面一一介紹。

(一)排序

(1)升序

使用 java.util.Arrays 類中的 sort() 方法對數組進行升序分為以下兩步:

  1. 導入 java.util.Arrays 包。
  2. 使用 Arrays.sort(數組名) 語法對數組進行排序,排序規則是從小到大,即升序

例1:假設在數組 scores 中存放了 5 名學生的成績,現在要實現從低到高排列的功能。在這里使用 Arrays.sort() 方法來實現,具體代碼如下:

public static void main(String[] args) {
    // 定義含有5個元素的數組
    double[] scores = new double[] { 78, 45, 85, 97, 87 };
    System.out.println("排序前數組內容如下:");
    // 對scores數組進行循環遍歷
    for (int i = 0; i < scores.length; i++) {
        System.out.print(scores[i] + "\t");
    }
    System.out.println("\n排序后的數組內容如下:");
    // 對數組進行排序
    Arrays.sort(scores);
    // 遍歷排序后的數組
    for (int j = 0; j < scores.length; j++) {
        System.out.print(scores[j] + "\t");
    }
}

如上述代碼所示,要對一個數組進行升序排列,只需要調用 Arrays.sort() 方法即可。運行后的輸出結果如下所示。

排序前數組內容如下:
78.0    45.0    85.0    97.0    87.0   
排序后的數組內容如下:
45.0    78.0    85.0    87.0    97.0

(2)降序

使用 sort 實現降序有兩種方法,簡單了解即可。

1)利用 Collections.reverseOrder() 方法(Collections 是一個包裝類。大家可以學習《Java Collections類》一節詳細了解):

public static void main(String[] args) {
    Integer[] a = { 9, 8, 7, 2, 3, 4, 1, 0, 6, 5 };    // 數組類型為Integer
    Arrays.sort(a, Collections.reverseOrder());
    for (int arr : a) {
        System.out.print(arr + " ");
    }
}

輸出結果如下:

9 8 7 6 5 4 3 2 1 0

2)實現 Comparator 接口的復寫 compare() 方法,代碼如下:

public class Test {
    public static void main(String[] args) {
        /*
         * 注意,要想改變默認的排列順序,不能使用基本類型(int,double,char)而要使用它們對應的類
         */
        Integer[] a = { 9, 8, 7, 2, 3, 4, 1, 0, 6, 5 };
        // 定義一個自定義類MyComparator的對象
        Comparator cmp = new MyComparator();
        Arrays.sort(a, cmp);
        for (int arr : a) {
            System.out.print(arr + " ");
        }
    }
}
// 實現Comparator接口
class MyComparator implements Comparator<Integer> {
    @Override
    public int compare(Integer o1, Integer o2) {
        /*
         * 如果o1小於o2,我們就返回正值,如果o1大於o2我們就返回負值, 這樣顛倒一下,就可以實現降序排序了,反之即可自定義升序排序了
         */
        return o2 - o1;
    }
}

輸出結果如下所示。

9 8 7 6 5 4 3 2 1 0 

注意:使用以上兩種方法時,數組必須是包裝類型,否則會編譯不通過。

(二)排序方法

(1)冒泡排序法(http://c.biancheng.net/view/927.html

基本原理:

  1. 比較相鄰的元素。如果第一個比第二個大,就交換他們兩個。
  2. 對每一對相鄰元素做同樣的工作,從開始第一對到結尾的最后一對。在這一點,最后的元素應該會是最大的數。  
  3. 針對所有的元素重復以上的步驟,除了最后一個。  
  4. 持續每次對越來越少的元素重復上面的步驟,直到沒有任何一對數字需要比較。

優化:

  • 針對問題:
數據的順序排好之后,冒泡算法仍然會繼續進行下一輪的比較,直到arr.length-1次,后面的比較沒有意義的。
  • 方案:
設置標志位flag,如果發生了交換flag設置為true;如果沒有交換就設置為false。
這樣當一輪比較結束后如果flag仍為false,即:這一輪沒有發生交換,說明數據的順序已經排好,沒有必要繼續進行下去。


特點:冒泡排序的算法比較簡單,排序的結果穩定,但時間效率不太高。

例1:(http://c.biancheng.net/view/6506.html

假設待排序序列為 (5,1,4,2,8),如果采用冒泡排序對其進行升序(由小到大)排序,則整個排序過程如下所示:
1) 第一輪排序,此時整個序列中的元素都位於待排序序列,依次掃描每對相鄰的元素,並對順序不正確的元素對交換位置,整個過程如圖 1 所示。

 

 

 2) 第二輪排序,此時待排序序列只包含前 4 個元素,依次掃描每對相鄰元素,對順序不正確的元素對交換位置,整個過程如圖 2 所示。

 

 

 3) 第三輪排序,此時待排序序列包含前 3 個元素,依次掃描每對相鄰元素,對順序不正確的元素對交換位置,整個過程如圖 3 所示。

 

 

 4) 第四輪排序,此時待排序序列包含前 2 個元素,對其進行冒泡排序的整個過程如圖 4 所示。

 

 

 5) (優化后,這一步不需要了)當進行第五輪冒泡排序時,由於待排序序列中僅剩 1 個元素,無論再進行相鄰元素的比較,因此直接將其並入已排序序列中,此時的序列就認定為已排序好的序列(如圖 5 所示)

 

 

 

例 2

獲取用戶在控制台輸入的 5 個成績信息,將這些成績保存到數組中,然后對數組應用冒泡排序,並輸出排序后的結果,實現步驟如下。

(1) 創建一個 Test24 類文件,在 main() 方法中開始編碼。首先創建 Scanner 類的實例后聲明 double 類型的 score 數組,然后接收用戶在控制台輸入的成績,並保存到元素中。代碼如下:

public static void main(String[] args) {
    Scanner scan = new Scanner(System.in);
    double[] score = new double[5];
    for (int i = 0; i < score.length; i++) {
        System.out.print("請輸入第 " + (i + 1) + " 個成績:");
        score[i] = scan.nextDouble();
    }
}

(2) 在對 score 數組排序之前,首先輸出數組中各個元素的值。代碼如下:

System.out.println("排序前的元素值:");
for(double val:score) {
    System.out.print(val+"\t");
}
System.out.println();

(3) 通過冒泡排序方法實現對 score 數組的排序,在實現時需要借助一個臨時變量。代碼如下:

public static void main(String[] args) {
    System.out.println("通過冒泡排序方法對數組進行排序:");
    for (int i = 0; i < score.length - 1; i++) {
        // 比較相鄰兩個元素,較大的數往后冒泡
        for (int j = 0; j < score.length - 1 - i; j++) {
            if (score[j] > score[j + 1]) {
                double temp = score[j + 1]; // 把第一個元素值保存到臨時變量中
                score[j + 1] = score[j]; // 把第二個元素值轉移到第一個元素變量中
                score[j] = temp; // 把臨時變量(第一個元素的原值)保存到第二個元素中
            }
            System.out.print(score[j] + " "); // 對排序后的數組元素進行輸出
        }
        System.out.print("【");
        for (int j = score.length - 1 - i; j < score.length; j++) {
            System.out.print(score[j] + " ");
        }
        System.out.println("】");
    }
}

(4) 運行前面的代碼進行測試,如下所示。

請輸入第 1 個成績:77
請輸入第 2 個成績:90
請輸入第 3 個成績:68
請輸入第 4 個成績:59
請輸入第 5 個成績:80
排序前的元素值:
77.0    90.0    68.0    59.0    80.0   
通過冒泡排序方法對數組進行排序:
77.0 68.0 59.0 80.0 【90.068.0 59.0 77.0 【80.0 90.059.0 68.0 【77.0 80.0 90.059.0 【68.0 77.0 80.0 90.0 】

(2)快速排序法(http://c.biancheng.net/view/929.html

基本原理:

 

(1)首先設定一個分界值,通過該分界值將數組分成左右兩部分。  
 
(2)將大於或等於分界值的數據集中到數組右邊,小於分界值的數據集中到數組的左邊。此時,左邊部分中各元素都小於或等於分界值,而右邊部分中各元素都大於或等於分界值。
 
(3)然后,左邊和右邊的數據可以獨立排序。對於左側的數組數據,又可以取一個分界值,將該部分數據分成左右兩部分,同樣在左邊放置較小值,右邊放置較大值。右側的數組數據也可以做類似處理。
 
(4)重復上述過程,可以看出,這是一個遞歸定義。通過遞歸將左側部分排好序后,再遞歸排好右側部分的順序。當左、右兩個部分各數據排序完成后,整個數組的排序也就完成了。

例1:利用快速排序法對一數組進行排序

(1) 聲明靜態的 getMiddle() 方法,該方法需要返回一個 int 類型的參數值,在該方法中傳入 3 個參數。代碼如下:

public static int getMiddle(int[] list, int low, int high) {
    int tmp = list[low]; // 數組的第一個值作為中軸(分界點或關鍵數據)
    while (low < high) {
        while (low < high && list[high] > tmp) {
            high--;
        }
        list[low] = list[high]; // 比中軸小的記錄移到低端
        while (low < high && list[low] < tmp) {
            low++;
        }
        list[high] = list[low]; // 比中軸大的記錄移到高端
    }
    list[low] = tmp; // 中軸記錄到尾
    return low; // 返回中軸的位置
}

(2) 創建靜態的 unckSort() 方法,在該方法中判斷 low 參數是否小於 high 參數,如果是則調用 getMiddle() 方法,將數組一分為二,並且調用自身的方法進行遞歸排序。代碼如下

public static void unckSort(int[] list,int low,int high) {
    if(low < high) {
        int middle = getMiddle(list,low,high);    // 將list數組一分為二
        unckSort(list,low,middle-1);    // 對低字表進行遞歸排序
        unckSort(list,middle+1,high);    // 對高字表進行遞歸排序
    }
}

(3) 聲明靜態的 quick() 方法,在該方法中判斷傳入的數組是否為空,如果不為空,則調用 unckSort() 方法進行排序。代碼如下:

public static void quick(int[] str) {
    if(str.length > 0) {
        // 查看數組是否為空
        unckSort(str,0,str.length-1);
    }
}

(4) 在 main() 方法中聲明 int 類型的 number 數組,接着輸出該數組中的元素。然后調用自定義的 quick() 方法進行排序,排序后重新輸出數組中的元素。代碼如下:

int[] number={13,15,24,99,14,11,1,2,3};
System.out.println("排序前:");
for(int val:number) {
    System.out.print(val+" ");
}
quick(number);
System.out.println("\n排序后:");
for(int val:number) {
    System.out.print(val +" ");
}

運行前面的代碼進行測試,輸出結果如下:

排序前:
13 15 24 99 14 11 1 2 3
排序后:
1 2 3 11 13 14 15 24 99

(3)選擇排序法

工作原理:每一次從待排序的數據元素中選出最小(或最大)的一個元素,存放在序列的起始位置,然后,再從剩余未排序元素中繼續尋找最小(大)元素,然后放到已排序序列的末尾。以此類推,直到全部待排序的數據元素排完。

類別

常見的選擇排序可以分為 直接選擇排序(Straight selection sort)、 樹形選擇排序(Tree-type selection sort)以及 堆排序(Heap sort)。
(1) 直接選擇排序。①基本思想。實現思想是每步從排序記錄中選出排序碼最小(最大)的記錄,放在已排序記錄序列的最后(前);②算法特點。直接選擇排序算法n個記錄的文件的直接選擇排序可經過n-1趟直接選擇排序得到有序結果。
(2) 樹形選擇排序。①基本思想。其實現思想是保存先前比較的結果以減少比較次數,是一種不穩定的排序方法。首先對n個記錄的關鍵字進行兩兩比較,然后在n/2個較小者之間再進行兩兩比較,如此重復,直至選出最小的記錄為止。
(3) 堆排序。①基本思想。堆排序是一種樹形選擇排序,是對直接選擇排序的有效改進;②算法描述。從算法描述來看,堆排序需要兩個過程,即建立堆和堆頂與堆的最后一個元素交換位置。所以堆排序有兩個函數組成,一是建堆的滲透函數,二是反復調用滲透函數實現排序的函數;③算法特點。堆排序可通過樹形結構保存部分比較結果,可減少比較次數。但由於建初始堆所需的比較次數較多,所以堆排序不適宜於記錄數較少的文件。
例1:使用選擇排序法重新對 number(該數組中的元素依次是 13、15、 24、99、4 和 1) 中的元素進行排序。
//第一趟選擇排序
13、15、24、1、4、99
//第二趟選擇排序
13、15、4、1、24、99
//第三趟選擇排序
13、1、4、15、24、99
//第四趟選擇排序
4、1、13、15、24、99
//第五趟選擇排序
1、4、13、15、24、99

例2:代碼。利用選擇排序方法通過編程的方式實現對 number 數組的排序,並輸出已排序的數組元素。

int[] number = {13,15,24,99,4,1};
String end = "\n";
int index;
for (int i = 1;i < number.length;i++) {
    index = 0;
    for(int j = 1;j <= number.length-i;j++) {
        if (number[j] > number[index]) {
            index = j;    // 查找最大值
        }
    }
    end = number[index] + " " + end;    // 定位已排好的數組元素
    int temp = number[number.length-i];
    number[number.length-1] = number[index];
    number[index] = temp;
    System.out.print("【");
    for (int j = 0;j < number.length-i;j++) {
        System.out.print(number[j]+" ");
    }
    System.out.print("】"+end);
}

結果:

【13 15 24 1 4 】9913 15 4 1 】24 9913 1 4 】15 24 994 1 】13 15 24 991 】4 13 15 24 99 

(4)直接插入排序法

基本思想:將 n 個有序數存放在數組 a 中,要插入的數為 x,首先確定 x 插在數組中的位置 p,然后將 p 之后的元素都向后移一個位置,空出 a(p),將 x 放入 a(p),這樣可實現插入 x 后仍然有序。

例 1:本例子通過直接插入的方法對上述例子中的 number 數組進行排序。創建一個 Test27 類文件,在 main() 方法中開始編碼,具體實現代碼如下:

public static void main(String[] args) {
    int[] number = { 13, 15, 24, 99, 4, 1 };
    System.out.println("排序前:");
    for (int val : number) { // 遍歷數組元素
        System.out.print(val + " "); // 輸出數組元素
    }
    int temp, j;
    for (int i = 1; i < number.length; i++) {
        temp = number[i];
        for (j = i - 1; j >= 0 && number[j] > temp; j--) {
            number[j + 1] = number[j];
        }
        number[j + 1] = temp;
    }
    System.out.println("\n排序后:");
    for (int val : number) { // 遍歷數組元素
        System.out.print(val + " "); // 輸出數組元素
    }
}

運行結果:

排序前:
13 15 24 99 4 1
排序后:
1 4 13 15 24 99

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM