數組七大經典排序算法(冒泡、插入、希爾、快排、選擇、歸併、堆)Java實現

簡介

這是一份常用經典排序算法的Java代碼實現,可運行。

算法包括:冒泡排序、插入排序、希爾排序、快速排序、選擇排序、歸併排序、堆排序。

複雜度

算法複雜度,穩定性:
複雜度穩定性
忘記說希爾排序了~

希爾排序是不穩定的。因爲,雖然一次插入排序是穩定的,但是分區後的插入可能導致不同分區之間的數相對位置發生改變,因此最終是不穩定的。

希爾排序的時間複雜度沒有明確的說法,有的說平均複雜度是O(nlogn),有的說是O(n1.3),總之就是比O(n2)小啦。

希爾排序是按照不同步長對元素進行插入排序,當剛開始元素很無序的時候,步長最大,所以插入排序的元素個數很少,速度很快;當元素基本有序了,步長很小,插入排序對於有序的序列效率很高。所以,希爾排序的時間複雜度會比o(n^2)好一些

代碼

話不多說,上代碼:

package test1;

public class Test {
	/**
	 * 交換數組中兩個數
	 * @param nums
	 * @param i
	 * @param j
	 */
	public static void swap(int nums[],int i,int j){
		int temp;
		temp = nums[i];
		nums[i] = nums[j];
		nums[j]=temp;
	}
	/**
	 * 冒泡排序
	 * @param nums
	 */
	public static void bubbleSort(int nums[]){
		for(int i = 0;i<nums.length;i++){
			for(int j = 0;j<nums.length-i-1;j++){
				if(nums[j]>nums[j+1]){
					swap(nums,j,j+1);
				}
			}
		}
	}
	/**
	 * 改進的冒泡排序
	 * @param nums
	 */
	public static void improvedBubbleSort(int nums[]){
		for(int i = 0;i<nums.length;i++){
			boolean isChanged = false;//標記一輪中,是否發生交換
			for(int j = 0;j<nums.length-i-1;j++){
				if(nums[j]>nums[j+1]){
					swap(nums,j,j+1);
					isChanged = true;
				}
			}
			if(!isChanged){//如果沒有發生交換,則已經有序,直接結束排序
				break;
			}
		}
	}
	/**
	 * 插入排序
	 * @param nums
	 */
	public static void insertSort(int nums[]){
		int len = nums.length;
		for(int i = 1;i<len;i++){
			if(nums[i]<nums[i-1]){//如果有逆序,則向前找到一個不大於該值的位置,插到它後面
				int temp = nums[i];//保存當前值
				int j;
				//向前尋找
				for(j = i-1;j>=0&&nums[j]>temp;j--){
					nums[j+1] = nums[j];
				}
				nums[j+1] = temp;//賦值
			}
		}
	}
	/**
	 * 引入二分查找的插入排序
	 * @param nums
	 */
	public static void insertBinarySort(int nums[]){
		int len = nums.length;
		for(int i = 1;i<len;i++){
			if(nums[i]<nums[i-1]){//如果有逆序,則向前找到一個不大於該值的位置,插到它後面
				int temp = nums[i];//記錄當前值
				int low = 0,h = i-1,mid=0;//二分查找的low,high,mid位置標記
				while(low<=h){
					mid = (low+h)/2;
					if(temp>nums[mid]){//如果當前值大於中間位置的值,則區間改爲[mid+1,h]
						low = mid+1;
					}else{//如果當前值小於中間位置的值,則區間改爲[low,mid-1]
						h = mid-1;
					}
				}
				//找到位置(就是low)後,從後向前移動數組,以騰出位置
				for(int j = i;j>low;j--){
					nums[j] = nums[j-1];
				}
				nums[low] = temp;//賦值
			}
		}
	}
	/**
	 * 希爾排序
	 * @param nums
	 */
	public static void shellSort(int nums[]){
		int len = nums.length;
		for(int gap = len/2;gap>0;gap/=2){//gap每次減半
			for(int i = 0;i<len;i+=gap){//比較的時候,每隔gap個數比較一次
				int temp = nums[i];//記錄當前值
				int j;
				//如果當前值小於前面對應的gap位置的數,則把前面的數賦值到當前位置
				//j-=gap,將索引又移到前面gap處,以便於交換
				for(j = i;j>=gap&&temp<nums[j-gap];j-=gap){
					nums[j] = nums[j-gap];
				}
				//再把當前值賦值到前面的位置,本質就是交換了當前值和前面對應gap位置的數
				nums[j] = temp;
			}
		}
	}
	/**
	 * 選擇排序
	 * @param nums
	 */
	public static void selectSort(int nums[]){
		int len = nums.length;
		for(int i = 0;i<len-1;i++){
			int min = i;//記錄最小值的位置
			for(int j = i+1;j<len;j++){
				if(nums[j]<nums[min]){//如果有更小的,則更新最小值的位置
					min = j;
				}
			}
			//如果最小值位置改變了(不是i了),則交換,就是將最小值從後面換到前面
			if(min!=i){
				swap(nums,i,min);
			}
		}
	}
	/**
	 * 分而治之,找到劃分基準,並交換
	 * @param nums
	 * @param left
	 * @param r
	 * @return
	 */
	public static int partation(int nums[],int left,int r){
		int pv = nums[left];//以最左邊的數爲基準
		int p = left;//記錄基準位置
		//遍歷數組,如果小於基準,則與基準交換
		for(int i = p+1;i<=r;i++){
			if(nums[i]<pv){
				p++;//基準前移
				if(p!=i){//如果是基準位置的下一個位置,則不用交換
					swap(nums,p,i);
				}
			}
		}
		//最後,交換 劃分位置 和 最左邊
		swap(nums,p,left);	
		return p;//返回劃分位置
	}
	/**
	 * 快速排序
	 * @param nums
	 * @param left
	 * @param r
	 */
	public static void quickSort(int nums[],int left,int r){
		if(left<r){//切記,一定要左邊小於右邊時才分部處理
			int p = partation(nums,left,r);//找到一個基準位置p
			quickSort(nums,0,p-1);//以p爲中心,將數組分成兩個子數組,分而治之
			quickSort(nums,p+1,r);
		}
	}
	/**
	 * 快速排序
	 * @param nums
	 */
	public static void quickSort(int nums[]){
		int len = nums.length;
		quickSort(nums,0,len-1);
	}
	
