1. 程式人生 > >java 八大排序演算法

java 八大排序演算法

文章目錄

常用的排序演算法主要包括:
  1. 插入排序   直接插入排序   希爾排序
  2. 交換排序   氣泡排序   快速排序
  3. 選擇排序   簡單選擇排序   堆排序
  4. 歸併排序
  5. 基數排序

交換排序

氣泡排序

氣泡排序是最簡單的一種排序演算法。

  • 思想 在要排序的一組數中,對當前還未排好序的範圍內的全部數,自上而下對相鄰的兩個數依次進行比較和調整,讓較大的數往下沉,較小的往上冒。即:每當兩相鄰的數比較後發現它們的排序與排序要求相反時,就將它們互換。 一句話概括:每遍歷一次,就把最大的元素放在最後。
  • 演算法實現
package sort;

/**
 * 氣泡排序
 * 思路:
 * 依次比較相鄰的兩個數,將小數放在前面,大數放在後面。即在第一趟:首先比較第1個和第2個數,將小數放前,大數放後。然後比較第2個數和第3個數,將小數放前,大數放後,如此繼續,直至比較最後兩個數,將小數放前,大數放後。重複第一趟步驟,直至全部排序完成。
 * 第一趟比較完成後,最後一個數一定是陣列中最大的一個數,所以第二趟比較的時候最後一個數不參與比較;
 * 第二趟比較完成後,倒數第二個數也一定是陣列中第二大的數,所以第三趟比較的時候最後兩個數不參與比較;
 * 依次類推,每一趟比較次數-1;
 * ……
 */
import java.util.Arrays; public class BubbleSort { public static void bubbleSort(int[] arr){ int temp=0; for(int i=0;i<arr.length-1;i++) { for(int j=0;j<arr.length-1-i;j++) { if(arr[j]>arr[j+1]) { temp=arr[j+1]; arr[j+1]=arr[j]; arr[j]=temp; } } System.out.
println("第["+(i+1)+"]輪,排序結果:"+Arrays.toString(arr)); } } public static void main(String[] args) { int arr[]= {65,58,95,10,57,62,13,106,78,23,85}; System.out.println("排序前:"+Arrays.toString(arr)); System.out.println("-------------------------------------------------------");
bubbleSort(arr); System.out.println("-------------------------------------------------------"); System.out.println("排序後:"+Arrays.toString(arr)); } }
  • 排序結果
排序前:[65, 58, 95, 10, 57, 62, 13, 106, 78, 23, 85]
-------------------------------------------------------[1]輪,排序結果:[58, 65, 10, 57, 62, 13, 95, 78, 23, 85, 106][2]輪,排序結果:[58, 10, 57, 62, 13, 65, 78, 23, 85, 95, 106][3]輪,排序結果:[10, 57, 58, 13, 62, 65, 23, 78, 85, 95, 106][4]輪,排序結果:[10, 57, 13, 58, 62, 23, 65, 78, 85, 95, 106][5]輪,排序結果:[10, 13, 57, 58, 23, 62, 65, 78, 85, 95, 106][6]輪,排序結果:[10, 13, 57, 23, 58, 62, 65, 78, 85, 95, 106][7]輪,排序結果:[10, 13, 23, 57, 58, 62, 65, 78, 85, 95, 106][8]輪,排序結果:[10, 13, 23, 57, 58, 62, 65, 78, 85, 95, 106][9]輪,排序結果:[10, 13, 23, 57, 58, 62, 65, 78, 85, 95, 106][10]輪,排序結果:[10, 13, 23, 57, 58, 62, 65, 78, 85, 95, 106]
-------------------------------------------------------
排序後:[10, 13, 23, 57, 58, 62, 65, 78, 85, 95, 106]
  • 優化思路 加入一標誌性變數exchange,用於標誌某一趟排序過程中是否有資料交換,如果進行某一趟排序時並沒有進行資料交換,則說明資料已經按要求排列好,可立即結束排序,避免不必要的比較過程。
  • 優化程式碼
package sort;
import java.util.Arrays;

public class BubbleSort {
	public static void bubbleSort(int[] arr){
		int temp=0;
		int flag=0;
		for(int i=0;i<arr.length-1;i++) {
			flag=0;//是否進行了交換的識別符號
			for(int j=0;j<arr.length-1-i;j++) {
				if(arr[j]>arr[j+1]) {
					temp=arr[j+1];
					arr[j+1]=arr[j];
					arr[j]=temp;
					flag=1;
				}
			}
			if(flag==0) {
		         break;
		    }
			System.out.println("第["+(i+1)+"]輪,排序結果:"+Arrays.toString(arr));
		}
	}
	
