排序算法

排序算法

1.1、冒泡排序

        冒泡排序(Bubble Sort,臺灣譯爲:泡沫排序或氣泡排序)是一種簡單的排序算法。它重複地走訪過要排序的數列,一次比較兩個元素,如果他們的順序錯誤就把他們交換過來。走訪數列的工作是重複地進行直到沒有再需要交換,也就是說該數列已經排序完成。這個算法的名字由來是因爲越小的元素會經由交換慢慢“浮”到數列的頂端。

1.1.1、步驟:

       1、比較相鄰的元素。如果第一個比第二個大,就交換他們兩個。

       2、對每一對相鄰元素作同樣的工作,從開始第一對到結尾的最後一對。在這一點,最後的

             元素應該會是最大的數。

       3、針對所有的元素重複以上的步驟,除了最後一個。

       4、持續每次對越來越少的元素重複上面的步驟,直到沒有任何一對數字需要比較。

1.1.2、示例

       初始數組:    49,38,65,97,76,13,27

       第1次排序:  38,49,65,76,13,27,97

       第2次排序:  38,49,65,13,27,76,97

       第3次排序:  38,49,13,27,65,76,97

       第4次排序:  38,13,27,49,65,76,97

       第5次排序:  13,27,38,49,65,76,97

       第6次排序:  13,27,38,49,65,76,97

1.1.3、程序

public static void bubbleSort(int []arr){
		int n = arr.length;
		int tmp = arr[0];
		for(int i=1;i<n;i++){
			for(int j=0;j<n-i;j++){
				if(arr[j]>arr[j+1]){
					tmp = arr[j+1];
					arr[j+1]=arr[j];
					arr[j] = tmp;
				}
			}
		}
	}

1.2、簡單選擇排序

       簡單選擇排序是一種選擇排序。選擇排序:每趟從待排序的記錄中選出關鍵字最小的記錄,順序放在已排序的記錄序列末尾,直到全部排序結束爲止。

1.2.1、步驟

      簡單排序處理流程

      1、從待排序序列中,找到關鍵字最小的元素;

      2、如果最小元素不是待排序序列的第一個元素,將其和第一個元素互換;

      3、從餘下的 N - 1 個元素中,找出關鍵字最小的元素,重複(1)、(2)步,直到排序結束。

1.2.2、示例

       初始數組:    49,38,65,97,76,13,27

       第1次排序:  13,38,65,97,76,49,27

       第2次排序:  13,27,65,97,76,49,38

       第3次排序:  13,27,38,97,76,49,65

       第4次排序:  13,27,38,49,76,97,65

       第5次排序:  13,27,38,49,65,97,76

       第6次排序:  13,27,38,49,65,76,97

1.2.3、程序

public static void simpleSelectSort(int []arr){
		int n= arr.length;
		int min = 0,tmp;
		for(int i=0;i<n;i++){
			min = i;
			//選取最小值所在的下標
			for(int j=i+1;j<n;j++){
				if(arr[j]<arr[min])
					min=j;
			}
			if(min==i)
				continue;
			else{
				tmp = arr[min];
				arr[min]=arr[i];
				arr[i] = tmp;
			}
		}
	}

1.3、插入排序

        插入排序(Insertion Sort)的算法描述是一種簡單直觀的排序算法。它的工作原理是通過構建有序序列,對於未排序數據,在已排序序列中從後向前掃描,找到相應位置並插入。插入排序在實現上,通常採用in-place排序(即只需用到O(1)的額外空間的排序),因而在從後向前掃描過程中,需要反覆把已排序元素逐步向後挪位,爲最新元素提供插入空間。

1.3.1、步驟

       1、從第一個元素開始,該元素可以認爲已經被排序

       2、取出下一個元素,在已經排序的元素序列中從後向前掃描

       3、如果該元素(已排序)大於新元素,將該元素移到下一位置

       4、重複步驟3,直到找到已排序的元素小於或者等於新元素的位置

       5、將新元素插入到該位置中

       6、重複步驟2

1.3.2、示例

      初始數組:    49,38,65,97,76,13,27

      第1次排序:  38,49,65,97,76,13,27

      第2次排序:  38,49,65,97,76,13,27

      第3次排序:  38,49,65,97,76,13,27

      第4次排序:  38,49,65,76,97,13,27

      第5次排序:  13,38,49,65,76,97,27

      第6次排序:  13,27,38,49,65,76,97

1.3.3、程序

public static void  insertSort(int []arr){
		int tmp=arr[0];
		for(int i=1;i<arr.length;i++){
			tmp =arr[i];
			int j= i-1;
			while(j>=0 && tmp<arr[j]){
				arr[j+1]=arr[j];
				j--;
			}
			arr[j+1] = tmp;
			print(arr);
		}
	}

