java排序算法

本文羅列java中的各種排序算法思想和編程實現。
java排序算法常用的有:快速排序、冒泡排序、選擇排序、插入排序、堆排序、希爾排序、合併排序。

1. 快速排序

/*
 * 快速排序思想:採用經典的分治思想
 * 1、通過一躺排序將要排序的數據分割成獨立的兩部分,其中一部分的所有數據都比另外一部分的所有數據都要小 
 * 2、然後再按次方法對這兩部分數據分別進行快速排序,整個排序過程可以遞歸進行,以此達到整個數據變成有序序列
 */

public class QuikSort {

	public static void main(String[] args) {
		int arr[]=new int[]{3,5,3,11,0,44,2,9,4,8,1};
		quikSort(arr,0,arr.length-1);
		print(arr);
	}
	public static void quikSort(int[] arr,int left,int right){
		//遞歸
		if(right>left){
			int middle=apart(arr,left,right);
			quikSort(arr,left,middle-1);
			quikSort(arr,middle+1,right);
		}
		
	}
	//一趟快速排序
	public static int apart(int[] arr,int left,int right){
		//key值取爲左邊第一個元素
		int key=arr[left];
		while(left<right){
			//從右邊開始,找第一個小於key值的元素
			while(arr[right]>=key)
				right--;
			arr[left]=arr[right];
			//從左邊開始,找第一個大於key值的元素
			while(right>left&&arr[left]<=key)
				left++;
			arr[right]=arr[left];
		}
		//此時left==right
		arr[left]=key;
		return left;
	}

	public static void print(int[] arr){
		for(int i=0;i<arr.length;i++){
			if(i==arr.length-1)
				System.out.println(arr[i]);
			else
				System.out.print(arr[i]+",");
		}
	}
}

2. 冒泡排序和選擇排序

/*
 * 冒泡排序和選擇排序
 * 冒泡排序思想:相鄰的兩個元素進行比較,如果符合條件換位
 * 選擇排序思想:將數組序列劃分爲無序和有序區,尋找無序區中的最小值和無序區的首元素交換,有序區擴大一個,循環最終完成全部排序
 */
public class BubbleSort {

	public static void main(String[] args) {
		int arr[]=new int[]{3,5,3,11,0,44,2,9,4,8,1};
		System.out.println("原數組:");
		print(arr);
		System.out.println("冒泡排序後:");
		bubbleSort(arr);
		print(arr);
		System.out.println("選擇排序後:");
		bubbleSort(arr);
		print(arr);
	}
	//冒泡排序
	public static void bubbleSort(int[] arr)
	{
		for(int x=0; x<arr.length-1; x++)
		{
			for(int y=0; y<arr.length-1-x; y++)
			{
				if(arr[y]>arr[y+1])	
				{
					swap(arr,y,y+1);
				}
			}	
		}
	}

	//選擇排序
	public static void selectSort(int[] arr)
	{
		for(int x=0; x<arr.length-1; x++)
		{
			for(int y=x+1; y<arr.length; y++)
			{
				if(arr[x]>arr[y])
				{
					swap(arr,x,y);
				}
			}
		}
	}
	//選擇排序,記錄每趟排序的最大值/最小值位置,只將最值與無序區的第1個元素進行交換
	public static void selectSort_2(int[] arr)
	{
		for(int x=0; x<arr.length-1; x++)
		{
			int num = arr[x];
			int index = x;
			for(int y=x+1; y<arr.length; y++)
			{
				if(num>arr[y])
				{
					num = arr[y];
					index = y;
				}
			}
			if(index!=x)
				swap(arr,x,index);
		}
	}
	public static void swap(int[] arr,int i,int j){
		int temp  = arr[i];
		arr[i] = arr[j];
		arr[j] = temp;
	}
	public static void print(int[] arr){
		for(int i=0;i<arr.length;i++){
			if(i==arr.length-1)
				System.out.println(arr[i]);
			else
				System.out.print(arr[i]+",");
		}
	}
}
3. 插入排序

/*
 * 插入排序
 * 思想:把n個待排序的元素看成一個有序表和一個無序表。開始時有序表中只包含一個元素,無序表中包含n-1個元素。
 * 排序過程中,每次從無序表中取出一個元素,把它的值和有序表中的元素的值進行比較,之後將其插入到有序表中適當的位置,
 * 使這個有序表成爲一個新的有序表。
 */
public class InsertSort {

	public static void main(String[] args) {
		int arr[]=new int[]{3,5,3,11,0,44,2,9,4,8,1};
		System.out.println("排序前:");
		print(arr);
		insertSort(arr);
		System.out.println("排序後:");
		print(arr);
		
	}
	public static void insertSort(int[] arr){
		//待排序元素是除arr[0]之外的所有元素
		for(int i=1; i<arr.length; i++){
	          int insertVal = arr[i];
	          int index = i-1;
	          //搜索insetVal的插入位置,插入位置之後的元素需要順延移動位置
	          while(index>=0 && insertVal<arr[index]){
	             arr[index+1] = arr[index];
	             index--;
	          }
	          arr[index+1] = insertVal;
	    }
	}
	public static void print(int[] arr){
		for(int i=0;i<arr.length;i++){
			if(i==arr.length-1)
				System.out.println(arr[i]);
			else
				System.out.print(arr[i]+",");
		}
	}	
}
4. 堆排序

