排序算法

本节分析及实现8大排序算法及其改进。使用了外存(一般是磁盘)的排序称为外部排序,否则是内部排序。

1、冒泡排序

每次排序将一个最大值放到待排序数组的末尾。实现如下:

public class Sort{
	private int[] array;
	
	public Sort(int[] array){
		this.array = array;
	}
	
	public void displayArray(){
		for(int i = 0;i<array.length;i++){
			System.out.print(array[i] + "\t");
		}
		System.out.println();
	}
	
	public void bubbleSort(){
		int temp;
		int len = array.length;
		for(int i=0;i<len;i++){
			for(int j=0;j<len-i-1;j++){
				if(array[j] > array[j+1]){
					temp = array[j];
					array[j] = array[j+1];
					array[j+1] = temp;
				}
			}
			System.out.println("第" + (i+1)+"轮排序");
			displayArray();
		}
	}
	
	public static void main(String[] args){
		int[] testArray = {3,6,4,2,11,10,5};
		Sort sort = new Sort(testArray);
		sort.bubbleSort();
	}
}

改进方案:设置一个是否交换的标志exchangeFlag,如果某一次排序没有发生交换,说明已经排列好,可立即结束排序。

public class Sort{
	private int[] array;
	
	public Sort(int[] array){
		this.array = array;
	}
	
	public void displayArray(){
		for(int i = 0;i<array.length;i++){
			System.out.print(array[i] + "\t");
		}
		System.out.println();
	}
	
	public void bubbleSort(){
		int temp;
		int len = array.length;
		for(int i=0;i<len;i++){
			boolean exchageFlag = false;
			for(int j=0;j<len-i-1;j++){
				if(array[j] > array[j+1]){
					temp = array[j];
					array[j] = array[j+1];
					array[j+1] = temp;
					if(!exchageFlag){
						exchageFlag = true;
					}
				}
			}
			System.out.println("第" + (i+1)+"轮排序");
			displayArray();
			if(!exchageFlag){
				break;
			}
		}
	}
	
	public static void main(String[] args){
		int[] testArray = {3,6,4,2,11,10,5};
		Sort sort = new Sort(testArray);
		sort.bubbleSort();
	}
}

进一步改进:某一轮排序中,后面一段数据没有发生数据交换,则下次不再比较后面一段数据。

public class Sort{
	private int[] array;
	
	public Sort(int[] array){
		this.array = array;
	}
	
	public void displayArray(){
		for(int i = 0;i<array.length;i++){
			System.out.print(array[i] + "\t");
		}
		System.out.println();
	}
	
	public void bubbleSort(){
		int temp;
		int counter = 1;
		int endPoint = array.length-1;
		while(endPoint > 0){
			int pos = 0;
			for(int j=0;j<endPoint;j++){
				if(array[j] > array[j+1]){
					temp = array[j];
					array[j] = array[j+1];
					array[j+1] = temp;
					pos = j+1;
				} 
			}
			counter++;
			endPoint = pos - 1;
			System.out.println("第" + counter+"轮排序");
			displayArray();
		}
	}
	
	public static void main(String[] args){
		int[] testArray = {3,6,4,2,11,10,5};
		Sort sort = new Sort(testArray);
		sort.bubbleSort();
	}
}

再进一步改进,正向冒泡确定最大值,反向冒泡确定最小值:

public class Sort{
	private int[] array;
	
	public Sort(int[] array){
		this.array = array;
	}
	
	public void displayArray(){
		for(int i = 0;i<array.length;i++){
			System.out.print(array[i] + "\t");
		}
		System.out.println();
	}
	
	public void bubbleSort(){
		int temp = 0;
		int low =0,high = array.length-1;
		int counter = 0;
		while(low < high){
			for(int i = low;i<high;i++){
				if(array[i] > array[i+1]){
					temp = array[i];
					array[i] = array[i+1];
					array[i+1] = temp;
				}
			}
			--high;
			
			for(int j=high;j>low;j--){
				if(array[j] < array[j-1]){
					temp = array[j];
					array[j] = array[j-1];
					array[j-1] = temp;
				}
			}
			++low;
			
			counter++;
			System.out.println("第" +counter +"轮排序");
			displayArray();
		}
	}
	
	public static void main(String[] args){
		int[] testArray = {3,6,4,2,11,10,5};
		Sort sort = new Sort(testArray);
		sort.bubbleSort();
	}
}