1.4、希爾排序

      希爾排序,也稱遞減增量排序算法,因DL.Shell於1959年提出而得名,是插入排序的一種高速而穩定的改進版本。

1.4.1、步驟

     1、先取一個小於n的整數d1作爲第一個增量,把文件的全部記錄分成d1個組。

     2、所有距離爲d1的倍數的記錄放在同一個組中,在各組內進行直接插入排序。

     3、取第二個增量d2<d1重複上述的分組和排序,

     4、直至所取的增量dt=1(dt<dt-l<…<d2<d1),即所有記錄放在同一組中進行直接插入排序爲止。

      希爾排序的時間複雜度與增量序列的選取有關,例如希爾增量時間複雜度爲O(n^2),而Hibbard增量的希爾排序的時間複雜度爲O(N^(5/4)),但是現今仍然沒有人能找出希爾排序的精確下界。

1.4.2、示例

                          

     排序前:9,1,2,5,7,4,8,6,3,5

      Dk =5 : 4 1 2 3 5 9 8 6 5 7

      DK =2 : 2 1 4 3 5 6 5 7 8 9

      DK =1 : 1 2 3 4 5 5 6 7 8 9

當DK=2時:

分組:                     排序後:

4   2   5   8   5           2   4    5    5    8

  1   3   9   6   7             1    3    6    7    9

1.4.3、程序

//希爾排序
	public static void shellSort(int arr[]){
		int dk = arr.length/2;
		while(dk>=1){
			shellInsertSort(arr,dk);
			dk=dk/2;
			print(arr);
		}
	}
	public static void shellInsertSort(int arr[],int dk){
		int tmp=arr[0];
		for(int i=dk;i<arr.length;i++){
			tmp =arr[i];
			int j= i-dk;
			while(j>=0 && tmp<arr[j]){
				arr[j+dk]=arr[j];
				j=j-dk;
			}
			arr[j+dk] = tmp;
		}
	}

1.5、堆排序

堆排序(Heapsort)是指利用堆這種數據結構所設計的一種排序算法。堆積是一個近似完全二叉樹的結構,並同時滿足堆積的性質:即子結點的鍵值或索引總是小於(或者大於)它的父節點。

1.5.1、大小堆的基本概念

堆的定義是:n個元素的序列{k1,k2,…,kn},當且僅當滿足如下關係時被成爲堆

    (1)Ki<= k2i 且 ki <= k2i-1        或

     (2) Ki>= k2i 且 ki >= k2i-1 (i = 1,2,…[n/2])

當滿足(1)時,爲最小堆,當滿足(2)時,爲最大堆。若將此序列對應的一維數組是一個完全二叉樹,則2i-1和2i+1個節點分別是節點i的左右子節點。

1.5.2、步驟

一般分爲兩個重要的步驟:構造初始堆、堆調整

(1)根據初始數組去構造初始堆(構建一個完全二叉樹,保證所有的父結點都比它的孩子結點數值大)。

(2)每次交換第一個和最後一個元素,輸出最後一個元素(最大值),然後把剩下元素重新調整爲大根堆。

1、初始堆

             

2、堆調整

1、在對應的數組元素A[i], 左孩子A[LEFT(i)], 和右孩子A[RIGHT(i)]中找到最大的那一個,將其下標存儲在largest中。

2、如果A[i]已經就是最大的元素,則程序直接結束。

3、否則,i的某個子結點爲最大的元素,將A[largest]與A[i]交換。

4、再從交換的子節點開始,重複1,2,3步,直至葉子節點,算完成一次堆調整。

這裏需要提一下的是,一般做一次堆調整的時間複雜度爲log(n)。

1.5.3、示例

                 

1.5.4、程序

//堆的初始化操作
public static void initMaxHeap(int[] arr){
		for(int i = arr.length / 2 -1 ; i >=0  ; i--){
			maxHeapAdjust(arr,i,arr.length);
		}
	}
//堆的調整操作
	public static  void maxHeapAdjust(int []arr, int i,int n){
		int j=2*i+1,tmp = arr[i];
		while(j<n){
            //取左右孩子節點中最大值節點的座標
			if((j+1< n) && arr[j+1]>arr[j])
				j=j+1;
            //與待下沉的節點值比較,如果小於就結束下沉操作
			if(arr[j]<tmp)
				break;
            //如果大於下沉節點,記錄該孩子節點的位置,並將值賦予父節點。
			arr[i] = arr[j];
			i = j;
			j = 2*j+1;
		}
		arr[i] = tmp ;	
	}
	//堆排序
public static void heapSort(int []arr){
		initMaxHeap(arr);
		int tmp = arr[0];
		for(int i=0;i<arr.length;i++){
            //每次循環將第n-i-1個元素與最大的元素即第0個元素交互
			arr[0] = arr[arr.length-i-1];
			arr[arr.length-i-1] = tmp;
            //元素交換完成後對第0個元素進行一次堆的調整,這時調整時元素的
            //個數爲n-i-1
			maxHeapAdjust(arr,0,arr.length-i-1);
			tmp = arr[0];
			print(arr);
		}
	}