/*
 * 堆排序
 * 個關鍵字序列Kl,K2,…,Kn稱爲(Heap),當且僅當該序列滿足如下性質(簡稱爲堆性質):
 * ki<=k(2i)且ki<=k(2i+1)(1≤i≤ n),當然,這是小根堆,大根堆則換成>=號。
 * k(i)相當於二叉樹的非葉子結點,K(2i)則是左子節點,k(2i+1)是右子節點。
 * 
 * 堆排序利用了大根堆(或小根堆)堆頂記錄的關鍵字最大(或最小)這一特徵,使得在當前無序區中選取最大(或最小)關鍵字的記錄變得簡單。
 * 用大根堆排序的基本思想:
 * 1.先將初始文件R[1..n]建成一個大根堆,此堆爲初始的無序區
 * 2.再將關鍵字最大的記錄R[1](即堆頂)和無序區的最後一個記錄R[n]交換,由此得到新的無序區R[1..n-1]和有序區R[n],且滿足R[1..n-1].keys≤R[n].key
 * 3.由於交換後新的根R[1]可能違反堆性質,故應將當前無序區R[1..n-1]調整爲堆。然後再次將R[1..n-1]中關鍵字最大的記錄R[1]和該區間的最後一個記錄R[n-1]交換,由此得到新的無序區R[1..n-2]和有序區R[n-1..n],且仍滿足關係R[1..n-2].keys≤R[n-1..n].keys,同樣要將R[1..n-2]調整爲堆。
 * 4.直到無序區只有一個元素爲止。
 * 
 * 特點
 * 堆排序(HeapSort)是一樹形選擇排序。堆排序的特點是:在排序過程中,將R[l..n]看成是一棵完全二叉樹的順序存儲結構,
 * 利用完全二叉樹中雙親結點和孩子結點之間的內在關係(參見二叉樹的順序存儲結構),在當前無序區中選擇關鍵字最大(或最小)的記錄。
 * 區別
 * 直接選擇排序中,爲了從R[1..n]中選出關鍵字最小的記錄,必須進行n-1次比較,然後在R[2..n]中選出關鍵字最小的記錄,
 * 又需要做n-2次比較。事實上,後面的n-2次比較中,有許多比較可能在前面的n-1次比較中已經做過,
 * 但由於前一趟排序時未保留這些比較結果,所以後一趟排序時又重複執行了這些比較操作。
 * 堆排序可通過樹形結構保存部分比較結果,可減少比較次數。
 */
public class HeapSort{
	private static int[]sort=new int[]{1,0,10,20,3,5,6,4,9,8,12,17,34,11};
	public static void main(String[] args){
		buildMaxHeapify(sort);
		heapSort(sort);
		print(sort);
	}
	 
	private static void buildMaxHeapify(int[]data){
		//沒有子節點的才需要創建最大堆,從最後一個的父節點開始
		int startIndex=getParentIndex(data.length-1);
		//從尾端開始創建最大堆,每次都是正確的堆
		for(int i=startIndex;i>=0;i--){
			maxHeapify(data,data.length,i);
		}
	}
	 
	/**
	*創建最大堆
	*heapSize需要創建最大堆的大小,一般在sort的時候用到,因爲最多值放在末尾,末尾就不再歸入最大堆了
	*index當前需要創建最大堆的位置
	*/
	private static void maxHeapify(int[] data,int heapSize,int index){
		//當前點與左右子節點比較
		int left=getChildLeftIndex(index);
		int right=getChildRightIndex(index);	 
		int largest=index;
		if(left<heapSize&&data[index]<data[left]){
			largest=left;
		}
		if(right<heapSize&&data[largest]<data[right]){
			largest=right;
		}
		//得到最大值後可能需要交換,如果交換了,其子節點可能就不是最大堆了,需要重新調整
		if(largest!=index){
			int temp=data[index];
			data[index]=data[largest];
			data[largest]=temp;
			maxHeapify(data,heapSize,largest);
		}
	}
	 
	/**
	*排序,最大值放在末尾,data雖然是最大堆,在排序後就成了遞增的
	*/
	private static void heapSort(int[]data){
		//末尾與頭交換,交換後調整最大堆
		for(int i=data.length-1;i>0;i--){
			int temp=data[0];
			data[0]=data[i];
			data[i]=temp;
			maxHeapify(data,i,0);
		}
	}
	 
	/**
	*父節點位置
	*/
	private static int getParentIndex(int current){
		return(current-1)>>1;
	}
	 