2、选择排序

从第一个元素开始,扫描整个待排数组,找到最小的元素放之后再与第一个元素交换位置,然后再从第二个元素开始,继续寻找最小的元素与第二个元素交换位置,依次类推。

public class Sort{
	private int[] array;
	
	public Sort(int[] array){
		this.array = array;
	}
	
	public void displayArray(){
		for(int i = 0;i<array.length;i++){
			System.out.print(array[i] + "\t");
		}
		System.out.println();
	}
	
	public void bubbleSort(){
		int temp = 0;
		int low =0,high = array.length-1;
		int counter = 0;
		while(low < high){
			for(int i = low;i<high;i++){
				if(array[i] > array[i+1]){
					temp = array[i];
					array[i] = array[i+1];
					array[i+1] = temp;
				}
			}
			--high;
			
			for(int j=high;j>low;j--){
				if(array[j] < array[j-1]){
					temp = array[j];
					array[j] = array[j-1];
					array[j-1] = temp;
				}
			}
			++low;
			
			counter++;
			System.out.println("第" +counter +"轮排序");
			displayArray();
		}
	}
	
	public void selectSort(){
		int minPoint;
		int len = array.length;
		int temp;
		int counter = 0;
		
		for(int i =0;i<len-1;i++){
			minPoint = i;
			for(int j=i+1;j<=len-1;j++){
				if(array[j] < array[minPoint]){
					minPoint = j;
				}
			}
			
			if(minPoint != i){
				temp = array[i];
				array[i] = array[minPoint];
				array[minPoint] = temp;
			}
			counter++;
			System.out.println("第" +counter +"轮排序");
			displayArray();
		}
	}
	
	public static void main(String[] args){
		int[] testArray = {3,6,4,2,11,10,5};
		Sort sort = new Sort(testArray);
		//sort.bubbleSort();
		sort.selectSort();
	}
}

选择排序改进:每次排序确定最大值和最小值,将排序趟数减少一半

public void selectSort(){
		int minPoint;
		int maxPoint;
		int len = array.length;
		int temp;
		int counter = 0;
		
		for(int i =0;i<len/2;i++){
			minPoint = i;
			maxPoint = i;
			for(int j=i+1;j<=len-1-i;j++){
				if(array[j] < array[minPoint]){
					minPoint = j;
				}
				if(array[j] > array[maxPoint]){
					maxPoint = j;
				}
			}
			
			if(minPoint != i){
				temp = array[i];
				array[i] = array[minPoint];
				array[minPoint] = temp;
			}
			
			if(maxPoint == i){
				maxPoint= minPoint;
			}
			if(maxPoint != len-1-i){
				temp = array[len-1-i];
				array[len-1-i] = array[maxPoint];
				array[maxPoint] = temp;
			}
			counter++;
			System.out.println("第" +counter +"轮排序");
			displayArray();
		}
	}

3、插入排序

在要排序的一组数中,假设前面(n-1)[n>=2] 个数已经是排好顺序的,现在要把第n个数找到相应位置并插入,使得这n个数也是排好顺序的。如此反复循环,直到全部排好顺序。相对于随机数据,这个算法比冒泡排序快一倍,比选择排序略快。

public void insertSort(){
		int len = array.length;
		int counter = 0;
		for(int i=1;i<len;i++){
			int temp = array[i];
			int insertPoint = i-1;
			while(insertPoint >=0 && array[insertPoint]>=temp){
				array[insertPoint+1] = array[insertPoint];
				insertPoint--;
			}
			array[insertPoint+1] = temp;
			
			counter++;
			System.out.println("第" +counter +"轮排序");
			displayArray();
		}
	}

插入排序改进:二分插入排序

public int binarySearch(int low,int high,int target){
		int curIndex;
		while(low < high){
			curIndex = (low + high)/2;
			if(array[curIndex] > target){
				high = curIndex - 1;
			}else{
				low = curIndex + 1;
			}
		}
		return low;
	}
	
	public void binaryInsertSort(){
		int len = array.length;
		int counter = 0;
		for(int i=1;i<len;i++){
			int temp = array[i];
			
			if(array[i-1] > temp){
				int insertIndex = binarySearch(0,i-1,temp);
				for(int j=i;j>insertIndex;j--){
					array[j] = array[j-1];
				}
				array[insertIndex] = temp;
			}
			
			counter++;
			System.out.println("第" +counter +"轮排序");
			displayArray();
		}
	}

