1. 程式人生 > 其它 >非科班面試之旅:Java實現資料結構中的八種排序方法

非科班面試之旅:Java實現資料結構中的八種排序方法

非科班面試之旅:Java實現資料結構中的八種排序方法
  • @return

    */

    public int partition(int[] data, int low, int high){

      getPivot(data, low, high);
    
      int pivot = data[high-1];//樞軸元素
    
      int i = low;
    
      int j = high - 1;
    
      while(i < j){
    
      	while(i < j && data[++i] <= pivot){}
    
      	data[j] = data[i];//data[i]大於pivot,移到後半區,此時原data[j]的值已儲存在pivot或data[i]中
    
      	while(i < j && data[--j] >= pivot){}
    
      	data[i] = data[j];//data[j]小於pivot,移到前半區,此時原data[i]的值已儲存在data[j]中
    
      }
    
      data[i] = pivot;
    
      return i;
    

    }

    /**

    • 取首、中、尾三者中間大小元素,避免快速排序效能退化

    • @param data

    • @param low

    • @param high

    */

    public void getPivot(int[] data, int left, int right){

      int mid = (left + right) / 2;
    
      //對三個元素按遞增排序
    
      if (data[left] > data[mid]) {
    
          swap(data, left, mid);
    
      }
    
      if (data[left] > data[right]) {
    
          swap(data, left, right);
    
      }
    
      if (data[right] < data[mid]) {
    
          swap(data, right, mid);
    
      }
    
      //將樞軸元素放置在待排序陣列後
    
      swap(data, right-1, mid);
    

    }

    /**

    • 快速排序非遞迴

    • @param data

    */

    public void quickSortNotRecur(int[] data, int low, int high){

      //使用棧來實現遞迴的壓棧出棧操作
    
      Stack<Object> s = new Stack<Object>();
    
      s.push(low);
    
      s.push(high);
    
      if(!s.empty()){
    
      	high = (Integer) s.pop();
    
      	low = (Integer) s.pop();
    
      	int loc = partition(data, low, high);//進行一次分割槽
    
      	s.push(loc+1);//後半區先進後出
    
      	s.push(high);
    
      	s.push(low);//前半區後進先出
    
      	s.push(loc-1);
    
      }
    

    }