	public static void main(String[] args) {

	      //int arr[]= {65,58,95,10,57,62,13,106,78,23,85};
		  int arr[]= {11,22,33,44,55,66};
	      System.out.println("排序前:"+Arrays.toString(arr));
	      System.out.println("-------------------------------------------------------");
	      bubbleSort(arr);
	      System.out.println("-------------------------------------------------------");
	      System.out.println("排序後:"+Arrays.toString(arr));

	}
}
  • 排序結果
排序前:[11, 22, 33, 44, 55, 66]
-------------------------------------------------------
-------------------------------------------------------
排序後:[11, 22, 33, 44, 55, 66]

快速排序

本質上來看,快速排序應該算是在氣泡排序基礎上的遞迴分治法。

  • 思想 快速排序使用分治策略來把一個序列分為兩個子序列。步驟為:

    從數列中挑出一個元素,稱為"基準"。 重新排序數列,所有比基準值小的元素擺放在基準前面,所有比基準值大的元素擺在基準後面(相同的數可以到任一邊)。在這個分割槽結束之後,該基準就處於數列的中間位置。這個稱為分割槽操作。 遞迴地把小於基準值元素的子數列和大於基準值元素的子數列排序。 遞迴到最底部時,數列的大小是0或1,也就是已經排序好了。這個演算法一定會結束,因為在每次的迭代中,它至少會把一個元素擺到它最後的位置去。

  • 演算法實現

package sort;

import java.util.Arrays;

/**
 * 快速排序
 * 思路:
 * 選擇一個關鍵值作為基準值。比基準值小的都在左邊序列(一般是無序的),比基準值大的都在右邊(一般是無序的)。一般選擇序列的第一個元素。
 * 一次迴圈:從後往前比較,用基準值和最後一個值比較,如果比基準值小的交換位置,如果沒有繼續比較下一個,直到找到第一個比基準值小的值才交換。
 * 找到這個值之後,又從前往後開始比較,如果有比基準值大的,交換位置,如果沒有繼續比較下一個,直到找到第一個比基準值大的值才交換。
 * 直到從前往後的比較索引>從後往前比較的索引,結束第一次迴圈,此時,對於基準值來說,左右兩邊就是有序的了。
 * 接著分別比較左右兩邊的序列,重複上述的迴圈。
 * 
 * @author 54060
 *
 */
public class QuickSort {

	   public static void quickSort(int [] arr,int left,int right) {

	      int pivot=0;

	      if(left<right) {

	         pivot=partition(arr,left,right);

	         quickSort(arr,left,pivot-1);

	         quickSort(arr,pivot+1,right);

	      }

	   }

	 

	   private static int partition(int[] arr,int left,int right) {

	      int key=arr[left];

	      while(left<right) {

	         while(left<right && arr[right]>=key) {

	            right--;

	         }

	         arr[left]=arr[right];

	         while(left<right && arr[left]<=key) {

	            left++;

	         }

	         arr[right]=arr[left];

	      }

	      arr[left]=key;

	      return left;

	   }

	  

	   public static void main(String[] args) {

	      int arr[]= {65,58,95,10,57,62,13,106,78,23,85};

	      System.out.println("排序前:"+Arrays.toString(arr));

	      quickSort(arr,0,arr.length-1);

	      System.out.println("排序後:"+Arrays.toString(arr));

	   }

	}
  • 排序結果
排序前:[65, 58, 95, 10, 57, 62, 13, 106, 78, 23, 85]
排序後:[10, 13, 23, 57, 58, 62, 65, 78, 85, 95, 106]

選擇排序

直接選擇排序

  • 思路 首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然後,再從剩餘未排序元素中繼續尋找最小(大)元素,然後放到已排序序列的末尾。以此類推,直到所有元素均排序完畢。
  • 演算法實現
package sort;

import java.util.Arrays;

/**
 * 選擇排序
 * 1.找到陣列中最小的那個元素
 * 2.將最小的這個元素和陣列中第一個元素交換位置
 * 3.在剩下的元素中找到最小的的元素,與陣列第二個元素交換位置
 * 重複以上步驟,即可以得到有序陣列。
 * @author 54060
 *
 */
public class SelectionSort {
	//{ 5,3,6,2,10 }->{2,3,6,5,10}->{2,3,6,5,10}->{2,3,5,6,10}->{2,3,5,6,10}
    public static void selectionSort(int[] arr) {
        int n = arr.length;
        for (int i = 0; i < n; i++) {
            int k = i;
            // 找出最小值的小標
            for (int j = i + 1; j < n; j++) {
                if (arr[j] < arr[k]) {
                    k = j;
                }
            }
            // 將最小值放到排序序列開頭(調換位置)
            if (k > i) {//如果第i個就是最小的,則不用交換了
                int tmp = arr[i];
                arr[i] = arr[k];
                arr[k] = tmp;
            }
        }
    }
 
    public static void main(String[] args) {
        int[] arr = {5,3,6,2,10};

        System.out.println("排序前:"+Arrays.toString(arr));
        selectionSort(arr);
        System.out.println("排序後:"+Arrays.toString(arr));
    }
}
  • 排序結果