另一种改进:2-路插入排序,就不细讲,参考:https://blog.csdn.net/u012152619/article/details/47306209

4、归并排序

归并排序先将一个无序的N长数组切成N个有序子序列(只有一个数据的序列认为是有序序列),然后两两合并,再将合并后的N/2(或者N/2 + 1)个子序列继续进行两两合并,以此类推得到一个完整的有序数组。

归并排序的核心思想是将两个有序的数组归并到另一个数组中,所以需要开辟额外的空间

private void merge(int[] workSpace,int low,int mid,int high){
		int lowBegin = low;
		int lowEnd = mid;
		int highBegin = mid + 1;
		int highEnd = high;
		int j = 0;
		int n = high - low + 1;
		
		while(lowBegin<=lowEnd && highBegin<=highEnd){
			if(array[lowBegin]<array[highBegin]){
				workSpace[j++] = array[lowBegin++];
			}else{
				workSpace[j++] = array[highBegin++];
			}
		}
		
		while(lowBegin<=lowEnd){
			workSpace[j++] = array[lowBegin++];
		}
		
		while(highBegin<=highEnd){
			workSpace[j++] = array[highBegin++];
		}
		
		for(j=0;j<n;j++){
			array[low++] = workSpace[j];
		}
	}
	
	private void recursiveMergeSort(int[] workSpace,int low,int high){
		if(low == high){
			return;
		}else{
			int mid = (low + high) / 2;
			recursiveMergeSort(workSpace,low,mid);
			recursiveMergeSort(workSpace,mid+1,high);
			merge(workSpace,low,mid,high);
			displayArray();
		}
		
	}
	
	public void mergeSort(){
		int[] workSpace = new int[array.length];
		recursiveMergeSort(workSpace,0,workSpace.length-1);
	}

一般来讲,复制操作的时间消耗要远大于比较操作的时间消耗,时间复杂度是由复制次数主导的。

5、快速排序

快速排序也是基于分治算法得。步骤如下:

(1)选择一个基准元素,通常选择第一个元素或者最后一个元素;

(2)通过一趟排序讲待排序的记录分割成独立的两部分,其中一部分记录的元素值均比基准元素值小。另一部分记录的元素值比基准值大;

(3)此时基准元素在其排好序后的正确位置;

(4)然后分别对这两部分记录用同样的方法继续进行排序,直到整个序列有序。

public void quickSort(){
		recursiveQuickSort(0,array.length-1);
	}
	
	private void recursiveQuickSort(int low,int high){
		if(low >= high){
			return;
		}else{
			int pivot = array[low];
			int partition = partition(low,high,pivot);
			displayArray();
			recursiveQuickSort(low,partition-1);
			recursiveQuickSort(partition+1,high);
		}
		
	}
	
	private int partition(int low,int high,int pivot){
		while(low < high){
			while(low < high && array[high]>=pivot){
				high--;
			}
			swap(low,high);
			
			while(low < high && array[low] <= pivot){
				low++;
			}
			swap(low,high);
		}
		return low;
	}
	
	private void swap(int low,int high){
		int temp = array[low];
		array[low] = array[high];
		array[high] = temp;
	}

6、希尔排序

希尔排序是基于插入排序的,又叫缩小增量排序

希尔排序通过加大插入排序时元素之间的间隔,并对这些间隔的元素进行插入排序,从而使数据能大跨度地移动。数据项之间的间隔被称为增量,习惯上还用h表示。h=3*h+1

public void shellSort(){
		int len = array.length;
		int counter = 0;
		int h =1;
		while(3*h+1 < len){
			h = 3*h + 1;
		}
		while(h > 0){
			for(int i=0;i<h;i++){
				shellInsertSort(i,h);
			}
			h = (h-1)/3;
			counter++;
			System.out.println("第" +counter +"轮排序");
			displayArray();
		} 
	}
	
	public void shellInsertSort(int beginIndex,int increment){
		int targetIndex = beginIndex + increment;
		while(targetIndex < array.length){
			int temp = array[targetIndex];
			
			int previousIndex = targetIndex - increment;
			while(previousIndex>=0 && array[previousIndex] > temp){
				array[previousIndex+increment] = array[previousIndex];
				previousIndex = previousIndex - increment;
			}
			array[previousIndex+increment] = temp;
			targetIndex = targetIndex + increment;
		}
	}