[](https://gitee.com/vip204888/java-p7)6、堆排序

-----------------------------------------------------------------------



對待排列的n個元素組成的序列,將其看成一個完全二叉樹,如果該二叉樹中的任一非葉子節點的值均不小於(不大於)其左右子樹節點,則該二叉樹稱為大根堆(小根堆),樹的根節點(堆頂元素)是序列中最大(最小)的元素。  

堆排序的基本思想是:對待非遞減排列的序列首先建立一個初始堆,此時輸出堆頂元素,即最大值元素,然後取最後一個葉子節點替代堆頂元素,並將剩下的元素重新構建為大根堆,再輸出新堆的堆頂元素作為次大值元素…以此類推,直到所有的元素有序排列。  

![堆排序](https://img-blog.csdnimg.cn/5abb79842dd9427e8832e59d43247cac.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3NpbmF0XzM3NjIzNDkw,size_16,color_FFFFFF,t_70)



/**

 * 堆排序

 * @param data

 */

public void heapSort(int[] data){

	int i, n, tmp;

	n = data.length;

	for(i=n/2-1;i>=0;i--){//從n位置子節點所在子樹開始向上調整整個陣列為大根堆(構建初始堆)

		heapAdjust(data, i, n-1);

	}

	for(i=n-1;i>0;i--){//從初始堆取堆頂(最大元素),對剩餘元素重新構建大根堆

		tmp = data[0];//取堆頂

		data[0] = data[i];//將最末尾子節點交換到堆頂

		data[i] = tmp;//當前最大值放到陣列末尾

		heapAdjust(data, 0, i-1);//剩餘i-1個元素構建大根堆

	}

	System.out.println(Arrays.toString(data));

}



/**

 * 將待排序元素調整為大根堆

 * @param data

 * @param s

 * @param m

 */

public void heapAdjust(int[] data, int s, int m){

	int i, tmp;

	tmp = data[s];//備份當前父節點關鍵字

	for(i=2*s+1;i<=m;i=2*i+1){

		if(i<m && data[i]<data[i+1]){//找到子節點中關鍵字最大者

			i++;

		}

		if(tmp>=data[i]) break; //所有子節點都不大於父節點,本次無需調整

		data[s] = data[i]; //存在比父節點大的子節點,插入s位置

		s = i; //以i作為父節點的子樹可能因為本次調整不再是大根堆,因此需要進行調整

	}

	data[s] = tmp;//將備份關鍵字插入最後做了交換的節點位置

} 



[](https://gitee.com/vip204888/java-p7)7、歸併排序

------------------------------------------------------------------------



歸併排序的一種實現方法是把一個有n個記錄的無序檔案看成是由n個長度為1的有序子檔案組成的檔案,對子檔案兩兩歸併,得到n/2個長度為2或1的有序檔案,再兩兩歸併,重複得到一個包含n個記錄的有序檔案。  

![歸併排序](https://img-blog.csdnimg.cn/849a5694e2a8463f88f6cfd704ec2a8d.png)  

假設一次歸併的兩個檔案為陣列arr1和陣列arr2,則需要一個大小為arr1和arr2長度之和的輔助空間arrTmp來進行歸併,此時三個指標分別指向arr1,arr2和arrTmp初始位置,每次對比arr1和arr2指標指向元素,將其中較小值插入到arrTmp指標所指位置。當arr1,arr2中任一陣列末尾元素插入到了arrTmp中,則將另一個數組剩餘元素直接全部插入arrTmp中即可。如下圖所示:  

![藉助輔助陣列進行歸併](https://img-blog.csdnimg.cn/735654007c6141d9a331c0608d4564c3.png)



/**

 * 歸併排序

 * @param data

 * @param s

 * @param t

 */

public void mergeSort(int[] data, int left, int right){

	if(left < right){

		int mid = (left + right) >> 1;

		mergeSort(data, left, mid);//前半區歸併得到有序子序列data[left..mid]

		mergeSort(data, mid + 1, right);//後半區歸併得到有序子序列data[mid..right]

		merge(data, left, mid, right);//data[left..mid]和data[mid..right]歸併得到有序的data[s..t]

	}

}



/**

 * 對兩個序列data[s..m]和data[m+1..n]進行歸併

 * @param data

 * @param s

 * @param m

 * @param n

 */

public void merge(int[] data, int s, int m, int n){

	int i = s, j = m + 1, k = 0;//i指向data[m+1..n]初始位置,j指向data[s..m]初始位置,k指向tmp[]初始位置

	int st = s;

	int[] tmp = new int[data.length];

	while(i<=m&&j<=n){//歸併data[s..m]和data[m+1..n]存入臨時陣列tmp[]

		if(data[i]<=data[j]){

			tmp[k++] = data[i++];

		}else{

			tmp[k++] = data[j++];

		}

	}

	for(;i<=m;i++){//將剩餘data[s..m]元素複製到tmp[]

		tmp[k++] = data[i];

	}

	for(;j<=n;j++){//將剩餘data[m+1..n]元素複製到tmp[]

		tmp[k++] = data[j];

	}

	for(i=0;i<k;i++){//將臨時陣列元素複製回原陣列

		data[st++] = tmp[i];

	}

	//用於輸出資訊

	if(s == 0 && n == data.length-1 >> 1){

		System.out.println("前半區歸併完成:" + Arrays.toString(data));

	}

	if(s == data.length >> 1 && n == data.length - 1){

		System.out.println("後半區歸併完成:" + Arrays.toString(data));

	}

} 



[](https://gitee.com/vip204888/java-p7)8、基數排序

------------------------------------------------------------------------



基數排序是一種通過比較元素各個位數大小來進行排序的方法:基數r是按照元素數值的進位制來確定的,比如十進位制r=10,八進位制則r=8。d原來表示元素的位數,d取所有待排序元素的位數最大值,比如125則d=3。  

進行基數排序時,設立r個佇列,佇列編號分別從0到r-1。首先按照最低有效位大小將元素分別排進佇列中,然後按照佇列中的順序將元素重新排列。接著按照次低有效位將重新排列好的元素再次排進佇列中…如此重複直到最高有效位完成排列,此時從佇列取出的元素有序排列。  

![基數排序](https://img-blog.csdnimg.cn/f0eefaabf8e648eba0be16f8092bb304.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3NpbmF0XzM3NjIzNDkw,size_16,color_FFFFFF,t_70)



/**

 * 基數排序

 * @param data

 */

public void radixSort(int[] data){

	//確定最大位數

	int max = data[0];

	for(int x: data){

		if(x > max){

			max = x;

		}

	}

	int length = (max+"").length();

	for(int i=0,n=1;i<length;i++,n*=10){

		int[][] tmp = new int[10][data.length];

		int[] queLength = new int[10];//儲存每個佇列入隊元素個數

		//將陣列元素一一入隊

		for(int x: data){

			int index = x / n % 10;//確定元素應入佇列

			tmp[index][queLength[index]++] = x;

		}

		//將佇列中元素重新排列為陣列

		int dataIdx = 0;

		for(int j=0;j<10;j++){

			for(int q=0;q<queLength[j];q++){

				data[dataIdx++] = tmp[j][q];

			}

		}

	}

	

	System.out.println(Arrays.toString(data));

} 



[](https://gitee.com/vip204888/java-p7)執行例項

----------------------------------------------------------------------



import java.util.Arrays;

import java.util.Stack;

public class Sort {

final int[] data = {76,34,18,12,45,5,5,9};



public static void main(String args[]){

	Sort sort1 = new Sort();

	System.out.print("**直接插入排序\n原陣列:" + Arrays.toString(sort1.data) + "\n排序後:");

	sort1.insertSort(sort1.data);



	Sort sort2 = new Sort();

	System.out.print("\n**氣泡排序\n原陣列:" + Arrays.toString(sort2.data) + "\n排序後:");

	sort2.bubbleSort(sort2.data);



	Sort sort3 = new Sort();

	System.out.print("\n**簡單選擇排序\n原陣列:" + Arrays.toString(sort3.data) + "\n排序後:");

	sort3.selectSort(sort3.data);



	Sort sort4 = new Sort();

	System.out.print("\n**希爾排序\n原陣列:" + Arrays.toString(sort4.data) + "\n");

	sort4.shellSort(sort4.data);



	Sort sort5 = new Sort();

	System.out.print("\n**快速排序\n原陣列:" + Arrays.toString(sort5.data) + "\n排序後:");

	sort4.quickSort(sort5.data, 0, sort5.data.length - 1);

	System.out.println(Arrays.toString(sort5.data));



	Sort sort51 = new Sort();

	System.out.print("\n**快速排序(非遞迴)\n原陣列:" + Arrays.toString(sort51.data) + "\n排序後:");

	sort4.quickSort(sort51.data, 0, sort51.data.length - 1);

	System.out.println(Arrays.toString(sort51.data));



	Sort sort6 = new Sort();

	System.out.print("\n**堆排序\n原陣列:" + Arrays.toString(sort6.data) + "\n排序後:");

	sort4.heapSort(sort6.data);



	Sort sort7 = new Sort();

	System.out.println("\n**歸併排序\n原陣列:" + Arrays.toString(sort7.data));

	sort4.mergeSort(sort7.data, 0, sort7.data.length - 1);

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



	Sort sort8 = new Sort();

	System.out.print("\n**基數排序\n原陣列:" + Arrays.toString(sort8.data) + "\n排序後:");

	sort4.radixSort(sort8.data);

}

public void swap(int[] data, int i, int j){

	int tmp = data[i];

	data[i] = data[j];

	data[j] = tmp;

}

}




輸出結果:



> \*\*直接插入排序  

> 原陣列:\[76, 34, 18, 12, 45, 5, 5, 9\]  

> 排序後:\[5, 5, 9, 12, 18, 34, 45, 76\]  

>   

> \*\*氣泡排序  

> 原陣列:\[76, 34, 18, 12, 45, 5, 5, 9\]  

> 排序後:\[5, 5, 9, 12, 18, 34, 45, 76\]  

>   

> \*\*簡單選擇排序  

> 原陣列:\[76, 34, 18, 12, 45, 5, 5, 9\]  

> 排序後:\[5, 5, 9, 12, 18, 34, 45, 76\]  

>   

> \*\*希爾排序  

> 原陣列:\[76, 34, 18, 12, 45, 5, 5, 9\]  

> 分組間距:4,此次排序得到:  

> \[45, 5, 5, 9, 76, 34, 18, 12\]  

> 分組間距:2,此次排序得到:  

> \[5, 5, 18, 9, 45, 12, 76, 34\]  

> 分組間距:1,此次排序得到:  

> \[5, 5, 9, 12, 18, 34, 45, 76\]  

>   



## 最後:學習總結——MyBtis知識腦圖(純手繪xmind文件)

學完之後,若是想驗收效果如何,其實最好的方法就是可自己去總結一下。比如我就會在學習完一個東西之後自己去手繪一份xmind檔案的知識梳理大綱腦圖,這樣也可方便後續的複習,且都是自己的理解,相信隨便瞟幾眼就能迅速過完整個知識,腦補回來。下方即為我手繪的MyBtis知識腦圖,由於是xmind檔案,不好上傳,所以小編將其以圖片形式匯出來傳在此處,細節方面不是特別清晰。但可給感興趣的朋友提供完整的MyBtis知識腦圖原件(包括上方的面試解析xmind文件)

![image](https://upload-images.jianshu.io/upload_images/24613101-cca28b670ec32761.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

除此之外,前文所提及的Alibaba珍藏版mybatis手寫文件以及一本小小的MyBatis原始碼分析文件——《MyBatis原始碼分析》等等相關的學習筆記文件,也皆可分享給認可的朋友!

**[資料領取方式:戳這裡免費下載](https://gitee.com/vip204888/java-p7)**