常用排序算法

稳定排序:冒泡排序O(n2) ;插入排序O(n2);归并排序O(n log n),需要 O(n) 额外空间

不稳定排序:快速排序O(n log n) ,希尔排序O(n log n)

冒泡排序示例:

package gss.study.sort;

import java.util.Random;

import gss.util.S;

/**
 * n个数从头开始每次与下一个数对比,将大的数字移到大的索引,一直到本次比较的结尾
 * 第一次比较到索引n,则n就是最大的数
 * 第二次比较到索引n-1,则n-1就是次大的数
 * 时间复杂度为o(n^2)
 * 
 * @author gss
 * 10^3数量级,耗时在10^2数量级毫秒
 */
public class BubbleSort implements Sort {
	
	public int[] sort(int[] input) {
		int loopCount = 0;
		int changeCount = 0;
		
		if(S.isUsable(input)) {
			int length = input.length;
			while(length>1) {
				int i=0;
				for(int j=i+1;j<length;) {
					loopCount++;
					int currentNo = input[i];
					int nextNo = input[j];
					int tempNo;
					if(currentNo > nextNo) {
						tempNo = currentNo;
						input[i]=input[j];
						input[j]=tempNo;
						i=j;
						j++;
						changeCount++;
						continue;
					}else {
						i=j;
						j++;
						continue;
					}
				}
				length--;
			}
		}
		S.echo("共执行了"+loopCount+"次循环!");
		S.echo("共执行了"+changeCount+"次交换!");
		return input;
	}
	
	
	public static void main(String[] args) {
		int[] input = new int[5000];
		Random random = new Random();
		for(int i=0;i<5000;i++) {
			input[i] = random.nextInt(10000);
		}
		
//		S.echo("输入序列:"+S.toString(input));
		
		Sort sort = new BubbleSort();
		
		long beginTime = System.currentTimeMillis();
		sort.sort(input);
		long endTime = System.currentTimeMillis();
		S.echo("冒泡耗时:"+(endTime-beginTime)+"毫秒");
		
//		S.echo("结果序列:"+S.toString(input));
		
	}
}


插入排序示例:

package gss.study.sort;

import java.util.Random;

import gss.util.S;

/**
 * 插入排序每次把当前的数据排好序,再取下一个看插入何处
 * @author gss
 *
 */
public class InsertionSort implements Sort {
	int[] input = null;
	
	public static void main(String[] args) {
		int[] input = new int[50000];
		Random random = new Random();
		for(int i=0;i<50000;i++) {
			input[i] = random.nextInt(100000);
		}
		long beginTime;
		long endTime;
		Sort sort;
//		S.echo("输入序列:"+S.toString(input));
		
		int[] insertionArr = input.clone();
		sort = new InsertionSort();
		beginTime = System.currentTimeMillis();
		sort.sort(insertionArr);
		endTime = System.currentTimeMillis();
		S.echo("插入排序耗时:"+(endTime-beginTime)+"毫秒");
//		S.echo("结果序列:"+S.toString(insertionArr));
	}
	
	public int[] sort(int[] input) {
		long loopCount = 0;
		long changeCount = 0;
		if(S.isUsable(input)) {
			//采用内外双指针
			//外层指针遍历全输入,内层指针从外层指针逆序查找该值应该在的位置
			for(int i=1;i<input.length;i++) {
				loopCount++;
				int position = i;
				int current = input[i];
				while(position>0 && (input[position-1]>current)) {
					input[position] = input[position-1];
					position--;
					changeCount++;
					loopCount++;
				}
				input[position] = current;
			}
		}
		S.echo("共执行了"+loopCount+"次循环!");
		S.echo("共执行了"+changeCount+"次交换!");
		return input;
	}
}


归并排序示例:

package gss.study.sort;

import gss.util.S;

import java.util.Random;

/**
 * 归并排序,时间复杂度为o(n logn),需要辅助空间
 * 递归,可以是稳定的
 * 将待排序输入划分成区间,相邻区间合并排列有序,递归至全数据
 * @author gss
 * 经测试,本机快速排序在10^6的数据下仍然有不错的效率,耗时在10^2毫秒
 */