	/**
	*左子節點position注意括號,加法優先級更高
	*/
	private static int getChildLeftIndex(int current){
		return(current<<1)+1;
	}
	 
	/**
	*右子節點position
	*/
	private static int getChildRightIndex(int current){
		return(current<<1)+2;
	}
	 
	private static void print(int[]data){
		int pre=-2;
		for(int i=0;i<data.length;i++){
			if(pre<(int)getLog(i+1)){
				pre=(int)getLog(i+1);
				System.out.println();
			}
			System.out.print(data[i]+"|");
			}
	}
	
	 
	/**
	*以2爲底的對數
	*/
	private static double getLog(double param){
		return Math.log(param)/Math.log(2);
	}
}
5. 希爾排序
/*
 * 希爾排序(Shell Sort)是插入排序的一種。是針對直接插入排序算法的改進。
 * 該方法又稱縮小增量排序,因DL.Shell於1959年提出而得名。
 * 思想:
 * 先取一個小於n的整數d1作爲第一個增量,把文件的全部記錄分組。所有距離爲d1的倍數的記錄放在同一個組中。
 * 先在各組內進行直接插入排序;然後,取第二個增量d2<d1重複上述的分組和排序,直至所取的增量dt=1(dt<dt-l<…<d2<d1),
 * 即所有記錄放在同一組中進行直接插入排序爲止。
 */
public class ShellSort {
	
	public static void main(String[] args){
		int[] a={49,38,65,97,76,13,27,49,78,34,12,64,1};
		System.out.println("排序之前:");
		for(int i=0;i<a.length;i++){
			System.out.print(a[i]+",");
		}
		//希爾排序
		int d=a.length;
		while(true){
			d=d/2;
			for(int x=0;x<d;x++){
				for(int i=x+d;i<a.length;i=i+d){
					int temp=a[i];
					int j;
					for(j=i-d;j>=0&&a[j]>temp;j=j-d){
						a[j+d]=a[j];
			        }
					a[j+d]=temp;
				}
		    }
			if(d==1){
				break;
			}
		}
		System.out.println();
		System.out.println("排序之後:");
		for(int i=0;i<a.length;i++){
			System.out.print(a[i]+",");
		}
	}
}
6. 合併排序
/*
 * 歸併(Merge)排序法是將兩個(或兩個以上)有序表合併成一個新的有序表,即把待排序序列分爲若干個有序的子序列,
 * 再把有序的子序列合併爲整體有序序列。--分治思想
 * 歸併操作的工作原理如下:
 * 第一步:申請空間,使其大小爲兩個已經排序序列之和,該空間用來存放合併後的序列
 * 第二步:設定兩個指針,最初位置分別爲兩個已經排序序列的起始位置
 * 第三步:比較兩個指針所指向的元素,選擇相對小的元素放入到合併空間,並移動指針到下一位置
 * 重複步驟3直到某一指針超出序列尾
 * 將另一序列剩下的所有元素直接複製到合併序列尾
 */
public class MergeSort{
	/*
	 * 二路歸併
	 * 原理:將兩個有序表合併和一個有序表
	 * s:第一個有序表的起始下標
	 * m:第二個有序表的起始下標
	 * t:第二個有序表的結束小標
	 */	
	private static void merge(int[] a,int s,int m,int t){
		int[] tmp=new int[t-s+1];
		int i=s,j=m,k=0;
		while(i<m&&j<=t){
			if(a[i]<=a[j]){
				tmp[k]=a[i];
				k++;
				i++;
			}
			else{
				tmp[k]=a[j];
				j++;
				k++;
		    }
	    }
		while(i<m){
			tmp[k]=a[i];
			i++;
			k++;
		}
		while(j<=t){
			tmp[k]=a[j];
			j++;
			k++;
		}
		System.arraycopy(tmp,0,a,s,tmp.length);
	}	 
	/*
	 * 每次歸併的有序集合的長度
	 */
	public static void mergeSort(int[] a,int s, int len){
		int size=a.length;
		int mid=size/(len<<1);
		int c=size&((len<<1)-1);
		// -------歸併到只剩一個有序集合的時候結束算法-------//
		if(mid==0)
			return;
		// ------進行一趟歸併排序-------//
		for(int i=0;i<mid;++i){
			s=i*2*len;
			merge(a,s,s+len,(len<<1)+s-1);
		}
		// -------將剩下的數和倒數一個有序集合歸併-------//
		if(c!=0)
			merge(a,size-c-2*len,size-c,size-1);
		// -------遞歸執行下一趟歸併排序------//
		mergeSort(a,0,2*len);
	}
	 
	public static void main(String[] args){ 
		int[] a=new  int[]{4,3,6,1,2,5};
		mergeSort(a,0,1); 
		for(int i=0;i<a.length;++i){
			System.out.print(a[i]+" ");
		}
	} 
}




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章