	/**
	 * 合併
	 * @param nums
	 * @param temp
	 * @param left
	 * @param mid
	 * @param r
	 */
	public static void merge(int nums[],int temp[],int left,int mid,int r){
		//拷貝一份數組
		for(int i = left;i<=r;i++){
			temp[i] = nums[i];
		}
		//記錄左右部分的索引
		int pa = left,pb = mid+1;
		int index = left;//合併後數組的索引
		
		while(pa<=mid&&pb<=r){
			if(temp[pa]<=temp[pb]){//哪一部分小,就拷貝哪一部分到新數組
				nums[index++] = temp[pa++];
			}else{
				nums[index++] = temp[pb++];
			}
		}
		//處理沒有拷貝完的部分,直接賦值到新數組
		while(pa<=mid){
			nums[index++] = temp[pa++];
		}
		while(pb<=r){
			nums[index++] = temp[pb++];
		}
		
		
	}
	/**
	 * 歸併排序
	 * @param nums
	 * @param temp
	 * @param left
	 * @param r
	 */
	public static void mergeSort(int nums[],int temp[],int left,int r){
		if(left<r){//左邊小於右邊時,分而治之
			int mid = (left+r)/2;
			mergeSort(nums,temp,0,mid);
			mergeSort(nums,temp,mid+1,r);
			//合併
			merge(nums,temp,left,mid,r);
		}
	}
	/**
	 * 歸併排序
	 * @param nums
	 */
	public static void mergeSort(int nums[]){
		int len = nums.length;
		int temp[] = new int[len];
		mergeSort(nums,temp,0,len-1);
	}
	/**
	 * 調整堆
	 * @param nums
	 * @param i
	 * @param len
	 */
	public static void adjustHeap(int nums[],int i,int len){
		int temp = nums[i];//記錄當前節點值(父節點)
		//向下遍歷子節點
		for(int k = 2*i+1;k<len;k = k*2+1){
			//如果存在右子節點,且右子節點大於左子節點
			if(k+1<len&&nums[k]<nums[k+1]){
				k++;//指向右子節點
			}
			//如果子節點比當前節點(父節點)大,則將子節點值賦給父節點
			if(nums[k]>temp){
				nums[i] = nums[k];
				i = k;//記錄父節點的位置,最終要落到的位置
			}else{//如果子節點小於父節點,則調整結束
				break;
			}
		}
		//將當前父節點的值,最終落位入座
		nums[i] = temp;
	}
	/**
	 * 堆排序,升序,最大堆
	 * 通過不斷把根節點(最大值)放到最後一個節點處,
	 * 實現數組從小到大排序
	 * @param nums
	 */
	public static void heapSort(int nums[]){
		int len = nums.length;
		// build heap
		for(int i = len/2-1;i>=0;i--){
			//從最後一個非葉節點開始調整,使之滿足最大堆
			adjustHeap(nums,i,len);
		}
		// exchange and adjust
		for(int j = len-1;j>=0;j--){
			//交換根節點與最後一個節點,通過不斷把根節點(最大值)放到最後一個節點處,
			//實現數組從小到大排序
			swap(nums,0,j);
			//調整剩下的節點,使它們滿足最大堆
			adjustHeap(nums,0,j);//j是當做長度傳過去的,所以,去掉了最後一個節點
		}
		
	}
	
	public static void main(String[] args) {
		
		int nums[] = {2,4,5,7,2,3,4};
		System.out.println("-----排序前-----");
		for(int n:nums){
			System.out.println(n);
		}
//		bubbleSort(nums);//冒泡排序
//		improvedBubbleSort(nums);//改進的冒泡排序
//		insertSort(nums);//插入排序
//		insertBinarySort(nums);//二分插入排序
//		shellSort(nums);//希爾排序
//		selectSort(nums);//選擇排序
//		quickSort(nums);//快速排序
//		mergeSort(nums);//歸併排序
		heapSort(nums);//堆排序
		
		System.out.println("------排序後-----");
		for(int n:nums){
			System.out.println(n);
		}
		
 }

}

補充:

快速排序的思想:分治法

  • 1.先從數組中取出一個數作爲基準數。
  • 2.分區過程,將比這個數大的數全放到它的右邊,小於或等於它的數全放到它的左邊。
  • 3.再對左右區間重複第二步,直到各區間只有一個數。

參考文章
堆排序講解 https://www.cnblogs.com/chengxiao/p/6129630.html
各算法的性能及複雜度講解 https://segmentfault.com/a/1190000004994003

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