public class MergeSort implements Sort {
	
	public static void main(String[] args) {
		int[] input = new int[5000000];
		Random random = new Random();
		for(int i=0;i<5000000;i++) {
			input[i] = random.nextInt(10000000);
		}
		long beginTime;
		long endTime;
		Sort sort;
		
		sort = new MergeSort();
//		S.echo("输入序列:"+S.toString(input));
		int[] quickArr = input.clone();
		beginTime = System.currentTimeMillis();
		sort.sort(quickArr);
		endTime = System.currentTimeMillis();
		S.echo("归并排序耗时:"+(endTime-beginTime)+"毫秒");
//		S.echo("结果序列:"+S.toString(quickArr));
	}

	@Override
	public int[] sort(int[] input) {
		this.recurse(input, 0, input.length-1);
		return input;
	}
	
	/**
	 * 递归方法,递归条件没有快速排序那么容易理解
	 * 注意这里使用的都是索引
	 * @param input 输入索引
	 * @param begin 起始索引
	 * @param end 结束索引
	 * @return
	 */
	public int[] recurse(int[]input,int begin,int end) {
		//递归结束条件是,起始索引大于 等于结束索引,即归并的粒度为1则停止
		if(begin>=end) {
			return input;
		}
		//归并排序是要将输入均分的,所以要求得中值的[索引]
		int mid = (end+begin)/2;
		//如果输入是基数,则左边比右边多1
		this.recurse(input, begin, mid);
		this.recurse(input, mid+1, end);
		
		//一次归并之后的子序列,这里要占用空间
		int[] temp = this.merge(input, begin, end, mid);
		
		for(int i=0;i<temp.length;i++) {
			input[begin+i]=temp[i];
		}
		
		return input;
	}
	
	/**
	 * 归并方法
	 * 比较输入的作用区间,因为左和右本身是有序的,所以左右都用指针顺序遍历,将小的置前,大的置后,直到一方没有数据,则另一方都是大的,直接放入
	 * @param input	输入
	 * @param begin 起始索引
	 * @param end 结束索引
	 * @param mid 中位索引
	 * @return
	 */
	public int[] merge(int[] input,int begin,int end,int mid) {
		//划分辅助空间
		int[] result = new int[end-begin+1];
		int x=begin;//左边起始索引
		int y=mid+1;//右边起始索引
		int z=0;//辅助空间起始索引
		
		//循环结束条件:直到归并的一方全部合并至辅助空间
		while(x<mid+1 && y<end+1) {
			if(input[x]<=input[y]) {
				result[z++] = input[x++];
			}else 
				result[z++] = input[y++];
		}
		//如果左边还有值未归并,直接放入辅助空间最后
		while(x<=mid) {
			result[z++]=input[x++];
		}
		//如果右边有值未归并,直接放入辅助空间最后
		while(y<=end) {
			result[z++]=input[y++];
		}
		return result;
	}

}


快速排序示例:

package gss.study.sort;

import gss.util.S;

import java.util.Random;

/**
 * 快速排序,时间复杂度o(n logn)	
 * 不稳定
 * 
 * 采用分而治之的方式,首先在目标中随机选取一个对象作为基准值
 * 剩余数据与此基准值对比,将序列分为两部分,所有比基准小的放在左边,比基准大的放在右边,然后这两部分再作为输入
 * 上述递归过程的终结是直到开头不小于结尾,就实际情况而言递归终结的时候输入"原子"的长度只有1或2
 * @author gss
 * 经测试,本机快速排序在n*10^6的数据下仍然有不错的效率,耗时在n*10^3毫秒数量级
 */
public class QuickSort implements Sort{
	private Random random = new Random();
	