1.6、歸併排序

1.6.1、步驟

歸併排序其實要做兩件事:

(1)“分解”——將序列每次折半劃分。

(2)“合併”——將劃分後的序列段兩兩合併後排序。

我們先來考慮第二步,如何合併?

在每次合併過程中,都是對兩個有序的序列段進行合併,然後排序。這兩個有序序列段分別爲 R[low, mid] 和 R[mid+1, high]。先將他們合併到一個局部的暫存數組R2中,帶合併完成後再將R2複製回R中。爲了方便描述,我們稱 R[low, mid] 第一段,R[mid+1, high] 爲第二段。每次從兩個段中取出一個記錄進行關鍵字的比較,將較小者放入R2中。最後將各段中餘下的部分直接複製到R2中。經過這樣的過程,R2已經是一個有序的序列,再將其複製回R中,一次合併排序就完成了

1.6.2、示例

              

1.6.3、程序

public static void mergeSort(int arr[],int low,int high){
		if(low<high){
			int mid = low + (high-low)/2;
			mergeSort(arr,low,mid);
			mergeSort(arr,mid+1,high);
			merge(arr,low,mid,high);
			
		}
	}
	public static void merge(int arr[],int low,int mid,int high){
		int arrary[] = new int[high-low+1];
		int i =low,j =mid+1,k=0;
		while(i<=mid && j<=high){
			if(arr[i]<arr[j]){
				arrary[k++] = arr[i++];
			}else arrary[k++] = arr[j++];
		}
		while(j<=high) 
			arrary[k++] = arr[j++];
		while(i<=mid) 
			arrary[k++] = arr[i++];
		for(int m=low;m<high+1;m++)
			arr[m] = arrary[m-low];
		
	}

1.7、快速排序

1.7.1、步驟

         快速排序的基本思想是,通過一輪的排序將序列分割成獨立的兩部分,其中一部分序列的關鍵字(這裏主要用值來表示)均比另一部分關鍵字小。繼續對長度較短的序列進行同樣的分割,最後到達整體有序。在排序過程中,由於已經分開的兩部分的元素不需要進行比較,故減少了比較次數,降低了排序時間。

  詳細描述:首先在要排序的序列 a 中選取一箇中軸值,而後將序列分成兩個部分,其中左邊的部分 b 中的元素均小於或者等於中軸值,右邊的部分 c 的元素 均大於或者等於中軸值,而後通過遞歸調用快速排序的過程分別對兩個部分進行排序,最後將兩部分產生的結果合併即可得到最後的排序序列。

  “基準值”的選擇有很多種方法。最簡單的是使用第一個記錄的關鍵字值。但是如果輸入的數組是正序或者逆序的,就會將所有的記錄分到“基準值”的一邊。較好的方法是隨機選取“基準值”,這樣可以減少原始輸入對排序造成的影響。但是隨機選取“基準值”的開銷大。

  爲了實現一次劃分,我們可以從數組(假定數據是存在數組中)的兩端移動下標,必要時交換記錄,直到數組兩端的下標相遇爲止。爲此,我們附設兩個指針(下角標)i 和 j, 通過 j 從當前序列的有段向左掃描,越過不小於基準值的記錄。當遇到小於基準值的記錄時,掃描停止。通過 i 從當前序列的左端向右掃描,越過小於基準值的記錄。當遇到不小於基準值的記錄時,掃描停止。交換兩個方向掃描停止的記錄 a[j] 與 a[i]。 然後,繼續掃描,直至 i 與 j 相遇爲止。掃描和交換的過程結束。這是 i 左邊的記錄的關鍵字值都小於基準值,右邊的記錄的關鍵字值都不小於基準值。

  通過兩個不相鄰元素交換,可以一次交換消除多個逆序,加快排序速度。快速排序方法在要排序的數據已經有序的情況下最不利於發揮其長處。

1.7.2、示例

               

1.7.3、程序

  public void quickSort(int[]arrary, int start, int end) { 

        if(start >= end) return;        

        int mid = partition(arrary, start, end);        

        quickSort(arrary, start, mid-1);

        quickSort(arrary, mid+1, end);

        print(arrary);

    }

    publicint partition(int[] arrary,int start,int end){

        int midvalue =arrary[start];

        while(start<end){

            while(arrary[end]>midvalue&& end > start)

                end--;

            arrary[start]= arrary[end];

            while(arrary[start]<midvalue&& end > start)

                start++;

            arrary[end]= arrary[start];

        }

        arrary[end]= midvalue;

        return end;
    }

       
      







發佈了33 篇原創文章 · 獲贊 10 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章