排序前:[5, 3, 6, 2, 10]
排序後:[2, 3, 5, 6, 10]

堆排序

  • 堆的定義 n個元素的序列{k1,k2,…,kn}當且僅當滿足下列關係之一時,稱之為堆。 情形1:ki <= k2i 且ki <= k2i+1 (最小化堆或小頂堆) 情形2:ki >= k2i 且ki >= k2i+1 (最大化堆或大頂堆) 其中i=1,2,…,n/2向下取整; 若將和此序列對應的一維陣列(即以一維陣列作此序列的儲存結構)看成是一個完全二叉樹,則堆的含義表明,完全二叉樹中所有非終端結點的值均不大於(或不小於)其左、右孩子結點的值。 由此,若序列{k1,k2,…,kn}是堆,則堆頂元素(或完全二叉樹的根)必為序列中n個元素的最小值(或最大值)。
  • 堆排序 初始時把要排序的n個數的序列看作是一棵順序儲存的二叉樹(一維陣列儲存二叉樹),調整它們的儲存序,使之成為一個堆,將堆頂元素輸出,得到n 個元素中最小(或最大)的元素,這時堆的根節點的數最小(或者最大)。然後對前面(n-1)個元素重新調整使之成為堆,輸出堆頂元素,得到n 個元素中次小(或次大)的元素。依此類推,直到只有兩個節點的堆,並對它們作交換,最後得到有n個節點的有序序列。稱這個過程為堆排序。
  • 堆排序的實現 因此,實現堆排序需解決兩個問題:
  1. 如何將n 個待排序的數建成堆;
  2. 輸出堆頂元素後,怎樣調整剩餘n-1 個元素,使其成為一個新堆。

首先討論第2個問題:輸出堆頂元素後,對剩餘n-1元素重新建成堆的調整過程。 調整小頂堆的方法:

1)設有m 個元素的堆,輸出堆頂元素後,剩下m-1 個元素。將堆底元素送入堆頂((最後一個元素與堆頂進行交換),堆被破壞,其原因僅是根結點不滿足堆的性質。

2)將根結點與左、右子樹中較小元素的進行交換。

3)若與左子樹交換:如果左子樹堆被破壞,即左子樹的根結點不滿足堆的性質,則重複方法 (2).

4)若與右子樹交換,如果右子樹堆被破壞,即右子樹的根結點不滿足堆的性質。則重複方法 (2).

5)繼續對不滿足堆性質的子樹進行上述交換操作,直到葉子結點,堆被建成。

稱這個自根結點到葉子結點的調整過程為篩選。如圖: 在這裡插入圖片描述 再討論對第2個問題:n 個元素初始建堆的過程。 建堆方法:對初始序列建堆的過程,就是一個反覆進行篩選的過程。

1)n 個結點的完全二叉樹,則最後一個結點是第個結點的子樹。

2)篩選從第個結點為根的子樹開始,該子樹成為堆。

3)之後向前依次對各結點為根的子樹進行篩選,使之成為堆,直到根結點。

如圖建堆初始過程:無序序列:(49,38,65,97,76,13,27,49) 在這裡插入圖片描述

  • 對於堆節點的訪問: 父節點i的左子節點在位置:(2i+1); 父節點i的右子節點在位置:(2i+2); 子節點i的父節點在位置:floor((i-1)/2);

  • 演算法實現

package sort;

import java.util.Arrays;

/**
 * 堆排序
 * @author 54060
 *
 */
public class HeapSort {
 
	/*
	 * 建立小頂堆:雙親節點小於子節點的值。從葉子節點開始,直到根節點。這樣建立的堆定位最小值
	 */
	private void createLittleHeap(int[] data, int last) {
		 for (int i = (last- 1) / 2; i >= 0; i--) {  //找到最後一個葉子節點的雙親節點
	            // 儲存當前正在判斷的節點  
	            int parent = i;  
	            // 若當前節點的左子節點存在,即子節點存在
	            while (2 * parent + 1 <= last) {  
	                // biggerIndex總是記錄較大節點的值,先賦值為當前判斷節點的左子節點  
	                int bigger = 2 * parent + 1;//bigger指向左子節點  
	                if (bigger < last) { //說明存在右子節點
	                  
	                    if (data[bigger] > data[bigger+ 1]) { //右子節點>左子節點時
	                     
	                        bigger=bigger+1;  //bigger取左子節點和右子節點最小的那個。因為父節點要小於這兩個節點的值
	                    }  
	                } 
	                //下-->上-->下
	                if (data[parent] > data[bigger]) {  //若雙親節點值大於子節點中最小的
	                    // 若當前節點值比子節點最小值大,則交換2者的值,交換後將biggerIndex值賦值給k  
	                    swap(data, parent, bigger)