    public static void main(String[] args) {
		int[] input = new int[5000000];
		Random random = new Random();
		for(int i=0;i<5000000;i++) {
			input[i] = random.nextInt(10000000);
		}
		long beginTime;
		long endTime;
		Sort sort;
		
		sort = new QuickSort();
//		S.echo("输入序列:"+S.toString(input));
		int[] quickArr = input.clone();
		beginTime = System.currentTimeMillis();
		sort.sort(quickArr);
		endTime = System.currentTimeMillis();
		S.echo("快速排序耗时:"+(endTime-beginTime)+"毫秒");
//		S.echo("结果序列:"+S.toString(quickArr));
	}

	@Override
	public int[] sort(int[] input) {
		this.quickSort(input, 0, input.length-1);
		return input;
	}
	
	/**
	 * 递归主方法
	 * @param input
	 * @param begin
	 * @param end
	 */
	public void quickSort(int[]input,int begin,int end) {
		if(end>begin) {
			//返回一次快速排序之后的基准索引
			int partitionKey = this.partition(input,begin,end);
			
			quickSort(input,begin,partitionKey-1);
			quickSort(input,partitionKey+1,end);
		}
	}
	
	/**
	 * 快速排序的核心,排列一个序列,并且返回选定的基准数的索引
	 * @param input
	 * @param begin
	 * @param end
	 * @return
	 */
	public int partition(int[]input,int begin,int end) {
		int key = begin + random.nextInt(end-begin);//选一个随机的基准索引
		
		int pivot = input[key];//随机基准值
		
		//将基准与结尾换位,其实只是为了将它找个地方放置
		//即便不对换,将基准剥离出来也可以,但要改变序列的大小,所以对换到结尾是最简单的
		this.swap(input, key, end);
		
		//这个循环的核心思想是,采用双指针
		//外指针顺序迭代所有数据,内指针负责指向所有大数据的开头,内指针结束的索引就是最终基准所在的索引
		//开始的时候,内外指针是相等的,外指针一直向后,内指针停在大数据的开头;外指针遇到比基准小的值,就将其与内指针的当前值互换,内指针只有在这种情况下才会+1后移
		//注意,结尾是放的基准值,不参与循环
		for(int i=key=begin;i<end;i++) {
			if(input[i]<pivot){
				swap(input,key++,i);
			}
		}
		//循环结束将基准值换回内指针当前的位置
		this.swap(input, key, end);
		
		return key;
	}
	
	/**
	 * 交换两个元素
	 * @param input
	 * @param i
	 * @param j
	 */
	public void swap (int[]input,int i,int j) {
		int temp = input[i];
		input[i] = input[j];
		input[j] = temp;
	}
}


希尔排序示例:

package gss.study.sort;

import gss.util.S;

import java.util.Random;

/**
 * 希尔排序,就是改进的插入排序
 * 
 * @author gss
 *
 */
public class ShellSort implements Sort {
	
    public static void main(String[] args) {
		int[] input = new int[5];
		Random random = new Random();
		for(int i=0;i<5;i++) {
			input[i] = random.nextInt(100);
		}
		long beginTime;
		long endTime;
		Sort sort;
		
		sort = new ShellSort();
		S.echo("输入序列:"+S.toString(input));
		int[] quickArr = input.clone();
		beginTime = System.currentTimeMillis();
		sort.sort(quickArr);
		endTime = System.currentTimeMillis();
		S.echo("希尔排序耗时:"+(endTime-beginTime)+"毫秒");
		S.echo("结果序列:"+S.toString(quickArr));
	}

