算法學習【7】常見排序算法

        部分內容參考:http://blog.csdn.net/whuslei/article/details/6442755

        常見排序算法的時間複雜度:


        注:圖片來源http://blog.chinaunix.net/uid-21457204-id-3060260.html

        在平均時間複雜度爲O(nlogn)的排序算法中,歸併排序是唯一穩定的

        在平均時間複雜度爲O(n*n)的排序算法中,選擇排序是唯一不穩定的

        最好時間複雜度爲O(n)的排序算法:插入排序、希爾排序、冒泡排序。

        時間複雜度和數據的初始排列無關:堆排序、歸併排序、選擇排序

        快速排序、基數排序需要額外空間。


        1、冒泡排序

        每次遍歷比較相鄰元素大小,通過交換相鄰元素將最大元素後移。

	public static void myBubbleSort(int[] arr, int len){
		for(int i=0;i<len-1;i++){
			boolean flag = true;
			for(int j=0;j<len-1-i;j++){
				if(arr[j]>arr[j+1]){
					swap(arr, j, j+1);
					flag = false;
				}
			}
			if(flag)
				return;
		}
	}

        相鄰元素相等時不交換。在第一次遍歷中加上標誌位flag,才能使最好情況達到O(n)。


        2、選擇排序

        每次遍歷直接找出最小元素然後與首位置元素交換。

	public static void mySelectSort(int[] arr, int len){
		for(int i=0;i<len-1;i++){
			int minNo = i;
			for(int j=i+1;j<len;j++){
				if(arr[j]<arr[minNo]){
					minNo = j;
				}
			}
			swap(arr, minNo, i);
		}
	}


        3、插入排序

        前n個元素已排序,現將第n+1個元素插入到前n個元素中,因此從後向前依次與前一位元素比較。若前一位小於等於元素n則停止,否則將前一位元素後移。爲了避免交換相鄰元素帶來的額外開銷,可將待插入元素暫存於temp,若前一元素大於元素n,直接賦值arr[n]=arr[n-1],覆蓋原來待插入元素即可。

	public static void myInsertSort(int[] arr, int len){
		for(int i=1;i<len;i++){
			int temp = arr[i];
			int j=i-1;
			for(;j>=0;j--){
				if(arr[j]<=temp){
					break;
				}else{
					arr[j+1]=arr[j];
				}
			}
			arr[j+1]=temp;
		}
	}


        4、快速排序

        選取一個數(通常取第一個數),將數組分成兩部分,一部分小於等於該數,一部分大於該數,然後採用遞歸思想,再對這兩部分分別拆分,當拆分後數組長度小於2時遞歸停止。

	public static void myQuickSort(int[] arr, int left, int right){
		if(right-left<=0){
			return;
		}
		int pivot = arr[left];
		int i = left;
		int j = right;
		while(i<j){
			while(arr[j]>pivot&&i<j)
				j--;
			while(arr[i]<=pivot&&i<j)
				i++;
			swap(arr,i,j);
		}
		swap(arr,left,i);
		myQuickSort(arr,left,i-1);
		myQuickSort(arr,i+1,right);
	}

        5、希爾排序

        希爾排序是插入排序的一種改進。它將記錄按下標的一定增量分組(例,選擇步長d=3,則將arr[0]、arr[3]、arr[6]作爲一組,arr[1]、arr[4]、arr[7]作爲一組,...),然後對每組進行直接插入排序。然後將步長逐步減少,每組元素個數越來越多,當步長減至1時,整個數組合成一組,排序結束希爾排序的時間複雜度與所選步長有關,一般取初始步長d = n/2,然後逐步減半

        示例如下,圖片來自百度百科。


	public static void myShellSort(int[] arr, int len){
		int d = len/2;
		while(d>0){
			for(int i=d;i<len;i++){
				int temp = arr[i];
				int j=i-d;
				for(;j>=0;j=j-d){
					if(arr[j]<=temp){
						break;
					}else{
						arr[j+d]=arr[j];
					}
				}
				arr[j+d]=temp;
			}
			d = d/2;
		}
	}

        6、歸併排序

       歸併排序先將數組拆成一個一個數,每個數認爲是一個有序表,然後將相鄰兩個有序表合併成一個有序表,依次兩兩合併直到只剩一個有序表。一般採用遞歸形式編寫。兩個有序表合併成一個有序表的過程中一般需要使用額外空間,因此空間複雜度爲O(n),因此上表這處有誤。


        圖片來源http://www.cnblogs.com/horizonice/p/4102553.html

	public static void myMergeSort(int[] arr, int st, int fh){
		if(st>=fh){
			return;//已經拆成一個數字
		}else{
			int mid = (st+fh)/2;
			myMergeSort(arr, st, mid);
			myMergeSort(arr, mid+1, fh);
			Merge(arr, st, mid, fh);//將兩個有序表arr[st:mid]和arr[mid+1:fh]合併成一個
		}
	}

	static int[] temp = new int[arr.length];
	public static void Merge(int[] arr, int st, int mid, int fh){
		int i = st;    //兩個指針指向兩個子有序表的開頭
		int j = mid+1;
		int k = 0;
		while(i<=mid&&j<=fh){
			if(arr[i]<=arr[j]){
				temp[k] = arr[i];
				i++;
				k++;
			}else{
				temp[k] = arr[j];
				j++;
				k++;
			}
		}

		while(i<=mid){
			temp[k] = arr[i];
			i++;
			k++;
		}
		while(j<=fh){
			temp[k] = arr[j];
			j++;
			k++;
		}

		for(int n=0;n<k;n++){
			arr[st+n] = temp[n];
		}
	}

        7、堆排序

        堆排序是利用堆這種數據結構進行排序的算法。堆這種數據結構是一種特殊的完全二叉樹,最大堆要求每個節點的值都不大於其父節點的值;反之,最小堆要求每個節點的值都不小於其父節點的值。

        堆排序詳細內容見:http://www.cnblogs.com/mengdd/archive/2012/11/30/2796845.html,講的很好。

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);
		}
	}

	/**
	*創建最大堆
	*
	*@paramdata
	*@paramheapSize需要創建最大堆的大小,一般在sort的時候用到,因爲最多值放在末尾,末尾就不再歸入最大堆了
	*@paramindex當前需要創建最大堆的位置
	*/
	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雖然是最大堆,在排序後就成了遞增的
	*
	*@paramdata
	*/
	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);
		}
	}

	/**
	*父節點位置
	*
	*@paramcurrent
	*@return
	*/
	private static int getParentIndex(int current){
		return(current-1)>>1;
	}

	/**
	*左子節點position注意括號,加法優先級更高
	*
	*@paramcurrent
	*@return
	*/
	private static int getChildLeftIndex(int current){
		return(current<<1)+1;
	}

	/**
	*右子節點position
	*
	*@paramcurrent
	*@return
	*/
	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爲底的對數
	*
	*@paramparam
	*@return
	*/
	private static double getLog(double param){
		return Math.log(param)/Math.log(2);
	}
}



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