7、堆排序

(1)堆中某个节点的值总是不大于或不小于其父节点的值;

(2)堆总是一棵完全二叉树(Complete Binary Tree)。

除最后一层无任何子节点外,每一层上的所有结点都有两个子结点的二叉树称为满二叉树。如果除最后一层外,每一层上的节点数均达到最大值;在最后一层上只缺少右边的若干结点,这样的二叉树被称为完全二叉树。

棵完全二叉树,如果某个节点的值总是不小于其父节点的值,则根节点的关键字是所有节点关键字中最小的,称为小根堆(小顶堆);如果某个节点的值总是不大于其父节点的值,则根节点的关键字是所有节点关键字中最大的,称为大根堆(大顶堆)。

可以发现,如果某个节点的编号为i,则它的子节点的编号分别为:2i、2i+1。具有n个元素的序列(k1,k2,...,kn),当且仅当满足

时称之为堆。

初始时把要排序的n个数看作是一棵顺序存储的完全二叉树,调整它们的存储顺序,使之成为一个堆,将堆顶元素输出,得到n 个元素中最小(最大)的元素,这时堆的根节点的数最小(或者最大)。然后对前面(n-1)个元素重新调整使之成为堆,输出堆顶元素,得到n 个元素中次小(或次大)的元素。依次类推,直到只有两个节点的堆,并对它们作交换,最后得到有n个节点的有序序列。这个过程就称为堆排序

public void heapSort(){
		buildHeap();
		System.out.println("建堆");
		printTree(array.length);
		
		int lastIndex = array.length - 1;
		while(lastIndex>0){
			swap(0,lastIndex);
			if(--lastIndex==0){
				break;
			}
			adjustHeap(0,lastIndex);
			
			System.out.println("调整堆");
			printTree(lastIndex+1);
		}
	}
	
	private void buildHeap(){
		int lastIndex = array.length - 1;
		for(int i= (lastIndex-1)/2;i>=0;i--){
			adjustHeap(i,lastIndex);
		}
	}
	
	private void adjustHeap(int rootIndex,int lastIndex){
		int biggerIndex = rootIndex;
		int leftChildIndex = 2*rootIndex+1;
		int rightChildIndex = 2*rootIndex+2;
		
		if(rightChildIndex<=lastIndex){
					
		    if(array[rootIndex]<array[leftChildIndex] || array[rootIndex]<array[rightChildIndex]){ //子节点中存在比根更大的元素
                     biggerIndex = array[leftChildIndex]<array[rightChildIndex] ? rightChildIndex :leftChildIndex; 
            }
		}else if(leftChildIndex<=lastIndex){
			if(array[leftChildIndex]>array[rootIndex]){
				biggerIndex = leftChildIndex;
			}
		}
		
		if(biggerIndex != rootIndex){
			swap(rootIndex,biggerIndex);
			adjustHeap(biggerIndex,lastIndex);
		}
	}
	
	private void printTree(int len){
		int layers = (int)Math.floor(Math.log((double)len)/Math.log((double)2))+1;
		int maxWidgth = (int)Math.pow(2,layers)-1;
		int endSpacing = maxWidgth;
		int spacing;
		int numberOfThisLayer;
		
		for(int i=1;i<=layers;i++){
			endSpacing = endSpacing / 2 ;
			spacing = 2*endSpacing + 1;
			numberOfThisLayer = (int)Math.pow(2,i-1);
			
			int j;
			for(j=0;j<endSpacing;j++){
				System.out.print(" ");
			}
			
			int beginIndex = (int)Math.pow(2,i-1)-1;
			for(j=1;j<numberOfThisLayer;j++){
				System.out.println(array[beginIndex++]+"");
				for(int k = 0;k<spacing;k++){
					System.out.print(" ");
				}
				if(beginIndex == len){
					break;
				}
			}
			
			System.out.println();
		}
		System.out.println();
	}

 

8、基数排序(桶排序)用的比较少,就不细说了。

八大排序算法比较:

 

实际使用中,经过实验,会发现相同的数据规模,快速排序比堆排序的效率高很多,并且随着数据规模的扩大,二者的差距不断扩大,快速排序的优势越来越明显。快速排序的时间复杂度近似线性增长,堆排序则要大很多。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值