	@Override
	public int[] sort(int[] input) {
		//步长数组
		int[] stepArr = new int[]{ 1,5,13,43,113,297,815,1989,4711,11969,27901,84801,
                213331,543749,1355339,3501671,8810089,21521774,
                58548857,157840433,410151271,1131376761,2147483647 };
		
		//选定初始步长
		int stepIndex=0;
		for(;stepArr[stepIndex]<input.length;stepIndex++);
		
		//以步长迭代,步长是逐步缩小至1的
		for(int m=stepIndex;m>=0;--m) {
			int step = stepArr[m];
			//已本次选定的步长为起点开始遍历至结尾
			for (int i=step; i<input.length; i++) {
	             int temp = input[i];//
	             //j的起始为i,每次以步长为单位缩进
	             int j = i;
	             //以步长为单位进行插入排序,循环第一个判断是找的是第二步长对应位置数据,然后是第三步长跟前两个进行插入
	             //再是第四步长跟前三个步长内相应数据对比插入排序,依次类推到最后一个步长
	             while (j>=step && input[j-step]>temp) {
	                 input[j] = input[j-step];
	                 j = j-step;
	             }
	             input[j] = temp;//j最终停在一个步长内之相应的位置
	         }
		}
		return input;
	}
}

 

 简单测试各排序性能:

package gss.study.sort;

import gss.util.S;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Random;

public class SortAdmain {
	public int[] readInput() {
		S.echo("请输入待排序数组:");
		InputStreamReader isr = new InputStreamReader(System.in);
		BufferedReader reader = new BufferedReader(isr);
		String input = null;
		try {
			input = reader.readLine();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		String[] inputArr = input.trim().split("\\s+");
		int[] result = null;
		if (S.isUsable(inputArr)) {
			result = new int[inputArr.length];
			for (int i = 0; i < inputArr.length; i++) {
				result[i] = Integer.parseInt(inputArr[i]);
			}
		}

		return result;
	}
	
	public static void main(String[] args) {
		int[] input = new int[100000];
		Random random = new Random();
		for(int i=0;i<100000;i++) {
			input[i] = random.nextInt(100000);
		}
		long beginTime;
		long endTime;
		Sort sort;
//		S.echo("输入序列:"+S.toString(input));
		
		sort = new BubbleSort();
		S.echo("\n");
		int[] bubbleArr = input.clone();
		beginTime = System.currentTimeMillis();
		sort.sort(bubbleArr);
		endTime = System.currentTimeMillis();
		S.echo("冒泡排序耗时:"+(endTime-beginTime)+"毫秒");
//		S.echo("结果序列:"+S.toString(bubbleArr));
		
		S.echo("\n");
		int[] insertionArr = input.clone();
		sort = new InsertionSort();
		beginTime = System.currentTimeMillis();
		sort.sort(insertionArr);
		endTime = System.currentTimeMillis();
		S.echo("插入排序耗时:"+(endTime-beginTime)+"毫秒");
//		S.echo("结果序列:"+S.toString(insertionArr));
		
		sort = new MergeSort();
		S.echo("\n");
		int[] mergeArr = input.clone();
		beginTime = System.currentTimeMillis();
		sort.sort(mergeArr);
		endTime = System.currentTimeMillis();
		S.echo("归并排序耗时:"+(endTime-beginTime)+"毫秒");
//		S.echo("结果序列:"+S.toString(mergeArr));
		
		S.echo("\n");
		sort = new QuickSort();
		int[] quickArr = input.clone();
		beginTime = System.currentTimeMillis();
		sort.sort(quickArr);
		endTime = System.currentTimeMillis();
		S.echo("快速排序耗时:"+(endTime-beginTime)+"毫秒");
//		S.echo("结果序列:"+S.toString(quickArr));
		
		sort = new MergeSort();
		S.echo("\n");
		int[] shellArr = input.clone();
		beginTime = System.currentTimeMillis();
		sort.sort(shellArr);
		endTime = System.currentTimeMillis();
		S.echo("希尔排序耗时:"+(endTime-beginTime)+"毫秒");
//		S.echo("结果序列:"+S.toString(shellArr));
	}
}


结果为:

冒泡排序耗时:30224毫秒

插入排序耗时:23928毫秒

归并排序耗时:54毫秒

快速排序耗时:19毫秒

希尔排序耗时:26毫秒


结论:数组长度不大的情况下不宜使用归并排序,其它排序差别不大。数组长度很大的情况下Shell最快,Quick其次,冒泡最慢。

归并虽然速度次于Shell和Quick但是唯一的稳定排序算法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值